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 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 @@ 0 0 351 - 546 + 554 @@ -220,16 +220,6 @@ ${location} - - - Generate white noise as baseband signal. - - - Generate BB noise - - - - Generate white noise as RF signal. 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: >