diff --git a/doc/img/RTTYMod_plugin.png b/doc/img/RTTYMod_plugin.png
new file mode 100644
index 000000000..8d0c862fc
Binary files /dev/null and b/doc/img/RTTYMod_plugin.png differ
diff --git a/plugins/channeltx/modrtty/readme.md b/plugins/channeltx/modrtty/readme.md
index 7465089bc..bdde25242 100644
--- a/plugins/channeltx/modrtty/readme.md
+++ b/plugins/channeltx/modrtty/readme.md
@@ -102,12 +102,12 @@ Full details of the API can be found in the Swagger documentation. Below are a f
 
 To transmit the current text simply send a "tx" action:
 
-    curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/actions" -d '{"channelType": "RTTYMod",  "direction": 1, "RTTYModActions": { "tx": 1}}'
+    curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/actions" -d '{"channelType": "RTTYMod",  "direction": 1, "RTTYModActions": { "tx": 1 }}'
 
-To transmit a packet from the command line:
+To transmit text specified on the command line:
 
-    curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/actions" -d '{"channelType": "RTTYMod",  "direction": 1, "RTTYModActions": { "tx": 1, "payload": {"text": "CQ CQ CQ anyone using SDRangel" }}}'
+    curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/actions" -d '{"channelType": "RTTYMod",  "direction": 1, "RTTYModActions": { "tx": 1, "payload": {"text": "CQ CQ CQ anyone using SDRangel CQ" }}}'
 
 To set the baud rate and frequency shift:
 
-    curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/settings" -d '{"channelType": "RTTYMod", "direction": 1, "RTTYModSettings": {"baud": 45.45; "frequencyShift": 170 }}'
+    curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/0/channel/0/settings" -d '{"channelType": "RTTYMod", "direction": 1, "RTTYModSettings": {"baud": 45.45, "frequencyShift": 170 }}'
diff --git a/plugins/channeltx/modrtty/rttymod.cpp b/plugins/channeltx/modrtty/rttymod.cpp
index 582519f89..48b9e1ab2 100644
--- a/plugins/channeltx/modrtty/rttymod.cpp
+++ b/plugins/channeltx/modrtty/rttymod.cpp
@@ -51,8 +51,7 @@
 MESSAGE_CLASS_DEFINITION(RttyMod::MsgConfigureRttyMod, Message)
 MESSAGE_CLASS_DEFINITION(RttyMod::MsgTx, Message)
 MESSAGE_CLASS_DEFINITION(RttyMod::MsgReportTx, Message)
-MESSAGE_CLASS_DEFINITION(RttyMod::MsgTXPacketBytes, Message)
-MESSAGE_CLASS_DEFINITION(RttyMod::MsgTXPacketData, Message)
+MESSAGE_CLASS_DEFINITION(RttyMod::MsgTXText, Message)
 
 const char* const RttyMod::m_channelIdURI = "sdrangel.channeltx.modrtty";
 const char* const RttyMod::m_channelId = "RTTYMod";
@@ -151,9 +150,9 @@ bool RttyMod::handleMessage(const Message& cmd)
 
         return true;
     }
-    else if (MsgTXPacketData::match(cmd))
+    else if (MsgTXText::match(cmd))
     {
-        MsgTXPacketData* msg = new MsgTXPacketData((const MsgTXPacketData&)cmd);
+        MsgTXText* msg = new MsgTXText((const MsgTXText&)cmd);
         m_basebandSource->getInputMessageQueue()->push(msg);
 
         return true;
@@ -209,6 +208,13 @@ void RttyMod::applySettings(const RttyModSettings& settings, bool force)
             << " m_channelMute: " << settings.m_channelMute
             << " m_repeat: " << settings.m_repeat
             << " m_repeatCount: " << settings.m_repeatCount
+            << " m_text: " << settings.m_text
+            << " m_characterSet: " << settings.m_characterSet
+            << " m_unshiftOnSpace: " << settings.m_unshiftOnSpace
+            << " m_msbFirst: " << settings.m_msbFirst
+            << " m_spaceHigh: " << settings.m_spaceHigh
+            << " m_prefixCRLF: " << settings.m_prefixCRLF
+            << " m_postfixCRLF: " << settings.m_postfixCRLF
             << " m_useReverseAPI: " << settings.m_useReverseAPI
             << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
             << " m_reverseAPIAddress: " << settings.m_reverseAPIPort
@@ -254,10 +260,6 @@ void RttyMod::applySettings(const RttyModSettings& settings, bool force)
         reverseAPIKeys.append("lpfTaps");
     }
 
-    if ((settings.m_bbNoise != m_settings.m_bbNoise) || force) {
-        reverseAPIKeys.append("bbNoise");
-    }
-
     if ((settings.m_rfNoise != m_settings.m_rfNoise) || force) {
         reverseAPIKeys.append("rfNoise");
     }
@@ -274,6 +276,30 @@ void RttyMod::applySettings(const RttyModSettings& settings, bool force)
         reverseAPIKeys.append("symbolSpan");
     }
 
+    if ((settings.m_characterSet != m_settings.m_characterSet) || force) {
+        reverseAPIKeys.append("characterSet");
+    }
+
+    if ((settings.m_unshiftOnSpace != m_settings.m_unshiftOnSpace) || force) {
+        reverseAPIKeys.append("unshiftOnSpace");
+    }
+
+    if ((settings.m_msbFirst != m_settings.m_msbFirst) || force) {
+        reverseAPIKeys.append("msbFirst");
+    }
+
+    if ((settings.m_spaceHigh != m_settings.m_spaceHigh) || force) {
+        reverseAPIKeys.append("spaceHigh");
+    }
+
+    if ((settings.m_prefixCRLF != m_settings.m_prefixCRLF) || force) {
+        reverseAPIKeys.append("prefixCRLF");
+    }
+
+    if ((settings.m_postfixCRLF != m_settings.m_postfixCRLF) || force) {
+        reverseAPIKeys.append("postfixCRLF");
+    }
+
     if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) {
         reverseAPIKeys.append("udpEnabled");
     }
@@ -450,14 +476,11 @@ void RttyMod::webapiUpdateChannelSettings(
     if (channelSettingsKeys.contains("lpfTaps")) {
         settings.m_lpfTaps = response.getRttyModSettings()->getLpfTaps();
     }
-    if (channelSettingsKeys.contains("bbNoise")) {
-        settings.m_bbNoise = response.getRttyModSettings()->getBbNoise() != 0;
-    }
     if (channelSettingsKeys.contains("rfNoise")) {
         settings.m_rfNoise = response.getRttyModSettings()->getRfNoise() != 0;
     }
     if (channelSettingsKeys.contains("text")) {
-        settings.m_text = *response.getRttyModSettings()->getData();
+        settings.m_text = *response.getRttyModSettings()->getText();
     }
     if (channelSettingsKeys.contains("beta")) {
         settings.m_beta = response.getRttyModSettings()->getBeta();
@@ -465,6 +488,24 @@ void RttyMod::webapiUpdateChannelSettings(
     if (channelSettingsKeys.contains("symbolSpan")) {
         settings.m_symbolSpan = response.getRttyModSettings()->getSymbolSpan();
     }
+    if (channelSettingsKeys.contains("characterSet")) {
+        settings.m_characterSet = (Baudot::CharacterSet) response.getRttyModSettings()->getCharacterSet();
+    }
+    if (channelSettingsKeys.contains("unshiftOnSpace")) {
+        settings.m_unshiftOnSpace = response.getRttyModSettings()->getUnshiftOnSpace();
+    }
+    if (channelSettingsKeys.contains("msbFirst")) {
+        settings.m_msbFirst = response.getRttyModSettings()->getMsbFirst();
+    }
+    if (channelSettingsKeys.contains("spaceHigh")) {
+        settings.m_spaceHigh = response.getRttyModSettings()->getSpaceHigh();
+    }
+    if (channelSettingsKeys.contains("prefixCRLF")) {
+        settings.m_prefixCRLF = response.getRttyModSettings()->getPrefixCrlf();
+    }
+    if (channelSettingsKeys.contains("postfixCRLF")) {
+        settings.m_postfixCRLF = response.getRttyModSettings()->getPostfixCrlf();
+    }
     if (channelSettingsKeys.contains("rgbColor")) {
         settings.m_rgbColor = response.getRttyModSettings()->getRgbColor();
     }
@@ -531,10 +572,10 @@ int RttyMod::webapiActionsPost(
             if (swgRttyModActions->getTx() != 0)
             {
                 if (channelActionsKeys.contains("payload")
-                   && (swgRttyModActions->getPayload()->getData()))
+                   && (swgRttyModActions->getPayload()->getText()))
                 {
-                    MsgTXPacketData *msg = MsgTXPacketData::create(
-                        *swgRttyModActions->getPayload()->getData()
+                    MsgTXText *msg = MsgTXText::create(
+                        *swgRttyModActions->getPayload()->getText()
                     );
                     m_basebandSource->getInputMessageQueue()->push(msg);
                 }
@@ -548,19 +589,19 @@ int RttyMod::webapiActionsPost(
             }
             else
             {
-                errorMessage = "Packet must contain tx action";
+                errorMessage = "Must contain tx action";
                 return 400;
             }
         }
         else
         {
-            errorMessage = "Unknown action";
+            errorMessage = "Unknown RTTYMod action";
             return 400;
         }
     }
     else
     {
-        errorMessage = "Missing RttyModActions in query";
+        errorMessage = "Missing RTTYModActions in query";
         return 400;
     }
     return 0;
@@ -577,18 +618,26 @@ void RttyMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respo
     response.getRttyModSettings()->setRepeat(settings.m_repeat ? 1 : 0);
     response.getRttyModSettings()->setRepeatCount(settings.m_repeatCount);
     response.getRttyModSettings()->setLpfTaps(settings.m_lpfTaps);
-    response.getRttyModSettings()->setBbNoise(settings.m_bbNoise ? 1 : 0);
     response.getRttyModSettings()->setRfNoise(settings.m_rfNoise ? 1 : 0);
 
-    if (response.getRttyModSettings()->getData()) {
-        *response.getRttyModSettings()->getData() = settings.m_text;
+    if (response.getRttyModSettings()->getText()) {
+        *response.getRttyModSettings()->getText() = settings.m_text;
     } else {
-        response.getRttyModSettings()->setData(new QString(settings.m_text));
+        response.getRttyModSettings()->setText(new QString(settings.m_text));
     }
 
     response.getRttyModSettings()->setPulseShaping(settings.m_pulseShaping ? 1 : 0);
     response.getRttyModSettings()->setBeta(settings.m_beta);
     response.getRttyModSettings()->setSymbolSpan(settings.m_symbolSpan);
+
+    response.getRttyModSettings()->setCharacterSet((int) settings.m_characterSet);
+    response.getRttyModSettings()->setSymbolSpan(settings.m_symbolSpan);
+    response.getRttyModSettings()->setUnshiftOnSpace(settings.m_unshiftOnSpace);
+    response.getRttyModSettings()->setMsbFirst(settings.m_msbFirst);
+    response.getRttyModSettings()->setSpaceHigh(settings.m_spaceHigh);
+    response.getRttyModSettings()->setPrefixCrlf(settings.m_prefixCRLF);
+    response.getRttyModSettings()->setPostfixCrlf(settings.m_postfixCRLF);
+
     response.getRttyModSettings()->setUdpEnabled(settings.m_udpEnabled);
     response.getRttyModSettings()->setUdpAddress(new QString(settings.m_udpAddress));
     response.getRttyModSettings()->setUdpPort(settings.m_udpPort);
@@ -741,14 +790,11 @@ void RttyMod::webapiFormatChannelSettings(
     if (channelSettingsKeys.contains("lpfTaps")) {
         swgRttyModSettings->setLpfTaps(settings.m_lpfTaps);
     }
-    if (channelSettingsKeys.contains("bbNoise")) {
-        swgRttyModSettings->setBbNoise(settings.m_bbNoise ? 1 : 0);
-    }
     if (channelSettingsKeys.contains("rfNoise")) {
         swgRttyModSettings->setRfNoise(settings.m_rfNoise ? 1 : 0);
     }
     if (channelSettingsKeys.contains("text")) {
-        swgRttyModSettings->setData(new QString(settings.m_text));
+        swgRttyModSettings->setText(new QString(settings.m_text));
     }
     if (channelSettingsKeys.contains("beta")) {
         swgRttyModSettings->setBeta(settings.m_beta);
@@ -756,6 +802,24 @@ void RttyMod::webapiFormatChannelSettings(
     if (channelSettingsKeys.contains("symbolSpan")) {
         swgRttyModSettings->setSymbolSpan(settings.m_symbolSpan);
     }
+    if (channelSettingsKeys.contains("characterSet")) {
+        swgRttyModSettings->setCharacterSet((int) settings.m_characterSet);
+    }
+    if (channelSettingsKeys.contains("unshiftOnSpace")) {
+        swgRttyModSettings->setUnshiftOnSpace(settings.m_unshiftOnSpace);
+    }
+    if (channelSettingsKeys.contains("msbFirst")) {
+        swgRttyModSettings->setMsbFirst(settings.m_msbFirst);
+    }
+    if (channelSettingsKeys.contains("spaceHigh")) {
+        swgRttyModSettings->setSpaceHigh(settings.m_spaceHigh);
+    }
+    if (channelSettingsKeys.contains("prefixCRLF")) {
+        swgRttyModSettings->setPrefixCrlf(settings.m_prefixCRLF);
+    }
+    if (channelSettingsKeys.contains("postfixCRLF")) {
+        swgRttyModSettings->setPostfixCrlf(settings.m_postfixCRLF);
+    }
     if (channelSettingsKeys.contains("rgbColor") || force) {
         swgRttyModSettings->setRgbColor(settings.m_rgbColor);
     }
@@ -838,7 +902,7 @@ void RttyMod::openUDP(const RttyModSettings& settings)
     if (!m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort))
         qCritical() << "RttyMod::openUDP: Failed to bind to port " << settings.m_udpAddress << ":" << settings.m_udpPort << ". Error: " << m_udpSocket->error();
     else
-        qDebug() << "RttyMod::openUDP: Listening for packets on " << settings.m_udpAddress << ":" << settings.m_udpPort;
+        qDebug() << "RttyMod::openUDP: Listening for text on " << settings.m_udpAddress << ":" << settings.m_udpPort;
     connect(m_udpSocket, &QUdpSocket::readyRead, this, &RttyMod::udpRx);
 }
 
@@ -857,7 +921,7 @@ void RttyMod::udpRx()
     while (m_udpSocket->hasPendingDatagrams())
     {
         QNetworkDatagram datagram = m_udpSocket->receiveDatagram();
-        MsgTXPacketBytes *msg = MsgTXPacketBytes::create(datagram.data());
+        MsgTXText *msg = MsgTXText::create(QString(datagram.data()));
         m_basebandSource->getInputMessageQueue()->push(msg);
     }
 }
diff --git a/plugins/channeltx/modrtty/rttymod.h b/plugins/channeltx/modrtty/rttymod.h
index df3d1a40a..b50934b87 100644
--- a/plugins/channeltx/modrtty/rttymod.h
+++ b/plugins/channeltx/modrtty/rttymod.h
@@ -102,40 +102,22 @@ public:
         { }
     };
 
-    class MsgTXPacketBytes : public Message {
+    class MsgTXText : public Message {
         MESSAGE_CLASS_DECLARATION
 
     public:
-        static MsgTXPacketBytes* create(QByteArray data) {
-            return new MsgTXPacketBytes(data);
-        }
-
-        QByteArray m_data;
-
-   private:
-
-        MsgTXPacketBytes(QByteArray data) :
-            Message(),
-            m_data(data)
-        { }
-    };
-
-    class MsgTXPacketData : public Message {
-        MESSAGE_CLASS_DECLARATION
-
-    public:
-        static MsgTXPacketData* create(QString data)
+        static MsgTXText* create(QString text)
         {
-            return new MsgTXPacketData(data);
+            return new MsgTXText(text);
         }
 
-        QString m_data;
+        QString m_text;
 
    private:
 
-        MsgTXPacketData(QString data) :
+       MsgTXText(QString text) :
             Message(),
-            m_data(data)
+            m_text(text)
         { }
     };
 
diff --git a/plugins/channeltx/modrtty/rttymodbaseband.cpp b/plugins/channeltx/modrtty/rttymodbaseband.cpp
index 466cf533a..5919b03a6 100644
--- a/plugins/channeltx/modrtty/rttymodbaseband.cpp
+++ b/plugins/channeltx/modrtty/rttymodbaseband.cpp
@@ -152,21 +152,14 @@ bool RttyModBaseband::handleMessage(const Message& cmd)
     else if (RttyMod::MsgTx::match(cmd))
     {
         qDebug() << "RttyModBaseband::handleMessage: MsgTx";
-        m_source.addTXPacket(m_settings.m_text);
+        m_source.addTXText(m_settings.m_text);
 
         return true;
     }
-    else if (RttyMod::MsgTXPacketBytes::match(cmd))
+    else if (RttyMod::MsgTXText::match(cmd))
     {
-        RttyMod::MsgTXPacketBytes& tx = (RttyMod::MsgTXPacketBytes&) cmd;
-        m_source.addTXPacket(tx.m_data);
-
-        return true;
-    }
-    else if (RttyMod::MsgTXPacketData::match(cmd))
-    {
-        RttyMod::MsgTXPacketData& tx = (RttyMod::MsgTXPacketData&) cmd;
-        m_source.addTXPacket(tx.m_data);
+        RttyMod::MsgTXText& tx = (RttyMod::MsgTXText&) cmd;
+        m_source.addTXText(tx.m_text);
 
         return true;
     }
diff --git a/plugins/channeltx/modrtty/rttymodgui.cpp b/plugins/channeltx/modrtty/rttymodgui.cpp
index 80636f9bc..dc5550b7a 100644
--- a/plugins/channeltx/modrtty/rttymodgui.cpp
+++ b/plugins/channeltx/modrtty/rttymodgui.cpp
@@ -71,7 +71,7 @@ QByteArray RttyModGUI::serialize() const
 
 bool RttyModGUI::deserialize(const QByteArray& data)
 {
-    if(m_settings.deserialize(data)) {
+    if (m_settings.deserialize(data)) {
         displaySettings();
         applySettings(true);
         return true;
@@ -246,8 +246,7 @@ void RttyModGUI::on_endian_clicked(bool checked)
     m_settings.m_msbFirst = checked;
     if (checked) {
         ui->endian->setText("MSB");
-    }
-    else {
+    } else {
         ui->endian->setText("LSB");
     }
     applySettings();
@@ -258,8 +257,7 @@ void RttyModGUI::on_spaceHigh_clicked(bool checked)
     m_settings.m_spaceHigh = checked;
     if (checked) {
         ui->spaceHigh->setText("M-S");
-    }
-    else {
+    } else {
         ui->spaceHigh->setText("S-M");
     }
     applySettings();
@@ -436,10 +434,9 @@ RttyModGUI::RttyModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
 
     ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum);
 
-    // Extra /2 here because SSB?
-    ui->glSpectrum->setCenterFrequency(8000/4);
-    ui->glSpectrum->setSampleRate(8000/2);
-    ui->glSpectrum->setLsbDisplay(true);
+    ui->glSpectrum->setCenterFrequency(0);
+    ui->glSpectrum->setSampleRate(2000);
+    ui->glSpectrum->setLsbDisplay(false);
 
     SpectrumSettings spectrumSettings = m_spectrumVis->getSettings();
     spectrumSettings.m_ssb = false;
@@ -464,7 +461,7 @@ RttyModGUI::RttyModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
     m_channelMarker.setColor(Qt::red);
     m_channelMarker.setBandwidth(12500);
     m_channelMarker.setCenterFrequency(0);
-    m_channelMarker.setTitle("Packet Modulator");
+    m_channelMarker.setTitle("RTTY Modulator");
     m_channelMarker.setSourceOrSinkStream(false);
     m_channelMarker.blockSignals(false);
     m_channelMarker.setVisible(true); // activate signal on the last setting only
@@ -496,18 +493,9 @@ RttyModGUI::~RttyModGUI()
     delete ui;
 }
 
-void RttyModGUI::transmit(const QString& str)
+void RttyModGUI::transmit(const QString& text)
 {
-    QString s = str;
-
-    if (m_settings.m_prefixCRLF) {
-        s.prepend("\r\r\n>"); // '>' switches to letters
-    }
-    if (m_settings.m_postfixCRLF) {
-        s.append("\r\r\n");
-    }
-
-    RttyMod::MsgTXPacketData *msg = RttyMod::MsgTXPacketData::create(s);
+    RttyMod::MsgTXText*msg = RttyMod::MsgTXText::create(text);
     m_rttyMod->getInputMessageQueue()->push(msg);
 }
 
@@ -566,12 +554,11 @@ void RttyModGUI::displaySettings()
 
     ui->mode->setCurrentText("Custom");
     ui->rfBWText->setText(formatFrequency(m_settings.m_rfBandwidth));
-    ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
+    ui->rfBW->setValue(m_settings.m_rfBandwidth);
     QString baudRate;
     if (m_settings.m_baud < 46.0f && m_settings.m_baud > 45.0f) {
         baudRate = "45.45";
-    }
-    else {
+    } else {
         baudRate = QString("%1").arg(m_settings.m_baud);
     }
     ui->baudRate->setCurrentIndex(ui->baudRate->findText(baudRate));
@@ -583,15 +570,13 @@ void RttyModGUI::displaySettings()
     ui->endian->setChecked(m_settings.m_msbFirst);
     if (m_settings.m_msbFirst) {
         ui->endian->setText("MSB");
-    }
-    else {
+    } else {
         ui->endian->setText("LSB");
     }
     ui->spaceHigh->setChecked(m_settings.m_spaceHigh);
     if (m_settings.m_spaceHigh) {
         ui->spaceHigh->setText("M-S");
-    }
-    else {
+    } else {
         ui->spaceHigh->setText("S-M");
     }
 
diff --git a/plugins/channeltx/modrtty/rttymodrepeatdialog.h b/plugins/channeltx/modrtty/rttymodrepeatdialog.h
index 6e4783016..b9bb0e153 100644
--- a/plugins/channeltx/modrtty/rttymodrepeatdialog.h
+++ b/plugins/channeltx/modrtty/rttymodrepeatdialog.h
@@ -27,7 +27,7 @@ public:
     explicit RttyModRepeatDialog(int repeatCount, QWidget* parent = 0);
     ~RttyModRepeatDialog();
 
-    int m_repeatCount;          // Number of packets to transmit (-1 = infinite)
+    int m_repeatCount;          // Number of times to transmit
 
 private slots:
     void accept();
diff --git a/plugins/channeltx/modrtty/rttymodsettings.cpp b/plugins/channeltx/modrtty/rttymodsettings.cpp
index 8724afa9a..5a81beb29 100644
--- a/plugins/channeltx/modrtty/rttymodsettings.cpp
+++ b/plugins/channeltx/modrtty/rttymodsettings.cpp
@@ -43,7 +43,6 @@ void RttyModSettings::resetToDefaults()
     m_repeat = false;
     m_repeatCount = 10;
     m_lpfTaps = 301;
-    m_bbNoise = false;
     m_rfNoise = false;
     m_writeToFile = false;
     m_text = "CQ CQ CQ DE SDRangel CQ";
@@ -94,7 +93,6 @@ QByteArray RttyModSettings::serialize() const
     s.writeBool(7, m_repeat);
     s.writeS32(9, m_repeatCount);
     s.writeS32(23, m_lpfTaps);
-    s.writeBool(24, m_bbNoise);
     s.writeBool(25, m_rfNoise);
     s.writeBool(26, m_writeToFile);
     s.writeString(30, m_text);
@@ -165,7 +163,6 @@ bool RttyModSettings::deserialize(const QByteArray& data)
         d.readBool(7, &m_repeat, false);
         d.readS32(9, &m_repeatCount, -1);
         d.readS32(23, &m_lpfTaps, 301);
-        d.readBool(24, &m_bbNoise, false);
         d.readBool(25, &m_rfNoise, false);
         d.readBool(26, &m_writeToFile, false);
         d.readString(30, &m_text, "CQ CQ CQ anyone using SDRangel");
diff --git a/plugins/channeltx/modrtty/rttymodsettings.h b/plugins/channeltx/modrtty/rttymodsettings.h
index 369f121ad..52e964b74 100644
--- a/plugins/channeltx/modrtty/rttymodsettings.h
+++ b/plugins/channeltx/modrtty/rttymodsettings.h
@@ -37,7 +37,6 @@ struct RttyModSettings
     bool m_repeat;
     int m_repeatCount;
     int m_lpfTaps;
-    bool m_bbNoise;
     bool m_rfNoise;
     bool m_writeToFile;
     QString m_text;     // Text to send
diff --git a/plugins/channeltx/modrtty/rttymodsource.cpp b/plugins/channeltx/modrtty/rttymodsource.cpp
index 2959dfa1b..8080ddb90 100644
--- a/plugins/channeltx/modrtty/rttymodsource.cpp
+++ b/plugins/channeltx/modrtty/rttymodsource.cpp
@@ -29,10 +29,10 @@
 RttyModSource::RttyModSource() :
     m_channelSampleRate(48000),
     m_channelFrequencyOffset(0),
-    m_spectrumRate(8000),
-    m_audioPhase(0.0f),
+    m_spectrumRate(2000),
     m_fmPhase(0.0),
     m_spectrumSink(nullptr),
+    m_specSampleBufferIndex(0),
     m_magsq(0.0),
     m_levelCalcCount(0),
     m_peakLevel(0.0f),
@@ -48,6 +48,7 @@ RttyModSource::RttyModSource() :
     m_demodBuffer.resize(1<<12);
     m_demodBufferFill = 0;
 
+    m_specSampleBuffer.resize(m_specSampleBufferSize);
     m_interpolatorDistanceRemain = 0;
     m_interpolatorConsumed = false;
     m_interpolatorDistance = (Real)m_channelSampleRate / (Real)m_spectrumRate;
@@ -98,21 +99,22 @@ void RttyModSource::pullOne(Sample& sample)
     sample.m_imag = (FixReal) (ci.imag() * SDR_TX_SCALEF);
 }
 
-void RttyModSource::sampleToSpectrum(Real sample)
+void RttyModSource::sampleToSpectrum(Complex sample)
 {
     if (m_spectrumSink)
     {
         Complex out;
-        Complex in;
-        in.real(sample);
-        in.imag(0.0f);
-        if (m_interpolator.decimate(&m_interpolatorDistanceRemain, in, &out))
+        if (m_interpolator.decimate(&m_interpolatorDistanceRemain, sample, &out))
         {
-            sample = std::real(out);
-            m_sampleBuffer.push_back(Sample(sample * 0.891235351562f * SDR_TX_SCALEF, 0.0f));
-            m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true);
-            m_sampleBuffer.clear();
             m_interpolatorDistanceRemain += m_interpolatorDistance;
+            Real r = std::real(out) * SDR_TX_SCALEF;
+            Real i = std::imag(out) * SDR_TX_SCALEF;
+            m_specSampleBuffer[m_specSampleBufferIndex++] = Sample(r, i);
+            if (m_specSampleBufferIndex == m_specSampleBufferSize)
+            {
+                m_spectrumSink->feed(m_specSampleBuffer.begin(), m_specSampleBuffer.end(), false);
+                m_specSampleBufferIndex = 0;
+            }
         }
     }
 }
@@ -130,12 +132,12 @@ void RttyModSource::modulateSample()
                 // Encode a character at a time, so we get a TxReport after each character
                 QString s = m_textToTransmit.left(1);
                 m_textToTransmit = m_textToTransmit.mid(1);
-                encodePacket(s);
+                encodeText(s);
             }
             else
             {
                 // Transmit "diddle"
-                encodePacket(">");
+                encodeText(">");
             }
             initTX();
         }
@@ -148,30 +150,20 @@ void RttyModSource::modulateSample()
         m_sampleIdx = 0;
     }
 
-    if (!m_settings.m_bbNoise)
+    // FSK
+    if (m_settings.m_pulseShaping)
     {
-        // FSK
-        if (m_settings.m_pulseShaping)
-        {
-            if (m_sampleIdx == 1) {
-                audioMod = m_pulseShape.filter(m_bit ? 1.0f : -1.0f);
-            } else {
-                audioMod = m_pulseShape.filter(0.0f);
-            }
-        }
-        else
-        {
-            audioMod = m_bit ? 1.0f : -1.0f;
+        if (m_sampleIdx == 1) {
+            audioMod = m_pulseShape.filter(m_bit ? 1.0f : -1.0f);
+        } else {
+            audioMod = m_pulseShape.filter(0.0f);
         }
     }
     else
     {
-        audioMod = (Real)rand() / ((Real)RAND_MAX) - 0.5; // Noise to test filter frequency response
+        audioMod = m_bit ? 1.0f : -1.0f;
     }
 
-    // Display baseband audio in spectrum analyser
-    sampleToSpectrum(audioMod);
-
     // FM
     m_fmPhase += m_phaseSensitivity * audioMod * (m_settings.m_spaceHigh ? -1.0f : 1.0f);
     // Keep phase in range -pi,pi
@@ -196,6 +188,9 @@ void RttyModSource::modulateSample()
     // Apply low pass filter to limit RF BW
     m_modSample = m_lowpass.filter(m_modSample);
 
+    // Display in spectrum analyser
+    sampleToSpectrum(m_modSample);
+
     Real s = std::real(m_modSample);
     calculateLevel(s);
 
@@ -245,7 +240,12 @@ void RttyModSource::calculateLevel(Real& sample)
 
 void RttyModSource::applySettings(const RttyModSettings& settings, bool force)
 {
-    // Only recreate filters if settings have changed
+    if ((settings.m_baud != m_settings.m_baud) || force)
+    {
+        m_samplesPerSymbol = m_channelSampleRate / settings.m_baud;
+        qDebug() << "m_samplesPerSymbol: " << m_samplesPerSymbol << " (" << m_channelSampleRate << "/" << settings.m_baud << ")";
+    }
+
     if ((settings.m_lpfTaps != m_settings.m_lpfTaps) || (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
     {
         qDebug() << "RttyModSource::applySettings: Creating new lpf with taps " << settings.m_lpfTaps << " rfBW " << settings.m_rfBandwidth;
@@ -273,9 +273,6 @@ void RttyModSource::applySettings(const RttyModSettings& settings, bool force)
 
     m_settings = settings;
 
-    m_samplesPerSymbol = m_channelSampleRate / m_settings.m_baud;
-    qDebug() << "m_samplesPerSymbol: " << m_samplesPerSymbol << " (" << m_channelSampleRate << "/" << m_settings.m_baud << ")";
-
     // Precalculate FM sensensity and linear gain to save doing it in the loop
     m_phaseSensitivity = 2.0f * M_PI * (m_settings.m_frequencyShift/2.0f) / (double)m_channelSampleRate;
     m_linearGain = powf(10.0f,  m_settings.m_gain/20.0f);
@@ -320,6 +317,7 @@ void RttyModSource::applyChannelSettings(int channelSampleRate, int channelFrequ
     m_channelFrequencyOffset = channelFrequencyOffset;
     m_samplesPerSymbol = m_channelSampleRate / m_settings.m_baud;
     qDebug() << "m_samplesPerSymbol: " << m_samplesPerSymbol << " (" << m_channelSampleRate << "/" << m_settings.m_baud << ")";
+
     // Precalculate FM sensensity to save doing it in the loop
     m_phaseSensitivity = 2.0f * M_PI * (m_settings.m_frequencyShift/2.0f) / (double)m_channelSampleRate;
 
@@ -385,25 +383,26 @@ void RttyModSource::initTX()
     m_bit = 0;
 }
 
-void RttyModSource::addTXPacket(QString data)
+void RttyModSource::addTXText(QString text)
 {
     int count = m_settings.m_repeat ? m_settings.m_repeatCount : 1;
 
     for (int i = 0; i < count; i++) {
-        m_textToTransmit.append(data);
+
+        QString s = text;
+
+        if (m_settings.m_prefixCRLF) {
+            s.prepend("\r\r\n>"); // '>' switches to letters
+        }
+        if (m_settings.m_postfixCRLF) {
+            s.append("\r\r\n");
+        }
+
+        m_textToTransmit.append(s);
     }
 }
 
-void RttyModSource::addTXPacket(QByteArray data)
-{
-    int count = m_settings.m_repeat ? m_settings.m_repeatCount : 1;
-
-    for (int i = 0; i < count; i++) {
-        m_textToTransmit.append(QString(data));
-    }
-}
-
-void RttyModSource::encodePacket(const QString& text)
+void RttyModSource::encodeText(const QString& text)
 {
     // RTTY encoding
     m_byteIdx = 0;
diff --git a/plugins/channeltx/modrtty/rttymodsource.h b/plugins/channeltx/modrtty/rttymodsource.h
index d85bb3cda..82f527772 100644
--- a/plugins/channeltx/modrtty/rttymodsource.h
+++ b/plugins/channeltx/modrtty/rttymodsource.h
@@ -58,10 +58,7 @@ public:
     void setSpectrumSink(BasebandSampleSink *sampleSink) { m_spectrumSink = sampleSink; }
     void applySettings(const RttyModSettings& settings, bool force = false);
     void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
-    void addTXPacket(QString data);
-    void addTXPacket(QByteArray data);
-    //void encodePacket(uint8_t *packet, int packet_length, uint8_t *packet_end);
-    void encodePacket(const QString& data);
+    void addTXText(QString data);
     void setChannel(ChannelAPI *channel) { m_channel = channel; }
     int getChannelSampleRate() const { return m_channelSampleRate; }
 
@@ -73,7 +70,6 @@ private:
     ChannelAPI *m_channel;
 
     NCO m_carrierNco;
-    Real m_audioPhase;
     double m_fmPhase;                   // Double gives cleaner spectrum than Real
     double m_phaseSensitivity;
     Real m_linearGain;
@@ -84,8 +80,10 @@ private:
     Lowpass<Complex> m_lowpass;         // Low pass filter to limit RF bandwidth
 
     BasebandSampleSink* m_spectrumSink; // Spectrum GUI to display baseband waveform
-    SampleVector m_sampleBuffer;
-    Interpolator m_interpolator;        // Interpolator to downsample to 4k in spectrum
+    SampleVector m_specSampleBuffer;
+    static const int m_specSampleBufferSize = 256;
+    int m_specSampleBufferIndex;
+    Interpolator m_interpolator;        // Interpolator to downsample to spectrum
     Real m_interpolatorDistance;
     Real m_interpolatorDistanceRemain;
     bool m_interpolatorConsumed;
@@ -121,13 +119,14 @@ private:
 
     MessageQueue* getMessageQueueToGUI() { return m_messageQueueToGUI; }
 
+    void encodeText(const QString& data);
     int getBit();                       // Get bit from m_bits
     void addBit(int bit);               // Add bit to m_bits, with zero stuffing
     void initTX();
 
     void calculateLevel(Real& sample);
     void modulateSample();
-    void sampleToSpectrum(Real sample);
+    void sampleToSpectrum(Complex sample);
 
 };
 
diff --git a/plugins/channeltx/modrtty/rttymodtxsettingsdialog.cpp b/plugins/channeltx/modrtty/rttymodtxsettingsdialog.cpp
index 57c40c6f8..ff1fc0e2b 100644
--- a/plugins/channeltx/modrtty/rttymodtxsettingsdialog.cpp
+++ b/plugins/channeltx/modrtty/rttymodtxsettingsdialog.cpp
@@ -39,7 +39,6 @@ RttyModTXSettingsDialog::RttyModTXSettingsDialog(RttyModSettings* settings, QWid
     ui->beta->setValue(m_settings->m_beta);
     ui->symbolSpan->setValue(m_settings->m_symbolSpan);
     ui->lpfTaps->setValue(m_settings->m_lpfTaps);
-    ui->bbNoise->setChecked(m_settings->m_bbNoise);
     ui->rfNoise->setChecked(m_settings->m_rfNoise);
 }
 
@@ -60,7 +59,6 @@ void RttyModTXSettingsDialog::accept()
     m_settings->m_beta = ui->beta->value();
     m_settings->m_symbolSpan = ui->symbolSpan->value();
     m_settings->m_lpfTaps = ui->lpfTaps->value();
-    m_settings->m_bbNoise = ui->bbNoise->isChecked();
     m_settings->m_rfNoise = ui->rfNoise->isChecked();
 
     QDialog::accept();
diff --git a/plugins/channeltx/modrtty/rttymodtxsettingsdialog.ui b/plugins/channeltx/modrtty/rttymodtxsettingsdialog.ui
index 1f1408bf8..3d1aa32e3 100644
--- a/plugins/channeltx/modrtty/rttymodtxsettingsdialog.ui
+++ b/plugins/channeltx/modrtty/rttymodtxsettingsdialog.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>351</width>
-    <height>546</height>
+    <height>554</height>
    </rect>
   </property>
   <property name="font">
@@ -220,16 +220,6 @@ ${location}</string>
      </property>
      <layout class="QFormLayout" name="formLayout_3">
       <item row="0" column="0">
-       <widget class="QCheckBox" name="bbNoise">
-        <property name="toolTip">
-         <string>Generate white noise as baseband signal.</string>
-        </property>
-        <property name="text">
-         <string>Generate BB noise</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="0">
        <widget class="QCheckBox" name="rfNoise">
         <property name="toolTip">
          <string>Generate white noise as RF signal.</string>
diff --git a/swagger/sdrangel/api/swagger/include/RTTYMod.yaml b/swagger/sdrangel/api/swagger/include/RTTYMod.yaml
index 6e8c0b332..2cbf1feff 100644
--- a/swagger/sdrangel/api/swagger/include/RTTYMod.yaml
+++ b/swagger/sdrangel/api/swagger/include/RTTYMod.yaml
@@ -23,12 +23,6 @@ RTTYModSettings:
       type: integer
     lpfTaps:
       type: integer
-    bbNoise:
-      type: integer
-      description: >
-        Boolean
-          * 0 - off
-          * 1 - on
     rfNoise:
       type: integer
       description: >