diff --git a/ft8/ft8.cpp b/ft8/ft8.cpp index f13674eb8..a64911f60 100644 --- a/ft8/ft8.cpp +++ b/ft8/ft8.cpp @@ -2566,8 +2566,14 @@ int FT8::decode(const float ll174[], int a174[], FT8Params& _params, int use_osd if (OSD::check_crc(a174)) { // success! return 1; + } else { + comment = "CRC fail"; } } + else + { + comment = "LDPC fail"; + } if (use_osd && _params.osd_depth >= 0 && ldpc_ok >= _params.osd_ldpc_thresh) { @@ -2582,6 +2588,10 @@ int FT8::decode(const float ll174[], int a174[], FT8Params& _params, int use_osd OSD::ldpc_encode(oplain, a174); return 1; } + else + { + comment = "OSD fail"; + } } return 0; diff --git a/ft8/packing.cpp b/ft8/packing.cpp index aa5eac1aa..84585792e 100644 --- a/ft8/packing.cpp +++ b/ft8/packing.cpp @@ -934,11 +934,11 @@ bool Packing::packfree(int a77[], const std::string& msg) return true; } -void Packing::pack1(int a77[], int c28_1, int c28_2, int g15, int report) +void Packing::pack1(int a77[], int c28_1, int c28_2, int g15, int reply) { pa64(a77, 0, 28, c28_1); pa64(a77, 28+1, 28, c28_2); - a77[28+1+28+1] = report; + a77[28+1+28+1] = reply; pa64(a77, 28+1+28+2, 15, g15); pa64(a77, 28+1+28+2+15, 3, 1); } diff --git a/ft8/packing.h b/ft8/packing.h index c3caa9775..f828c183e 100644 --- a/ft8/packing.h +++ b/ft8/packing.h @@ -40,7 +40,7 @@ public: static bool packcall_std(int& c28, const std::string& callstr); static bool packgrid(int& g15, const std::string& locstr); static bool packfree(int a77[], const std::string& msg); - static void pack1(int a77[], int c28_1, int c28_2, int g15, int report); + static void pack1(int a77[], int c28_1, int c28_2, int g15, int reply); private: static int ihashcall(std::string call, int m); diff --git a/plugins/channelrx/demodchirpchat/CMakeLists.txt b/plugins/channelrx/demodchirpchat/CMakeLists.txt index f312b65e4..aafc386d1 100644 --- a/plugins/channelrx/demodchirpchat/CMakeLists.txt +++ b/plugins/channelrx/demodchirpchat/CMakeLists.txt @@ -1,5 +1,10 @@ project(chirpchat) +if (FT8_SUPPORT) + set(chirpchatmod_FT8_LIB ft8) + set(chirpchatmod_FT8_INCLUDE ${CMAKE_SOURCE_DIR}/ft8) +endif() + set(chirpchat_SOURCES chirpchatdemod.cpp chirpchatdemodsettings.cpp @@ -10,6 +15,7 @@ set(chirpchat_SOURCES chirpchatdemoddecodertty.cpp chirpchatdemoddecoderascii.cpp chirpchatdemoddecoderlora.cpp + chirpchatdemoddecoderft.cpp chirpchatdemodmsg.cpp ) @@ -22,12 +28,14 @@ set(chirpchat_HEADERS chirpchatdemoddecodertty.h chirpchatdemoddecoderascii.h chirpchatdemoddecoderlora.h + chirpchatdemoddecoderft.h chirpchatdemodmsg.h chirpchatplugin.h ) include_directories( ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${chirpchatmod_FT8_INCLUDE} ) if(NOT SERVER_MODE) @@ -61,6 +69,7 @@ target_link_libraries(${TARGET_NAME} sdrbase ${TARGET_LIB_GUI} swagger + ${chirpchatmod_FT8_LIB} ) install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp index df90d9195..cea13d828 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemod.cpp @@ -318,6 +318,29 @@ bool ChirpChatDemod::handleMessage(const Message& cmd) return true; } + else if (ChirpChatDemodMsg::MsgReportDecodeFT::match(cmd)) + { + qDebug() << "ChirpChatDemod::handleMessage: MsgReportDecodeFT"; + ChirpChatDemodMsg::MsgReportDecodeFT& msg = (ChirpChatDemodMsg::MsgReportDecodeFT&) cmd; + m_lastMsgSignalDb = msg.getSingalDb(); + m_lastMsgNoiseDb = msg.getNoiseDb(); + m_lastMsgSyncWord = msg.getSyncWord(); + m_lastMsgTimestamp = msg.getMsgTimestamp(); + m_lastMsgString = msg.getMessage(); // for now we do not handle message components (call1, ...) + + if (m_settings.m_sendViaUDP) + { + const QByteArray& byteArray = m_lastMsgString.toUtf8(); + const uint8_t *bytes = reinterpret_cast(byteArray.data()); + m_udpSink.writeUnbuffered(bytes, byteArray.size()); + } + + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new ChirpChatDemodMsg::MsgReportDecodeFT(msg)); // make a copy + } + + return true; + } else if (DSPSignalNotification::match(cmd)) { DSPSignalNotification& notif = (DSPSignalNotification&) cmd; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.cpp index 6bc23c577..3a4b77970 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.cpp @@ -21,6 +21,7 @@ #include "chirpchatdemoddecodertty.h" #include "chirpchatdemoddecoderascii.h" #include "chirpchatdemoddecoderlora.h" +#include "chirpchatdemoddecoderft.h" #include "chirpchatdemodmsg.h" ChirpChatDemodDecoder::ChirpChatDemodDecoder() : @@ -60,7 +61,7 @@ void ChirpChatDemodDecoder::decodeSymbols(const std::vector& sym } break; case ChirpChatDemodSettings::CodingASCII: - if (m_nbSymbolBits == 5) { + if (m_nbSymbolBits == 7) { ChirpChatDemodDecoderASCII::decodeSymbols(symbols, str); } break; @@ -106,6 +107,33 @@ void ChirpChatDemodDecoder::decodeSymbols(const std::vector& sym } } +void ChirpChatDemodDecoder::decodeSymbols( //!< For FT coding scheme + const std::vector>& mags, // vector of symbols magnitudes + int nbSymbolBits, //!< number of bits per symbol + std::string& msg, //!< formatted message + std::string& call1, //!< 1st callsign or shorthand + std::string& call2, //!< 2nd callsign + std::string& loc, //!< locator, report or shorthand + bool& reply //!< true if message is a reply report +) +{ + if (m_codingScheme != ChirpChatDemodSettings::CodingFT) { + return; + } + + ChirpChatDemodDecoderFT::decodeSymbols( + mags, + nbSymbolBits, + msg, + call1, + call2, + loc, + reply, + m_payloadParityStatus, + m_payloadCRCStatus + ); +} + bool ChirpChatDemodDecoder::handleMessage(const Message& cmd) { if (ChirpChatDemodMsg::MsgDecodeSymbols::match(cmd)) @@ -145,7 +173,34 @@ bool ChirpChatDemodDecoder::handleMessage(const Message& cmd) } else if (m_codingScheme == ChirpChatDemodSettings::CodingFT) { + std::string fmsg, call1, call2, loc; + bool reply; + decodeSymbols( + msg.getMagnitudes(), + m_nbSymbolBits, + fmsg, + call1, + call2, + loc, + reply + ); + if (m_outputMessageQueue) + { + ChirpChatDemodMsg::MsgReportDecodeFT *outputMsg = ChirpChatDemodMsg::MsgReportDecodeFT::create(); + outputMsg->setSyncWord(msgSyncWord); + outputMsg->setSignalDb(msgSignalDb); + outputMsg->setNoiseDb(msgNoiseDb); + outputMsg->setMsgTimestamp(msgTimestamp); + outputMsg->setMessage(QString(fmsg.c_str())); + outputMsg->setCall1(QString(call1.c_str())); + outputMsg->setCall2(QString(call2.c_str())); + outputMsg->setLoc(QString(loc.c_str())); + outputMsg->setReply(reply); + outputMsg->setPayloadParityStatus(getPayloadParityStatus()); + outputMsg->setPayloadCRCStatus(getPayloadCRCStatus()); + m_outputMessageQueue->push(outputMsg); + } } else { diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.h b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.h index e818fbda5..3abcc8cd5 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoder.h @@ -40,8 +40,22 @@ public: void setLoRaHasHeader(bool hasHeader) { m_hasHeader = hasHeader; } void setLoRaHasCRC(bool hasCRC) { m_hasCRC = hasCRC; } void setLoRaPacketLength(unsigned int packetLength) { m_packetLength = packetLength; } + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + void setOutputMessageQueue(MessageQueue *messageQueue) { m_outputMessageQueue = messageQueue; } + +private: + bool handleMessage(const Message& cmd); void decodeSymbols(const std::vector& symbols, QString& str); //!< For ASCII and TTY void decodeSymbols(const std::vector& symbols, QByteArray& bytes); //!< For raw bytes (original LoRa) + void decodeSymbols( //!< For FT coding scheme + const std::vector>& mags, // vector of symbols magnitudes + int nbSymbolBits, //!< number of bits per symbol + std::string& msg, //!< formatted message + std::string& call1, //!< 1st callsign or shorthand + std::string& call2, //!< 2nd callsign + std::string& loc, //!< locator, report or shorthand + bool& reply //!< true if message is a reply report + ); unsigned int getNbParityBits() const { return m_nbParityBits; } unsigned int getPacketLength() const { return m_packetLength; } bool getHasCRC() const { return m_hasCRC; } @@ -52,11 +66,6 @@ public: bool getHeaderCRCStatus() const { return m_headerCRCStatus; } int getPayloadParityStatus() const { return m_payloadParityStatus; } bool getPayloadCRCStatus() const { return m_payloadCRCStatus; } - MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } - void setOutputMessageQueue(MessageQueue *messageQueue) { m_outputMessageQueue = messageQueue; } - -private: - bool handleMessage(const Message& cmd); ChirpChatDemodSettings::CodingScheme m_codingScheme; unsigned int m_spreadFactor; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.cpp new file mode 100644 index 000000000..1cabc0361 --- /dev/null +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.cpp @@ -0,0 +1,111 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "chirpchatdemodsettings.h" +#include "chirpchatdemoddecoderft.h" + +#ifndef HAS_FT8 +void ChirpChatDemodDecoderFT::decodeSymbols( + const std::vector>& mags, // vector of symbols magnitudes + int nbSymbolBits, //!< number of bits per symbol + QString& msg, //!< formatted message + QString& call1, //!< 1st callsign or shorthand + QString& call2, //!< 2nd callsign + QString& loc, //!< locator, report or shorthand + bool& reply //!< true if message is a reply report +) +{ + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: not implemented"); +} +#else + +#include "ft8.h" +#include "packing.h" + +void ChirpChatDemodDecoderFT::decodeSymbols( + const std::vector>& mags, // vector of symbols magnitudes + int nbSymbolBits, //!< number of bits per symbol + std::string& msg, //!< formatted message + std::string& call1, //!< 1st callsign or shorthand + std::string& call2, //!< 2nd callsign + std::string& loc, //!< locator, report or shorthand + bool& reply, //!< true if message is a reply report + int& payloadParityStatus, + bool& payloadCRCStatus +) +{ + if (mags.size()*nbSymbolBits < 174) + { + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: insufficient number of symbols for FT payload"); + return; + } + + float *lls = new float[mags.size()*nbSymbolBits]; // bits log likelihoods (>0 for 0, <0 for 1) + std::fill(lls, lls+mags.size()*nbSymbolBits, 0.0); + FT8::FT8Params params; + FT8::FT8::soft_decode_mags(params, mags, nbSymbolBits, lls); + int r174[174]; + std::string comments; + payloadParityStatus = (int) ChirpChatDemodSettings::ParityOK; + payloadCRCStatus = false; + + if (FT8::FT8::decode(lls, r174, params, 0, comments) == 0) + { + if (comments == "LDPC fail") + { + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: LDPC failed"); + payloadParityStatus = (int) ChirpChatDemodSettings::ParityError; + } + else if (comments == "OSD fail") + { + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: OSD failed"); + payloadParityStatus = (int) ChirpChatDemodSettings::ParityError; + } + else if (comments == "CRC fail") + { + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: CRC failed"); + } + else + { + qWarning("ChirpChatDemodDecoderFT::decodeSymbols: decode failed for unknown reason"); + payloadParityStatus = (int) ChirpChatDemodSettings::ParityUndefined; + } + + return; + } + + payloadCRCStatus = true; + FT8::Packing packing; + std::string msgType; + msg = packing.unpack(r174, call1, call2, loc, msgType); + reply = false; + + if ((msgType == "0.3") || (msgType == "0.3")) { + reply = r174[56] != 0; + } + if ((msgType == "1") || (msgType == "2")) { + reply = r174[58] != 0; + } + if ((msgType == "3")) { + reply = r174[57] != 0; + } + if ((msgType == "5")) { + reply = r174[34] != 0; + } +} + +#endif // HAS_FT8 diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.h b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.h new file mode 100644 index 000000000..a6975d230 --- /dev/null +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderft.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2024 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_CHIRPCHATDEMODDECODERFT_H +#define INCLUDE_CHIRPCHATDEMODDECODERFT_H + +#include +#include + +class ChirpChatDemodDecoderFT +{ +public: + enum ParityStatus + { + ParityUndefined, + ParityError, + ParityCorrected, + ParityOK + }; + + static void decodeSymbols( + const std::vector>& mags, // vector of symbols magnitudes + int nbSymbolBits, //!< number of bits per symbol + std::string& msg, //!< formatted message + std::string& call1, //!< 1st callsign or shorthand + std::string& call2, //!< 2nd callsign + std::string& loc, //!< locator, report or shorthand + bool& reply , //!< true if message is a reply report + int& payloadParityStatus, + bool& payloadCRCStatus + ); +}; + + +#endif diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.cpp index 3bf878414..684c637c0 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.cpp @@ -17,6 +17,7 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include "chirpchatdemodsettings.h" #include "chirpchatdemoddecoderlora.h" void ChirpChatDemodDecoderLoRa::decodeHeader( @@ -71,14 +72,14 @@ void ChirpChatDemodDecoderLoRa::decodeHeader( if (bad) { - headerParityStatus = (int) ParityError; + headerParityStatus = (int) ChirpChatDemodSettings::ParityError; } else { if (error) { - headerParityStatus = (int) ParityCorrected; + headerParityStatus = (int) ChirpChatDemodSettings::ParityCorrected; } else { - headerParityStatus = (int) ParityOK; + headerParityStatus = (int) ChirpChatDemodSettings::ParityOK; } if (bytes[2] != 0) { @@ -300,11 +301,11 @@ void ChirpChatDemodDecoderLoRa::decodeBytes( } if (bad) { - payloadParityStatus = (int) ParityError; + payloadParityStatus = (int) ChirpChatDemodSettings::ParityError; } else if (error) { - payloadParityStatus = (int) ParityCorrected; + payloadParityStatus = (int) ChirpChatDemodSettings::ParityCorrected; } else { - payloadParityStatus = (int) ParityOK; + payloadParityStatus = (int) ChirpChatDemodSettings::ParityOK; } // finalization: diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.h b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.h index e9fb63e5a..6459ee2bb 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemoddecoderlora.h @@ -26,14 +26,6 @@ class ChirpChatDemodDecoderLoRa { public: - enum ParityStatus - { - ParityUndefined, - ParityError, - ParityCorrected, - ParityOK - }; - static void decodeBytes( QByteArray& bytes, const std::vector& inSymbols, diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp index be771ac87..54786e9cf 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.cpp @@ -115,8 +115,16 @@ bool ChirpChatDemodGUI::handleMessage(const Message& message) else if (ChirpChatDemodMsg::MsgReportDecodeString::match(message)) { if ((m_settings.m_codingScheme == ChirpChatDemodSettings::CodingASCII) - || (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingTTY)) { - showTextMessage(message); + || (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingTTY)) { + showTextMessage(message); + } + + return true; + } + else if (ChirpChatDemodMsg::MsgReportDecodeFT::match(message)) + { + if (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingFT) { + showFTMessage(message); } return true; @@ -544,11 +552,11 @@ void ChirpChatDemodGUI::displaySquelch() void ChirpChatDemodGUI::displayLoRaStatus(int headerParityStatus, bool headerCRCStatus, int payloadParityStatus, bool payloadCRCStatus) { - if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityOK)) { + if (m_settings.m_hasHeader && (headerParityStatus == (int) ChirpChatDemodSettings::ParityOK)) { ui->headerHammingStatus->setStyleSheet("QLabel { background-color : green; }"); - } else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityError)) { + } else if (m_settings.m_hasHeader && (headerParityStatus == (int) ChirpChatDemodSettings::ParityError)) { ui->headerHammingStatus->setStyleSheet("QLabel { background-color : red; }"); - } else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityCorrected)) { + } else if (m_settings.m_hasHeader && (headerParityStatus == (int) ChirpChatDemodSettings::ParityCorrected)) { ui->headerHammingStatus->setStyleSheet("QLabel { background-color : blue; }"); } else { ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); @@ -562,11 +570,11 @@ void ChirpChatDemodGUI::displayLoRaStatus(int headerParityStatus, bool headerCRC ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); } - if (payloadParityStatus == (int) ParityOK) { + if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityOK) { ui->payloadFECStatus->setStyleSheet("QLabel { background-color : green; }"); - } else if (payloadParityStatus == (int) ParityError) { + } else if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityError) { ui->payloadFECStatus->setStyleSheet("QLabel { background-color : red; }"); - } else if (payloadParityStatus == (int) ParityCorrected) { + } else if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityCorrected) { ui->payloadFECStatus->setStyleSheet("QLabel { background-color : blue; }"); } else { ui->payloadFECStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); @@ -589,6 +597,25 @@ void ChirpChatDemodGUI::resetLoRaStatus() ui->nbCodewordsText->setText("---"); } +void ChirpChatDemodGUI::displayFTStatus(int payloadParityStatus, bool payloadCRCStatus) +{ + if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityOK) { + ui->payloadFECStatus->setStyleSheet("QLabel { background-color : green; }"); + } else if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityError) { + ui->payloadFECStatus->setStyleSheet("QLabel { background-color : red; }"); + } else if (payloadParityStatus == (int) ChirpChatDemodSettings::ParityCorrected) { + ui->payloadFECStatus->setStyleSheet("QLabel { background-color : blue; }"); + } else { + ui->payloadFECStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); + } + + if (payloadCRCStatus) { + ui->payloadCRCStatus->setStyleSheet("QLabel { background-color : green; }"); + } else { + ui->payloadCRCStatus->setStyleSheet("QLabel { background-color : red; }"); + } +} + void ChirpChatDemodGUI::setBandwidths() { int maxBandwidth = m_basebandSampleRate/ChirpChatDemodSettings::oversampling; @@ -644,7 +671,7 @@ void ChirpChatDemodGUI::showLoRaMessage(const Message& message) .arg(msg.getHeaderCRCStatus() ? "ok" : "err"); displayStatus(loRaStatus); - displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), (int) ParityUndefined, true); + displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), (int) ChirpChatDemodSettings::ParityUndefined, true); ui->payloadCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); // reset payload CRC } else @@ -694,6 +721,27 @@ void ChirpChatDemodGUI::showTextMessage(const Message& message) displayText(msg.getString()); } +void ChirpChatDemodGUI::showFTMessage(const Message& message) +{ + const ChirpChatDemodMsg::MsgReportDecodeFT& msg = (ChirpChatDemodMsg::MsgReportDecodeFT&) message; + + QDateTime dt = QDateTime::currentDateTime(); + QString dateStr = dt.toString("HH:mm:ss"); + ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1)); + ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1)); + + QString status = tr("%1 S:%2 SN:%3 FEC:%4 CRC:%5") + .arg(dateStr) + .arg(msg.getSingalDb(), 0, 'f', 1) + .arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1) + .arg(getParityStr(msg.getPayloadParityStatus())) + .arg(msg.getPayloadCRCStatus() ? "ok" : "err"); + + displayStatus(status); + displayText(msg.getMessage()); // We do not show constituents of the message (call1, ...) + displayFTStatus(msg.getPayloadParityStatus(), msg.getPayloadCRCStatus()); +} + void ChirpChatDemodGUI::displayText(const QString& text) { QTextCursor cursor = ui->messageText->textCursor(); @@ -754,11 +802,11 @@ void ChirpChatDemodGUI::displayStatus(const QString& status) QString ChirpChatDemodGUI::getParityStr(int parityStatus) { - if (parityStatus == (int) ParityError) { + if (parityStatus == (int) ChirpChatDemodSettings::ParityError) { return "err"; - } else if (parityStatus == (int) ParityCorrected) { + } else if (parityStatus == (int) ChirpChatDemodSettings::ParityCorrected) { return "fix"; - } else if (parityStatus == (int) ParityOK) { + } else if (parityStatus == (int) ChirpChatDemodSettings::ParityOK) { return "ok"; } else { return "n/a"; diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h index 8ce74aa30..277ae430c 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodgui.h @@ -89,14 +89,6 @@ private slots: void tick(); private: - enum ParityStatus // matches decoder status - { - ParityUndefined, - ParityError, - ParityCorrected, - ParityOK - }; - Ui::ChirpChatDemodGUI* ui; PluginAPI* m_pluginAPI; DeviceUISet* m_deviceUISet; @@ -120,12 +112,14 @@ private: void displaySettings(); void displaySquelch(); void setBandwidths(); - void showLoRaMessage(const Message& message); + void showLoRaMessage(const Message& message); //!< For LoRa coding scheme void showTextMessage(const Message& message); //!< For TTY and ASCII + void showFTMessage(const Message& message); //!< For FT coding scheme void displayText(const QString& text); void displayBytes(const QByteArray& bytes); void displayStatus(const QString& status); void displayLoRaStatus(int headerParityStatus, bool headerCRCStatus, int payloadParityStatus, bool payloadCRCStatus); + void displayFTStatus(int payloadParityStatus, bool payloadCRCStatus); QString getParityStr(int parityStatus); void resetLoRaStatus(); bool handleMessage(const Message& message); diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodmsg.h b/plugins/channelrx/demodchirpchat/chirpchatdemodmsg.h index 45f30061a..2969916e1 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodmsg.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodmsg.h @@ -21,6 +21,8 @@ #include #include "util/message.h" +#include "chirpchatdemodsettings.h" + namespace ChirpChatDemodMsg { class MsgDecodeSymbols : public Message { @@ -178,7 +180,7 @@ namespace ChirpChatDemodMsg m_earlyEOM(false), m_headerParityStatus(false), m_headerCRCStatus(false), - m_payloadParityStatus(false), + m_payloadParityStatus((int) ChirpChatDemodSettings::ParityUndefined), m_payloadCRCStatus(false) { } }; @@ -240,6 +242,8 @@ namespace ChirpChatDemodMsg float getSingalDb() const { return m_signalDb; } float getNoiseDb() const { return m_noiseDb; } const QString& getMsgTimestamp() const { return m_msgTimestamp; } + int getPayloadParityStatus() const { return m_payloadParityStatus; } + bool getPayloadCRCStatus() const { return m_payloadCRCStatus; } static MsgReportDecodeFT* create() { @@ -275,6 +279,12 @@ namespace ChirpChatDemodMsg void setMsgTimestamp(const QString& ts) { m_msgTimestamp = ts; } + void setPayloadParityStatus(int payloadParityStatus) { + m_payloadParityStatus = payloadParityStatus; + } + void setPayloadCRCStatus(bool payloadCRCStatus) { + m_payloadCRCStatus = payloadCRCStatus; + } private: QString m_message; @@ -287,6 +297,8 @@ namespace ChirpChatDemodMsg float m_signalDb; float m_noiseDb; QString m_msgTimestamp; + int m_payloadParityStatus; + bool m_payloadCRCStatus; MsgReportDecodeFT() : Message(), @@ -294,7 +306,9 @@ namespace ChirpChatDemodMsg m_freeText(false), m_syncWord(0), m_signalDb(0.0), - m_noiseDb(0.0) + m_noiseDb(0.0), + m_payloadParityStatus((int) ChirpChatDemodSettings::ParityUndefined), + m_payloadCRCStatus(false) { } }; } diff --git a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h index 051312d9c..f66f1ef58 100644 --- a/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h +++ b/plugins/channelrx/demodchirpchat/chirpchatdemodsettings.h @@ -41,6 +41,14 @@ struct ChirpChatDemodSettings CodingFT //!< FT8/4 scheme (payload 174 bits LDPC) }; + enum ParityStatus + { + ParityUndefined, + ParityError, + ParityCorrected, + ParityOK + }; + int m_inputFrequencyOffset; int m_bandwidthIndex; int m_spreadFactor; diff --git a/plugins/channeltx/modchirpchat/chirpchatmodencoderft.cpp b/plugins/channeltx/modchirpchat/chirpchatmodencoderft.cpp index c874fc350..8e50699da 100644 --- a/plugins/channeltx/modchirpchat/chirpchatmodencoderft.cpp +++ b/plugins/channeltx/modchirpchat/chirpchatmodencoderft.cpp @@ -96,7 +96,7 @@ void ChirpChatModEncoderFT::encodeTextMsg(const QString& text, int a174[]) std::fill(a77, a77 + 77, 0); QString sentMsg = text.rightJustified(13, ' ', true); - if (!FT8::Packing::packfree(a77, sentMsg.toStdString())) + if (!FT8::Packing::packfree(a77, sentMsg.toUpper().toStdString())) { qDebug("ChirpChatModEncoderFT::encodeTextMsg: failed to encode free text message (%s)", qPrintable(sentMsg)); return; @@ -109,21 +109,27 @@ void ChirpChatModEncoderFT::encodeMsgBeaconOrCQ(const QString& myCall, const QSt { int c28_1, c28_2, g15; - if (!FT8::Packing::packcall_std(c28_1, shorthand.toStdString())) // + if (!FT8::Packing::packcall_std(c28_1, shorthand.toUpper().toStdString())) // { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(shorthand)); + qDebug("ChirpChatModEncoderFT::encodeMsgBeaconOrCQ: failed to encode call1 (%s)", qPrintable(shorthand)); return; } - if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString())) + if (!FT8::Packing::packcall_std(c28_2, myCall.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgBeaconOrCQ: failed to encode call2 (%s)", qPrintable(myCall)); return; } - if (!FT8::Packing::packgrid(g15, myLocator.toStdString())) + if (myLocator.size() < 4) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode locator (%s)", qPrintable(myLocator)); + qDebug("ChirpChatModEncoderFT::encodeMsgBeaconOrCQ: locator invalid (%s)", qPrintable(myLocator)); + return; + } + + if (!FT8::Packing::packgrid(g15, myLocator.left(4).toUpper().toStdString())) + { + qDebug("ChirpChatModEncoderFT::encodeMsgBeaconOrCQ: failed to encode locator (%s)", qPrintable(myLocator)); return; } @@ -137,21 +143,27 @@ void ChirpChatModEncoderFT::encodeMsgReply(const QString& myCall, const QString& { int c28_1, c28_2, g15; - if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) // + if (!FT8::Packing::packcall_std(c28_1, urCall.toUpper().toStdString())) // { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgReply: failed to encode call1 (%s)", qPrintable(urCall)); return; } - if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString())) + if (!FT8::Packing::packcall_std(c28_2, myCall.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgReply: failed to encode call2 (%s)", qPrintable(myCall)); return; } - if (!FT8::Packing::packgrid(g15, myLocator.toStdString())) + if (myLocator.size() < 4) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode locator (%s)", qPrintable(myLocator)); + qDebug("ChirpChatModEncoderFT::encodeMsgReply: locator invalid (%s)", qPrintable(myLocator)); + return; + } + + if (!FT8::Packing::packgrid(g15, myLocator.left(4).toUpper().toStdString())) + { + qDebug("ChirpChatModEncoderFT::encodeMsgReply: failed to encode locator (%s)", qPrintable(myLocator)); return; } @@ -165,21 +177,21 @@ void ChirpChatModEncoderFT::encodeMsgReport(const QString& myCall, const QString { int c28_1, c28_2, g15; - if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) // + if (!FT8::Packing::packcall_std(c28_1, urCall.toUpper().toStdString())) // { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgReport: failed to encode call1 (%s)", qPrintable(urCall)); return; } - if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString())) + if (!FT8::Packing::packcall_std(c28_2, myCall.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgReport: failed to encode call2 (%s)", qPrintable(myCall)); return; } - if (!FT8::Packing::packgrid(g15, myReport.toStdString())) + if (!FT8::Packing::packgrid(g15, myReport.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode report (%s)", qPrintable(myReport)); + qDebug("ChirpChatModEncoderFT::encodeMsgReport: failed to encode report (%s)", qPrintable(myReport)); return; } @@ -193,21 +205,21 @@ void ChirpChatModEncoderFT::encodeMsgFinish(const QString& myCall, const QString { int c28_1, c28_2, g15; - if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) // + if (!FT8::Packing::packcall_std(c28_1, urCall.toUpper().toStdString())) // { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgFinish: failed to encode call1 (%s)", qPrintable(urCall)); return; } - if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString())) + if (!FT8::Packing::packcall_std(c28_2, myCall.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall)); + qDebug("ChirpChatModEncoderFT::encodeMsgFinish: failed to encode call2 (%s)", qPrintable(myCall)); return; } - if (!FT8::Packing::packgrid(g15, shorthand.toStdString())) + if (!FT8::Packing::packgrid(g15, shorthand.toUpper().toStdString())) { - qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode shorthand (%s)", qPrintable(shorthand)); + qDebug("ChirpChatModEncoderFT::encodeMsgFinish: failed to encode shorthand (%s)", qPrintable(shorthand)); return; }