From d4ede8457bc113d3e270c74af083ec1aa04712a7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 14 Feb 2020 18:04:20 +0100 Subject: [PATCH] LoRa modulator: implement basic messaging --- plugins/channeltx/modlora/loramod.cpp | 109 +++- plugins/channeltx/modlora/loramod.h | 22 + plugins/channeltx/modlora/loramodbaseband.cpp | 14 +- plugins/channeltx/modlora/loramodbaseband.h | 25 + plugins/channeltx/modlora/loramodencoder.cpp | 15 + plugins/channeltx/modlora/loramodencoder.h | 1 + plugins/channeltx/modlora/loramodgui.cpp | 165 +++++- plugins/channeltx/modlora/loramodgui.h | 13 + plugins/channeltx/modlora/loramodgui.ui | 535 +++++++++++++++++- plugins/channeltx/modlora/loramodsettings.cpp | 80 ++- plugins/channeltx/modlora/loramodsettings.h | 35 +- plugins/channeltx/modlora/loramodsource.cpp | 49 +- plugins/channeltx/modlora/loramodsource.h | 4 + 13 files changed, 1028 insertions(+), 39 deletions(-) diff --git a/plugins/channeltx/modlora/loramod.cpp b/plugins/channeltx/modlora/loramod.cpp index 074c1b45a..ba4e0bfa8 100644 --- a/plugins/channeltx/modlora/loramod.cpp +++ b/plugins/channeltx/modlora/loramod.cpp @@ -40,6 +40,7 @@ #include "loramod.h" MESSAGE_CLASS_DEFINITION(LoRaMod::MsgConfigureLoRaMod, Message) +MESSAGE_CLASS_DEFINITION(LoRaMod::MsgReportPayloadTime, Message) const QString LoRaMod::m_channelIdURI = "sdrangel.channeltx.modlora"; const QString LoRaMod::m_channelId = "LoRaMod"; @@ -130,7 +131,7 @@ bool LoRaMod::handleMessage(const Message& cmd) void LoRaMod::applySettings(const LoRaModSettings& settings, bool force) { - qDebug() << "NFMMod::applySettings:" + qDebug() << "LoRaMod::applySettings:" << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset << " m_rfBandwidth: " << settings.m_bandwidthIndex << " bandwidth: " << LoRaModSettings::bandwidths[settings.m_bandwidthIndex] @@ -154,6 +155,107 @@ void LoRaMod::applySettings(const LoRaModSettings& settings, bool force) reverseAPIKeys.append("channelMute"); } + if ((settings.m_spreadFactor != m_settings.m_spreadFactor) + || (settings.m_deBits != m_settings.m_deBits) || force) { + m_encoder.setNbSymbolBits(settings.m_spreadFactor - settings.m_deBits); + } + + if ((settings.m_codingScheme != m_settings.m_codingScheme) || force) { + m_encoder.setCodingScheme(settings.m_codingScheme); + } + + LoRaModBaseband::MsgConfigureLoRaModPayload *payloadMsg = nullptr; + std::vector symbols; + + if ((settings.m_messageType == LoRaModSettings::MessageNone) + && ((settings.m_messageType != m_settings.m_messageType) || force)) + { + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(); + } + else if ((settings.m_messageType == LoRaModSettings::MessageBeacon) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_beaconMessage != m_settings.m_beaconMessage) || force)) + { + m_encoder.encodeString(m_settings.m_beaconMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageCQ) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_cqMessage != m_settings.m_cqMessage) || force)) + { + m_encoder.encodeString(m_settings.m_cqMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageReply) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_replyMessage != m_settings.m_replyMessage) || force)) + { + m_encoder.encodeString(m_settings.m_replyMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageReport) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_reportMessage != m_settings.m_reportMessage) || force)) + { + m_encoder.encodeString(m_settings.m_reportMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageReplyReport) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_replyReportMessage != m_settings.m_replyReportMessage) || force)) + { + m_encoder.encodeString(m_settings.m_replyReportMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageRRR) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_rrrMessage != m_settings.m_rrrMessage) || force)) + { + m_encoder.encodeString(m_settings.m_rrrMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::Message73) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_73Message != m_settings.m_73Message) || force)) + { + m_encoder.encodeString(m_settings.m_73Message, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageQSOText) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_qsoTextMessage != m_settings.m_qsoTextMessage) || force)) + { + m_encoder.encodeString(m_settings.m_qsoTextMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageText) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_textMessage != m_settings.m_textMessage) || force)) + { + m_encoder.encodeString(m_settings.m_textMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + else if ((settings.m_messageType == LoRaModSettings::MessageBytes) + && ((settings.m_messageType != m_settings.m_messageType) + || (settings.m_bytesMessage != m_settings.m_bytesMessage) || force)) + { + m_encoder.encodeBytes(m_settings.m_bytesMessage, symbols); + payloadMsg = LoRaModBaseband::MsgConfigureLoRaModPayload::create(symbols); + } + + if (payloadMsg) + { + m_basebandSource->getInputMessageQueue()->push(payloadMsg); + + if (getMessageQueueToGUI()) + { + MsgReportPayloadTime *rpt = MsgReportPayloadTime::create( + (symbols.size()*(1<push(rpt); + } + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -406,3 +508,8 @@ uint32_t LoRaMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); } + +bool LoRaMod::getModulatorActive() const +{ + return m_basebandSource->getActive(); +} diff --git a/plugins/channeltx/modlora/loramod.h b/plugins/channeltx/modlora/loramod.h index b2a81a593..b36ec2783 100644 --- a/plugins/channeltx/modlora/loramod.h +++ b/plugins/channeltx/modlora/loramod.h @@ -30,6 +30,7 @@ #include "util/message.h" #include "loramodsettings.h" +#include "loramodencoder.h" class QNetworkAccessManager; class QNetworkReply; @@ -65,6 +66,25 @@ public: { } }; + class MsgReportPayloadTime : public Message { + MESSAGE_CLASS_DECLARATION + + public: + unsigned int getPayloadTimeMs() const { return m_timeMs; } + static MsgReportPayloadTime* create(unsigned int timeMs) { + return new MsgReportPayloadTime(timeMs); + } + + private: + unsigned int m_timeMs; //!< time in milliseconds + + MsgReportPayloadTime(unsigned int timeMs) : + Message(), + m_timeMs(timeMs) + {} + + }; + //================================================================= LoRaMod(DeviceAPI *deviceAPI); @@ -120,6 +140,7 @@ public: CWKeyer *getCWKeyer(); void setLevelMeter(QObject *levelMeter); uint32_t getNumberOfDeviceStreams() const; + bool getModulatorActive() const; static const QString m_channelIdURI; static const QString m_channelId; @@ -128,6 +149,7 @@ private: DeviceAPI* m_deviceAPI; QThread *m_thread; LoRaModBaseband* m_basebandSource; + LoRaModEncoder m_encoder; // TODO: check if it needs to be on its own thread LoRaModSettings m_settings; SampleVector m_sampleBuffer; diff --git a/plugins/channeltx/modlora/loramodbaseband.cpp b/plugins/channeltx/modlora/loramodbaseband.cpp index 9400e77f3..d150dd7af 100644 --- a/plugins/channeltx/modlora/loramodbaseband.cpp +++ b/plugins/channeltx/modlora/loramodbaseband.cpp @@ -24,6 +24,7 @@ #include "loramodbaseband.h" MESSAGE_CLASS_DEFINITION(LoRaModBaseband::MsgConfigureLoRaModBaseband, Message) +MESSAGE_CLASS_DEFINITION(LoRaModBaseband::MsgConfigureLoRaModPayload, Message) LoRaModBaseband::LoRaModBaseband() : m_mutex(QMutex::Recursive) @@ -134,20 +135,29 @@ bool LoRaModBaseband::handleMessage(const Message& cmd) { if (MsgConfigureLoRaModBaseband::match(cmd)) { + qDebug() << "LoRaModBaseband::handleMessage: MsgConfigureLoRaModBaseband"; QMutexLocker mutexLocker(&m_mutex); MsgConfigureLoRaModBaseband& cfg = (MsgConfigureLoRaModBaseband&) cmd; - qDebug() << "LoRaModBaseband::handleMessage: MsgConfigureLoRaModBaseband"; applySettings(cfg.getSettings(), cfg.getForce()); return true; } + else if (MsgConfigureLoRaModPayload::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureLoRaModPayload& cfg = (MsgConfigureLoRaModPayload&) cmd; + qDebug() << "LoRaModBaseband::handleMessage: MsgConfigureLoRaModPayload:" << cfg.getPayload().size(); + m_source.setSymbols(cfg.getPayload()); + + return true; + } else if (DSPSignalNotification::match(cmd)) { QMutexLocker mutexLocker(&m_mutex); DSPSignalNotification& notif = (DSPSignalNotification&) cmd; - qDebug() << "LoRaModBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); + qDebug() << "LoRaModBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_source.applyChannelSettings( m_channelizer->getChannelSampleRate(), diff --git a/plugins/channeltx/modlora/loramodbaseband.h b/plugins/channeltx/modlora/loramodbaseband.h index 6b6196b5d..eb5e7c741 100644 --- a/plugins/channeltx/modlora/loramodbaseband.h +++ b/plugins/channeltx/modlora/loramodbaseband.h @@ -56,6 +56,30 @@ public: { } }; + class MsgConfigureLoRaModPayload : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const std::vector& getPayload() const { return m_payload; } + + static MsgConfigureLoRaModPayload* create() { + return new MsgConfigureLoRaModPayload(); + } + static MsgConfigureLoRaModPayload* create(const std::vector& payload) { + return new MsgConfigureLoRaModPayload(payload); + } + + private: + std::vector m_payload; + + MsgConfigureLoRaModPayload() : // This is empty payload notification + Message() + {} + MsgConfigureLoRaModPayload(const std::vector& payload) : + Message() + { m_payload = payload; } + }; + LoRaModBaseband(); ~LoRaModBaseband(); void reset(); @@ -63,6 +87,7 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication double getMagSq() const { return m_source.getMagSq(); } int getChannelSampleRate() const; + bool getActive() const { return m_source.getActive(); } signals: /** diff --git a/plugins/channeltx/modlora/loramodencoder.cpp b/plugins/channeltx/modlora/loramodencoder.cpp index 85f37faa2..11e64f14f 100644 --- a/plugins/channeltx/modlora/loramodencoder.cpp +++ b/plugins/channeltx/modlora/loramodencoder.cpp @@ -105,11 +105,26 @@ void LoRaModEncoder::encodeString(const QString& str, std::vector& case LoRaModSettings::CodingASCII: encodeStringASCII(str, symbols); break; + case LoRaModSettings::CodingLoRa: + // TODO + break; default: break; } } +void LoRaModEncoder::encodeBytes(const QByteArray& bytes, std::vector& symbols) +{ + switch (m_codingScheme) + { + case LoRaModSettings::CodingLoRa: + // TODO + break; + default: + break; + }; +} + void LoRaModEncoder::encodeStringASCII(const QString& str, std::vector& symbols) { if (m_nbSymbolBits != 7) { diff --git a/plugins/channeltx/modlora/loramodencoder.h b/plugins/channeltx/modlora/loramodencoder.h index 8cad06a54..760f4648f 100644 --- a/plugins/channeltx/modlora/loramodencoder.h +++ b/plugins/channeltx/modlora/loramodencoder.h @@ -30,6 +30,7 @@ public: void setCodingScheme(LoRaModSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; } void setNbSymbolBits(unsigned int symbolBits) { m_nbSymbolBits = symbolBits; } void encodeString(const QString& str, std::vector& symbols); + void encodeBytes(const QByteArray& bytes, std::vector& symbols); private: enum TTYState diff --git a/plugins/channeltx/modlora/loramodgui.cpp b/plugins/channeltx/modlora/loramodgui.cpp index a3a5d7aec..280b2407c 100644 --- a/plugins/channeltx/modlora/loramodgui.cpp +++ b/plugins/channeltx/modlora/loramodgui.cpp @@ -105,6 +105,12 @@ bool LoRaModGUI::handleMessage(const Message& message) blockApplySettings(false); return true; } + else if (LoRaMod::MsgReportPayloadTime::match(message)) + { + const LoRaMod::MsgReportPayloadTime& rpt = (LoRaMod::MsgReportPayloadTime&) message; + ui->msgTimeText->setText(tr("%1").arg(rpt.getPayloadTimeMs())); + return true; + } else if (DSPSignalNotification::match(message)) { DSPSignalNotification& notif = (DSPSignalNotification&) message; @@ -216,6 +222,107 @@ void LoRaModGUI::on_syncWord_editingFinished() } } +void LoRaModGUI::on_scheme_currentIndexChanged(int index) +{ + m_settings.m_codingScheme = (LoRaModSettings::CodingScheme) index; + applySettings(); +} + +void LoRaModGUI::on_myCall_editingFinished() +{ + m_settings.m_myCall = ui->myCall->text(); + applySettings(); +} + +void LoRaModGUI::on_urCall_editingFinished() +{ + m_settings.m_urCall = ui->urCall->text(); + applySettings(); +} + +void LoRaModGUI::on_myLocator_editingFinished() +{ + m_settings.m_myLoc = ui->myLocator->text(); + applySettings(); +} + +void LoRaModGUI::on_report_editingFinished() +{ + m_settings.m_myRpt = ui->report->text(); + applySettings(); +} + +void LoRaModGUI::on_msgType_currentIndexChanged(int index) +{ + m_settings.m_messageType = (LoRaModSettings::MessageType) index; + displayCurrentPayloadMessage(); + applySettings(); +} + +void LoRaModGUI::on_resetMessages_clicked(bool checked) +{ + (void) checked; + m_settings.setDefaultTemplates(); + displayCurrentPayloadMessage(); + applySettings(); +} + +void LoRaModGUI::on_playMessage_clicked(bool checked) +{ + (void) checked; + // Switch to message None then back to current message type to trigger sending process + LoRaModSettings::MessageType msgType = m_settings.m_messageType; + m_settings.m_messageType = LoRaModSettings::MessageNone; + applySettings(); + m_settings.m_messageType = msgType; + applySettings(); +} + +void LoRaModGUI::on_repeatMessage_valueChanged(int value) +{ + m_settings.m_messageRepeat = value; + ui->repeatText->setText(tr("%1").arg(m_settings.m_messageRepeat)); + applySettings(); +} + +void LoRaModGUI::on_generateMessages_clicked(bool checked) +{ + (void) checked; + m_settings.generateMessages(); + displayCurrentPayloadMessage(); + applySettings(); +} + +void LoRaModGUI::on_messageText_editingFinished() +{ + if (m_settings.m_messageType == LoRaModSettings::MessageBeacon) { + m_settings.m_beaconMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageCQ) { + m_settings.m_cqMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReply) { + m_settings.m_replyMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReport) { + m_settings.m_reportMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReplyReport) { + m_settings.m_replyReportMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageRRR) { + m_settings.m_rrrMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::Message73) { + m_settings.m_73Message = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageQSOText) { + m_settings.m_qsoTextMessage = ui->messageText->toPlainText(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageText) { + m_settings.m_textMessage = ui->messageText->toPlainText(); + } + + applySettings(); +} + +void LoRaModGUI::on_hexText_editingFinished() +{ + +} + void LoRaModGUI::onWidgetRolled(QWidget* widget, bool rollDown) { (void) widget; @@ -351,6 +458,7 @@ void LoRaModGUI::displaySettings() setWindowTitle(m_channelMarker.getTitle()); displayStreamIndex(); + displayCurrentPayloadMessage(); blockApplySettings(true); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); @@ -366,6 +474,14 @@ void LoRaModGUI::displaySettings() ui->idleTimeText->setText(tr("%1").arg(m_settings.m_quietMillis / 1000.0, 0, 'f', 1)); ui->syncWord->setText((tr("%1").arg(m_settings.m_syncWord, 2, 16))); ui->channelMute->setChecked(m_settings.m_channelMute); + ui->scheme->setCurrentIndex((int) m_settings.m_codingScheme); + ui->myCall->setText(m_settings.m_myCall); + ui->urCall->setText(m_settings.m_urCall); + ui->myLocator->setText(m_settings.m_myLoc); + ui->report->setText(m_settings.m_myRpt); + ui->repeatMessage->setValue(m_settings.m_messageRepeat); + ui->repeatText->setText(tr("%1").arg(m_settings.m_messageRepeat)); + ui->msgType->setCurrentIndex((int) m_settings.m_messageType); blockApplySettings(false); } @@ -378,6 +494,35 @@ void LoRaModGUI::displayStreamIndex() } } +void LoRaModGUI::displayCurrentPayloadMessage() +{ + ui->messageText->blockSignals(true); + + if (m_settings.m_messageType == LoRaModSettings::MessageNone) { + ui->messageText->clear(); + } else if (m_settings.m_messageType == LoRaModSettings::MessageBeacon) { + ui->messageText->setText(m_settings.m_beaconMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageCQ) { + ui->messageText->setText(m_settings.m_cqMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReply) { + ui->messageText->setText(m_settings.m_replyMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReport) { + ui->messageText->setText(m_settings.m_reportMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageReplyReport) { + ui->messageText->setText(m_settings.m_replyReportMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageRRR) { + ui->messageText->setText(m_settings.m_rrrMessage); + } else if (m_settings.m_messageType == LoRaModSettings::Message73) { + ui->messageText->setText(m_settings.m_73Message); + } else if (m_settings.m_messageType == LoRaModSettings::MessageQSOText) { + ui->messageText->setText(m_settings.m_qsoTextMessage); + } else if (m_settings.m_messageType == LoRaModSettings::MessageText) { + ui->messageText->setText(m_settings.m_textMessage); + } + + ui->messageText->blockSignals(false); +} + void LoRaModGUI::setBandwidths() { int maxBandwidth = m_basebandSampleRate / LoRaModSettings::oversampling; @@ -407,7 +552,21 @@ void LoRaModGUI::enterEvent(QEvent*) void LoRaModGUI::tick() { - double powDb = CalcDb::dbPower(m_loRaMod->getMagSq()); - m_channelPowerDbAvg(powDb); - ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + if (m_tickCount < 10) + { + m_tickCount++; + } + else + { + m_tickCount = 0; + double powDb = CalcDb::dbPower(m_loRaMod->getMagSq()); + m_channelPowerDbAvg(powDb); + ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + + if (m_loRaMod->getModulatorActive()) { + ui->playMessage->setStyleSheet("QPushButton { background-color : green; }"); + } else { + ui->playMessage->setStyleSheet("QPushButton { background:rgb(79,79,79); }"); + } + } } diff --git a/plugins/channeltx/modlora/loramodgui.h b/plugins/channeltx/modlora/loramodgui.h index c3ee9a71c..16b2744db 100644 --- a/plugins/channeltx/modlora/loramodgui.h +++ b/plugins/channeltx/modlora/loramodgui.h @@ -78,6 +78,7 @@ private: void applySettings(bool force = false); void displaySettings(); void displayStreamIndex(); + void displayCurrentPayloadMessage(); void setBandwidths(); void leaveEvent(QEvent*); @@ -93,6 +94,18 @@ private slots: void on_idleTime_valueChanged(int value); void on_syncWord_editingFinished(); void on_channelMute_toggled(bool checked); + void on_scheme_currentIndexChanged(int index); + void on_myCall_editingFinished(); + void on_urCall_editingFinished(); + void on_myLocator_editingFinished(); + void on_report_editingFinished(); + void on_msgType_currentIndexChanged(int index); + void on_resetMessages_clicked(bool checked); + void on_playMessage_clicked(bool checked); + void on_repeatMessage_valueChanged(int value); + void on_generateMessages_clicked(bool checked); + void on_messageText_editingFinished(); + void on_hexText_editingFinished(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void tick(); diff --git a/plugins/channeltx/modlora/loramodgui.ui b/plugins/channeltx/modlora/loramodgui.ui index 79208b448..7f50ed4c0 100644 --- a/plugins/channeltx/modlora/loramodgui.ui +++ b/plugins/channeltx/modlora/loramodgui.ui @@ -6,13 +6,13 @@ 0 0 - 350 - 220 + 380 + 411 - 350 + 380 180 @@ -30,7 +30,7 @@ 10 20 - 331 + 370 150 @@ -74,7 +74,7 @@ 40 50 - 170 + 221 16 @@ -102,7 +102,7 @@ 40 70 - 70 + 81 16 @@ -131,7 +131,7 @@ - 120 + 130 70 30 16 @@ -153,7 +153,7 @@ - 240 + 280 50 80 16 @@ -175,7 +175,7 @@ - 170 + 180 70 32 16 @@ -188,7 +188,7 @@ - 290 + 320 70 30 16 @@ -210,9 +210,9 @@ - 210 + 220 70 - 70 + 81 16 @@ -243,7 +243,7 @@ 10 10 - 311 + 351 26 @@ -366,7 +366,7 @@ 40 90 - 70 + 81 16 @@ -408,7 +408,7 @@ - 120 + 130 90 30 16 @@ -430,7 +430,7 @@ - 170 + 180 90 32 16 @@ -443,9 +443,9 @@ - 210 + 220 90 - 70 + 81 16 @@ -456,7 +456,7 @@ 1 - 600 + 900 1 @@ -474,7 +474,7 @@ - 290 + 320 90 30 16 @@ -497,7 +497,7 @@ 2 - 110 + 114 32 16 @@ -510,9 +510,9 @@ 40 - 110 + 112 30 - 16 + 20 @@ -528,6 +528,490 @@ 00 + + + + 90 + 114 + 50 + 16 + + + + Scheme + + + + + + 140 + 112 + 86 + 20 + + + + + LoRa + + + + + ASCII + + + + + TTY + + + + + + + + 10 + 180 + 370 + 210 + + + + + 0 + 210 + + + + Payload + + + + + 2 + 10 + 32 + 16 + + + + QSO + + + + + + 40 + 10 + 40 + 16 + + + + MyCall + + + + + + 200 + 10 + 50 + 16 + + + + YourCall + + + + + + 90 + 10 + 90 + 16 + + + + Qt::ClickFocus + + + Caller callsign + + + + + + 10 + + + + + + 260 + 10 + 90 + 16 + + + + Qt::ClickFocus + + + Callee callsign + + + + + + 10 + + + + + + 40 + 30 + 40 + 16 + + + + MyLoc + + + + + + 90 + 30 + 90 + 16 + + + + Qt::ClickFocus + + + Caller QRA locator + + + + + + 10 + + + + + + 200 + 30 + 50 + 16 + + + + Report + + + + + + 260 + 30 + 90 + 16 + + + + Qt::ClickFocus + + + Report to callee + + + + + + 10 + + + + + + 310 + 58 + 40 + 20 + + + + Generate standard messages + + + Gen + + + + + + 40 + 58 + 90 + 20 + + + + Message type + + + + None + + + + + Beacon + + + + + CQ + + + + + Reply + + + + + Report + + + + + R-Report + + + + + RRR + + + + + 73 + + + + + QSO Text + + + + + Text + + + + + Bytes + + + + + Test + + + + + + + 2 + 60 + 32 + 16 + + + + Msg + + + + + + 40 + 90 + 311 + 60 + + + + + + + 2 + 160 + 32 + 16 + + + + Hex + + + + + + 40 + 160 + 311 + 20 + + + + + + + 140 + 58 + 20 + 20 + + + + Restore default message templates + + + + + + + :/recycle.png:/recycle.png + + + + + + 170 + 58 + 20 + 20 + + + + Play message + + + + + + + :/play.png:/play.png + + + false + + + + + + 200 + 60 + 51 + 16 + + + + Repeat + + + + + + 250 + 56 + 24 + 24 + + + + Message repetition (0 for infinite) + + + 20 + + + 1 + + + 1 + + + + + + 280 + 60 + 22 + 16 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 2 + 190 + 40 + 16 + + + + Time + + + + + + 40 + 190 + 50 + 16 + + + + Payload time in milliseconds + + + 00000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 95 + 190 + 20 + 16 + + + + ms + + @@ -543,6 +1027,11 @@
gui/valuedialz.h
1 + + CustomTextEdit + QTextEdit +
gui/customtextedit.h
+
diff --git a/plugins/channeltx/modlora/loramodsettings.cpp b/plugins/channeltx/modlora/loramodsettings.cpp index 03bafd0ea..167f0b9ec 100644 --- a/plugins/channeltx/modlora/loramodsettings.cpp +++ b/plugins/channeltx/modlora/loramodsettings.cpp @@ -42,9 +42,15 @@ void LoRaModSettings::resetToDefaults() m_deBits = 0; m_preambleChirps = 8; m_quietMillis = 1000; - m_message = "Hello LoRa"; + m_codingScheme = CodingLoRa; + m_textMessage = "Hello LoRa"; + m_myCall = "MYCALL"; + m_urCall = "URCALL"; + m_myLoc = "AA00AA"; + m_myRpt = "59"; m_syncWord = 0x34; m_channelMute = false; + m_messageRepeat = 1; m_rgbColor = QColor(255, 0, 255).rgb(); m_title = "LoRa Modulator"; m_streamIndex = 0; @@ -53,7 +59,40 @@ void LoRaModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + setDefaultTemplates(); +} +void LoRaModSettings::setDefaultTemplates() +{ + // %1: myCall %2: urCall %3: myLoc %4: report + m_beaconMessage = "VVV DE %1 %2"; // Beacon + m_cqMessage = "CQ DE %1 %2"; // caller calls CQ + m_replyMessage = "%1 %2 %3"; // Reply to CQ from caller + m_reportMessage = "%1 %2 %3"; // Report to caller + m_replyReportMessage = "%1 %2 R%3"; // Report to callee + m_rrrMessage = "%1 %2 RRR"; // RRR to callee + m_73Message = "%1 %2 73"; // 73 to caller + m_qsoTextMessage = "%1 %2 %3"; // Freeflow message to caller - %3 is m_textMessage +} + +void LoRaModSettings::generateMessages() +{ + m_beaconMessage = m_beaconMessage + .arg(m_myCall).arg(m_myLoc); + m_cqMessage = m_cqMessage + .arg(m_myCall).arg(m_myLoc); + m_replyMessage = m_replyMessage + .arg(m_urCall).arg(m_myCall).arg(m_myLoc); + m_reportMessage = m_reportMessage + .arg(m_urCall).arg(m_myCall).arg(m_myRpt); + m_replyReportMessage = m_replyReportMessage + .arg(m_urCall).arg(m_myCall).arg(m_myRpt); + m_rrrMessage = m_rrrMessage + .arg(m_urCall).arg(m_myCall); + m_73Message = m_73Message + .arg(m_urCall).arg(m_myCall); + m_qsoTextMessage = m_qsoTextMessage + .arg(m_urCall).arg(m_myCall).arg(m_textMessage); } QByteArray LoRaModSettings::serialize() const @@ -62,7 +101,7 @@ QByteArray LoRaModSettings::serialize() const s.writeS32(1, m_inputFrequencyOffset); s.writeS32(2, m_bandwidthIndex); s.writeS32(3, m_spreadFactor); - s.writeString(4, m_message); + s.writeS32(4, m_codingScheme); if (m_channelMarker) { s.writeBlob(5, m_channelMarker->serialize()); @@ -79,6 +118,22 @@ QByteArray LoRaModSettings::serialize() const s.writeU32(14, m_reverseAPIPort); s.writeU32(15, m_reverseAPIDeviceIndex); s.writeU32(16, m_reverseAPIChannelIndex); + s.writeString(20, m_beaconMessage); + s.writeString(21, m_cqMessage); + s.writeString(22, m_replyMessage); + s.writeString(23, m_reportMessage); + s.writeString(24, m_replyReportMessage); + s.writeString(25, m_rrrMessage); + s.writeString(26, m_73Message); + s.writeString(27, m_qsoTextMessage); + s.writeString(28, m_textMessage); + s.writeBlob(29, m_bytesMessage); + s.writeS32(30, (int) m_messageType); + s.writeString(40, m_myCall); + s.writeString(41, m_urCall); + s.writeString(42, m_myLoc); + s.writeString(43, m_myRpt); + s.writeS32(44, m_messageRepeat); return s.final(); } @@ -97,11 +152,13 @@ bool LoRaModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; unsigned int utmp; + int tmp; d.readS32(1, &m_inputFrequencyOffset, 0); d.readS32(2, &m_bandwidthIndex, 0); d.readS32(3, &m_spreadFactor, 0); - d.readString(4, &m_message, "Hello LoRa"); + d.readS32(4, &tmp, 0); + m_codingScheme = (CodingScheme) tmp; if (m_channelMarker) { @@ -130,6 +187,23 @@ bool LoRaModSettings::deserialize(const QByteArray& data) m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; d.readU32(15, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readString(20, &m_beaconMessage, "VVV DE %1 %2"); + d.readString(21, &m_cqMessage, "CQ DE %1 %2"); + d.readString(22, &m_replyMessage, "%2 %1 %3"); + d.readString(23, &m_reportMessage, "%2 %1 %3"); + d.readString(24, &m_replyReportMessage, "%2 %1 R%3"); + d.readString(25, &m_rrrMessage, "%2 %1 RRR"); + d.readString(26, &m_73Message, "%2 %1 73"); + d.readString(27, &m_qsoTextMessage, "%2 %1 Hello LoRa"); + d.readString(28, &m_textMessage, "Hello LoRa"); + d.readBlob(29, &m_bytesMessage); + d.readS32(30, &tmp, 0); + m_messageType = (MessageType) tmp; + d.readString(40, &m_myCall, "MYCALL"); + d.readString(41, &m_urCall, "URCALL"); + d.readString(42, &m_myLoc, "AA00AA"); + d.readString(43, &m_myRpt, "59"); + d.readS32(44, &m_messageRepeat, 1); return true; } diff --git a/plugins/channeltx/modlora/loramodsettings.h b/plugins/channeltx/modlora/loramodsettings.h index ed70983e1..c0f5864a6 100644 --- a/plugins/channeltx/modlora/loramodsettings.h +++ b/plugins/channeltx/modlora/loramodsettings.h @@ -29,9 +29,25 @@ struct LoRaModSettings { enum CodingScheme { - CodingTTY, //!< plain TTY (5 bits) + CodingLoRa, //!< Standard LoRa CodingASCII, //!< plain ASCII (7 bits) - CodingLoRa //!< Standard LoRa + CodingTTY //!< plain TTY (5 bits) + }; + + enum MessageType + { + MessageNone, + MessageBeacon, + MessageCQ, + MessageReply, + MessageReport, + MessageReplyReport, + MessageRRR, + Message73, + MessageQSOText, + MessageText, + MessageBytes, + MessageTest }; int m_inputFrequencyOffset; @@ -43,11 +59,22 @@ struct LoRaModSettings unsigned char m_syncWord; bool m_channelMute; CodingScheme m_codingScheme; - QString m_message; //!< Freeflow message QString m_myCall; //!< QSO mode: my callsign QString m_urCall; //!< QSO mode: your callsign QString m_myLoc; //!< QSO mode: my locator QString m_myRpt; //!< QSO mode: my report + MessageType m_messageType; + QString m_beaconMessage; + QString m_cqMessage; + QString m_replyMessage; + QString m_reportMessage; + QString m_replyReportMessage; + QString m_rrrMessage; + QString m_73Message; + QString m_qsoTextMessage; + QString m_textMessage; + QByteArray m_bytesMessage; + int m_messageRepeat; uint32_t m_rgbColor; QString m_title; int m_streamIndex; @@ -65,6 +92,8 @@ struct LoRaModSettings LoRaModSettings(); void resetToDefaults(); + void setDefaultTemplates(); + void generateMessages(); void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } QByteArray serialize() const; bool deserialize(const QByteArray& data); diff --git a/plugins/channeltx/modlora/loramodsource.cpp b/plugins/channeltx/modlora/loramodsource.cpp index 7cc387282..d33f1d2e5 100644 --- a/plugins/channeltx/modlora/loramodsource.cpp +++ b/plugins/channeltx/modlora/loramodsource.cpp @@ -30,7 +30,9 @@ LoRaModSource::LoRaModSource() : m_modPhasor(0.0f), m_levelCalcCount(0), m_peakLevel(0.0f), - m_levelSum(0.0f) + m_levelSum(0.0f), + m_repeatCount(0), + m_active(false) { m_magsq = 0.0; @@ -184,11 +186,36 @@ void LoRaModSource::modulateSample() m_modSample = Complex{0.0, 0.0}; m_sampleCounter++; - if (m_sampleCounter == m_quietSamples*LoRaModSettings::oversampling) + if (m_sampleCounter == m_quietSamples*LoRaModSettings::oversampling) // done with quiet time { m_chirp0 = 0; m_chirp = m_fftLength*LoRaModSettings::oversampling - 1; - m_state = LoRaStatePreamble; + + if (m_symbols.size() != 0) // some payload to transmit + { + if (m_settings.m_messageRepeat == 0) // infinite + { + m_state = LoRaStatePreamble; + m_active = true; + } + else + { + if (m_repeatCount != 0) + { + m_repeatCount--; + m_state = LoRaStatePreamble; + m_active = true; + } + else + { + m_active = false; + } + } + } + else + { + m_active = false; + } } } else if (m_state == LoRaStatePreamble) @@ -356,6 +383,10 @@ void LoRaModSource::applySettings(const LoRaModSettings& settings, bool force) reset(); } + if ((settings.m_messageRepeat != m_settings.m_messageRepeat) || force) { + m_repeatCount = settings.m_messageRepeat; + } + m_settings = settings; } @@ -388,4 +419,14 @@ void LoRaModSource::applyChannelSettings(int channelSampleRate, int bandwidth, i m_quietSamples = (bandwidth*m_settings.m_quietMillis) / 1000; m_state = LoRaStateIdle; reset(); -} \ No newline at end of file +} + +void LoRaModSource::setSymbols(const std::vector& symbols) +{ + m_symbols = symbols; + qDebug("LoRaModSource::setSymbols: m_symbols: %lu", m_symbols.size()); + m_repeatCount = m_settings.m_messageRepeat; + m_state = LoRaStateIdle; // first reset to idle + reset(); + m_sampleCounter = m_quietSamples*LoRaModSettings::oversampling - 1; // start immediately +} diff --git a/plugins/channeltx/modlora/loramodsource.h b/plugins/channeltx/modlora/loramodsource.h index 4ce53bd93..0dd009f88 100644 --- a/plugins/channeltx/modlora/loramodsource.h +++ b/plugins/channeltx/modlora/loramodsource.h @@ -47,6 +47,8 @@ public: } void applySettings(const LoRaModSettings& settings, bool force = false); void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false); + void setSymbols(const std::vector& symbols); + bool getActive() const { return m_active; } private: enum LoRaMode @@ -83,6 +85,8 @@ private: unsigned int m_chirpCount; //!< chirp or quarter chirp counter unsigned int m_quietSamples; //!< number of samples during quiet period unsigned int m_quarterSamples; //!< number of samples in a quarter chirp + unsigned int m_repeatCount; //!< message repetition counter + bool m_active; //!< modulator is in a sending sequence (icluding periodic quiet times) NCO m_carrierNco; double m_modPhasor; //!< baseband modulator phasor