Removed LoRa demod and added ChirpChat demod (1)

This commit is contained in:
f4exb 2020-11-09 11:35:18 +01:00
parent 9024d3f6fa
commit 00885a48c9
60 changed files with 8578 additions and 1445 deletions

View File

@ -12,6 +12,7 @@ add_subdirectory(demodwfm)
add_subdirectory(localsink)
add_subdirectory(filesink)
add_subdirectory(freqtracker)
add_subdirectory(demodchirpchat)
if(LIBDSDCC_FOUND AND LIBMBE_FOUND)
add_subdirectory(demoddsd)
@ -26,7 +27,6 @@ if (CODEC2_FOUND)
endif(CODEC2_FOUND)
if(NOT SERVER_MODE)
add_subdirectory(demodlora)
add_subdirectory(chanalyzer)
add_subdirectory(demodatv)

View File

@ -0,0 +1,66 @@
project(chirpchat)
set(chirpchat_SOURCES
chirpchatdemod.cpp
chirpchatdemodsettings.cpp
chirpchatdemodsink.cpp
chirpchatdemodbaseband.cpp
chirpchatplugin.cpp
chirpchatdemoddecoder.cpp
chirpchatdemoddecodertty.cpp
chirpchatdemoddecoderascii.cpp
chirpchatdemoddecoderlora.cpp
chirpchatdemodmsg.cpp
)
set(chirpchat_HEADERS
chirpchatdemod.h
chirpchatdemodsettings.h
chirpchatdemodsink.h
chirpchatdemodbaseband.h
chirpchatdemoddecoder.h
chirpchatdemoddecodertty.h
chirpchatdemoddecoderascii.h
chirpchatdemoddecoderlora.h
chirpchatdemodmsg.h
chirpchatplugin.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(chirpchat_SOURCES
${chirpchat_SOURCES}
chirpchatdemodgui.cpp
chirpchatdemodgui.ui
)
set(chirpchat_HEADERS
${chirpchat_HEADERS}
chirpchatdemodgui.h
)
set(TARGET_NAME demodchirpchat)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demodchirpchatsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${chirpchat_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,796 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// (c) 2015 John Greb //
// (c) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <QTime>
#include <QDebug>
#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include "SWGChannelSettings.h"
#include "SWGChannelReport.h"
#include "SWGChirpChatDemodReport.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "util/db.h"
#include "maincore.h"
#include "chirpchatdemodmsg.h"
#include "chirpchatdemod.h"
MESSAGE_CLASS_DEFINITION(ChirpChatDemod::MsgConfigureChirpChatDemod, Message)
MESSAGE_CLASS_DEFINITION(ChirpChatDemod::MsgReportDecodeBytes, Message)
MESSAGE_CLASS_DEFINITION(ChirpChatDemod::MsgReportDecodeString, Message)
const QString ChirpChatDemod::m_channelIdURI = "sdrangel.channel.chirpchatdemod";
const QString ChirpChatDemod::m_channelId = "ChirpChatDemod";
ChirpChatDemod::ChirpChatDemod(DeviceAPI* deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_spectrumVis(SDR_RX_SCALEF),
m_basebandSampleRate(0),
m_lastMsgSignalDb(0.0),
m_lastMsgNoiseDb(0.0),
m_lastMsgSyncWord(0),
m_lastMsgPacketLength(0),
m_lastMsgNbParityBits(0),
m_lastMsgHasCRC(false),
m_lastMsgNbSymbols(0),
m_lastMsgNbCodewords(0),
m_lastMsgEarlyEOM(false),
m_lastMsgHeaderCRC(false),
m_lastMsgHeaderParityStatus(0),
m_lastMsgPayloadCRC(false),
m_lastMsgPayloadParityStatus(0),
m_udpSink(this, 256)
{
setObjectName(m_channelId);
m_thread = new QThread(this);
m_basebandSink = new ChirpChatDemodBaseband();
m_basebandSink->setSpectrumSink(&m_spectrumVis);
m_basebandSink->setDecoderMessageQueue(getInputMessageQueue()); // Decoder held on the main thread
m_basebandSink->moveToThread(m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
}
ChirpChatDemod::~ChirpChatDemod()
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
delete m_basebandSink;
delete m_thread;
}
uint32_t ChirpChatDemod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void ChirpChatDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO)
{
(void) pO;
m_basebandSink->feed(begin, end);
}
void ChirpChatDemod::start()
{
qDebug() << "ChirpChatDemod::start";
if (m_basebandSampleRate != 0) {
m_basebandSink->setBasebandSampleRate(m_basebandSampleRate);
}
m_basebandSink->reset();
m_thread->start();
GLSpectrumSettings spectrumSettings = m_spectrumVis.getSettings();
spectrumSettings.m_ssb = true;
SpectrumVis::MsgConfigureSpectrumVis *msg = SpectrumVis::MsgConfigureSpectrumVis::create(spectrumSettings, false);
m_spectrumVis.getInputMessageQueue()->push(msg);}
void ChirpChatDemod::stop()
{
qDebug() << "ChirpChatDemod::stop";
m_thread->exit();
m_thread->wait();
}
bool ChirpChatDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureChirpChatDemod::match(cmd))
{
qDebug() << "ChirpChatDemod::handleMessage: MsgConfigureChirpChatDemod";
MsgConfigureChirpChatDemod& cfg = (MsgConfigureChirpChatDemod&) cmd;
ChirpChatDemodSettings settings = cfg.getSettings();
applySettings(settings, cfg.getForce());
return true;
}
else if (ChirpChatDemodMsg::MsgDecodeSymbols::match(cmd))
{
qDebug() << "ChirpChatDemod::handleMessage: MsgDecodeSymbols";
ChirpChatDemodMsg::MsgDecodeSymbols& msg = (ChirpChatDemodMsg::MsgDecodeSymbols&) cmd;
m_lastMsgSignalDb = msg.getSingalDb();
m_lastMsgNoiseDb = msg.getNoiseDb();
m_lastMsgSyncWord = msg.getSyncWord();
if (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingLoRa)
{
m_decoder.decodeSymbols(msg.getSymbols(), m_lastMsgBytes);
QDateTime dt = QDateTime::currentDateTime();
m_lastMsgTimestamp = dt.toString(Qt::ISODateWithMs);
m_lastMsgPacketLength = m_decoder.getPacketLength();
m_lastMsgNbParityBits = m_decoder.getNbParityBits();
m_lastMsgHasCRC = m_decoder.getHasCRC();
m_lastMsgNbSymbols = m_decoder.getNbSymbols();
m_lastMsgNbCodewords = m_decoder.getNbCodewords();
m_lastMsgEarlyEOM = m_decoder.getEarlyEOM();
m_lastMsgHeaderCRC = m_decoder.getHeaderCRCStatus();
m_lastMsgHeaderParityStatus = m_decoder.getHeaderParityStatus();
m_lastMsgPayloadCRC = m_decoder.getPayloadCRCStatus();
m_lastMsgPayloadParityStatus = m_decoder.getPayloadParityStatus();
QByteArray bytesCopy(m_lastMsgBytes);
bytesCopy.truncate(m_lastMsgPacketLength);
bytesCopy.replace('\0', " ");
m_lastMsgString = QString(bytesCopy.toStdString().c_str());
if (m_settings.m_sendViaUDP)
{
uint8_t *bytes = reinterpret_cast<uint8_t*>(m_lastMsgBytes.data());
m_udpSink.writeUnbuffered(bytes, m_lastMsgPacketLength);
}
if (getMessageQueueToGUI())
{
MsgReportDecodeBytes *msgToGUI = MsgReportDecodeBytes::create(m_lastMsgBytes);
msgToGUI->setSyncWord(m_lastMsgSyncWord);
msgToGUI->setSignalDb(m_lastMsgSignalDb);
msgToGUI->setNoiseDb(m_lastMsgNoiseDb);
msgToGUI->setPacketSize(m_lastMsgPacketLength);
msgToGUI->setNbParityBits(m_lastMsgNbParityBits);
msgToGUI->setHasCRC(m_lastMsgHasCRC);
msgToGUI->setNbSymbols(m_lastMsgNbSymbols);
msgToGUI->setNbCodewords(m_lastMsgNbCodewords);
msgToGUI->setEarlyEOM(m_lastMsgEarlyEOM);
msgToGUI->setHeaderParityStatus(m_lastMsgHeaderParityStatus);
msgToGUI->setHeaderCRCStatus(m_lastMsgHeaderCRC);
msgToGUI->setPayloadParityStatus(m_lastMsgPayloadParityStatus);
msgToGUI->setPayloadCRCStatus(m_lastMsgPayloadCRC);
getMessageQueueToGUI()->push(msgToGUI);
}
if (m_settings.m_autoNbSymbolsMax)
{
ChirpChatDemodSettings settings = m_settings;
settings.m_nbSymbolsMax = m_lastMsgNbSymbols;
applySettings(settings);
if (getMessageQueueToGUI()) // forward to GUI if any
{
MsgConfigureChirpChatDemod *msgToGUI = MsgConfigureChirpChatDemod::create(settings, false);
getMessageQueueToGUI()->push(msgToGUI);
}
}
}
else
{
m_decoder.decodeSymbols(msg.getSymbols(), m_lastMsgString);
QDateTime dt = QDateTime::currentDateTime();
m_lastMsgTimestamp = dt.toString(Qt::ISODateWithMs);
if (m_settings.m_sendViaUDP)
{
const QByteArray& byteArray = m_lastMsgString.toUtf8();
const uint8_t *bytes = reinterpret_cast<const uint8_t*>(byteArray.data());
m_udpSink.writeUnbuffered(bytes, byteArray.size());
}
if (getMessageQueueToGUI())
{
MsgReportDecodeString *msgToGUI = MsgReportDecodeString::create(m_lastMsgString);
msgToGUI->setSyncWord(m_lastMsgSyncWord);
msgToGUI->setSignalDb(m_lastMsgSignalDb);
msgToGUI->setNoiseDb(m_lastMsgNoiseDb);
getMessageQueueToGUI()->push(msgToGUI);
}
}
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "ChirpChatDemod::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << m_basebandSampleRate;
m_basebandSink->getInputMessageQueue()->push(rep);
if (getMessageQueueToGUI())
{
DSPSignalNotification* repToGUI = new DSPSignalNotification(notif); // make a copy
getMessageQueueToGUI()->push(repToGUI);
}
return true;
}
else
{
return false;
}
}
QByteArray ChirpChatDemod::serialize() const
{
return m_settings.serialize();
}
bool ChirpChatDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureChirpChatDemod *msg = MsgConfigureChirpChatDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureChirpChatDemod *msg = MsgConfigureChirpChatDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void ChirpChatDemod::applySettings(const ChirpChatDemodSettings& settings, bool force)
{
qDebug() << "ChirpChatDemod::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spreadFactor: " << settings.m_spreadFactor
<< " m_deBits: " << settings.m_deBits
<< " m_codingScheme: " << settings.m_codingScheme
<< " m_hasHeader: " << settings.m_hasHeader
<< " m_hasCRC: " << settings.m_hasCRC
<< " m_nbParityBits: " << settings.m_nbParityBits
<< " m_packetLength: " << settings.m_packetLength
<< " m_autoNbSymbolsMax: " << settings.m_autoNbSymbolsMax
<< " m_sendViaUDP: " << settings.m_sendViaUDP
<< " m_udpAddress: " << settings.m_udpAddress
<< " m_udpPort: " << settings.m_udpPort
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((settings.m_bandwidthIndex != m_settings.m_bandwidthIndex) || force)
{
reverseAPIKeys.append("bandwidthIndex");
DSPSignalNotification *msg = new DSPSignalNotification(
ChirpChatDemodSettings::bandwidths[settings.m_bandwidthIndex],
0);
m_spectrumVis.getInputMessageQueue()->push(msg);
}
if ((settings.m_spreadFactor != m_settings.m_spreadFactor) || force) {
reverseAPIKeys.append("spreadFactor");
}
if ((settings.m_deBits != m_settings.m_deBits) || force) {
reverseAPIKeys.append("deBits");
}
if ((settings.m_fftWindow != m_settings.m_fftWindow) || force) {
reverseAPIKeys.append("fftWindow");
}
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|| (settings.m_deBits != m_settings.m_deBits) || force) {
m_decoder.setNbSymbolBits(settings.m_spreadFactor, settings.m_deBits);
}
if ((settings.m_codingScheme != m_settings.m_codingScheme) || force)
{
reverseAPIKeys.append("codingScheme");
m_decoder.setCodingScheme(settings.m_codingScheme);
}
if ((settings.m_hasHeader != m_settings.m_hasHeader) || force)
{
reverseAPIKeys.append("hasHeader");
m_decoder.setLoRaHasHeader(settings.m_hasHeader);
}
if ((settings.m_hasCRC != m_settings.m_hasCRC) || force)
{
reverseAPIKeys.append("hasCRC");
m_decoder.setLoRaHasCRC(settings.m_hasCRC);
}
if ((settings.m_nbParityBits != m_settings.m_nbParityBits) || force)
{
reverseAPIKeys.append("nbParityBits");
m_decoder.setLoRaParityBits(settings.m_nbParityBits);
}
if ((settings.m_packetLength != m_settings.m_packetLength) || force)
{
reverseAPIKeys.append("packetLength");
m_decoder.setLoRaPacketLength(settings.m_packetLength);
}
if ((settings.m_decodeActive != m_settings.m_decodeActive) || force) {
reverseAPIKeys.append("decodeActive");
}
if ((settings.m_eomSquelchTenths != m_settings.m_eomSquelchTenths) || force) {
reverseAPIKeys.append("eomSquelchTenths");
}
if ((settings.m_nbSymbolsMax != m_settings.m_nbSymbolsMax) || force) {
reverseAPIKeys.append("nbSymbolsMax");
}
if ((settings.m_preambleChirps != m_settings.m_preambleChirps) || force) {
reverseAPIKeys.append("preambleChirps");
}
if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) {
reverseAPIKeys.append("rgbColor");
}
if ((settings.m_title != m_settings.m_title) || force) {
reverseAPIKeys.append("title");
}
if ((settings.m_sendViaUDP != m_settings.m_sendViaUDP) || force) {
reverseAPIKeys.append("sendViaUDP");
}
if ((settings.m_autoNbSymbolsMax != m_settings.m_autoNbSymbolsMax) || force) {
reverseAPIKeys.append("autoNbSymbolsMax");
}
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force)
{
reverseAPIKeys.append("udpAddress");
m_udpSink.setAddress(settings.m_udpAddress);
}
if ((settings.m_udpPort != m_settings.m_udpPort) || force)
{
reverseAPIKeys.append("udpPort");
m_udpSink.setPort(settings.m_udpPort);
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
ChirpChatDemodBaseband::MsgConfigureChirpChatDemodBaseband *msg =
ChirpChatDemodBaseband::MsgConfigureChirpChatDemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
if (m_featuresSettingsFeedback.size() > 0) {
featuresSendSettings(reverseAPIKeys, settings, force);
}
m_settings = settings;
}
int ChirpChatDemod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setChirpChatDemodSettings(new SWGSDRangel::SWGChirpChatDemodSettings());
response.getChirpChatDemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int ChirpChatDemod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
ChirpChatDemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureChirpChatDemod *msg = MsgConfigureChirpChatDemod::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureChirpChatDemod *msgToGUI = MsgConfigureChirpChatDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void ChirpChatDemod::webapiUpdateChannelSettings(
ChirpChatDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getChirpChatDemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("bandwidthIndex")) {
settings.m_bandwidthIndex = response.getChirpChatDemodSettings()->getBandwidthIndex();
}
if (channelSettingsKeys.contains("spreadFactor")) {
settings.m_spreadFactor = response.getChirpChatDemodSettings()->getSpreadFactor();
}
if (channelSettingsKeys.contains("deBits")) {
settings.m_deBits = response.getChirpChatDemodSettings()->getDeBits();
}
if (channelSettingsKeys.contains("fftWindow")) {
settings.m_fftWindow = (FFTWindow::Function) response.getChirpChatDemodSettings()->getFftWindow();
}
if (channelSettingsKeys.contains("codingScheme")) {
settings.m_codingScheme = (ChirpChatDemodSettings::CodingScheme) response.getChirpChatDemodSettings()->getCodingScheme();
}
if (channelSettingsKeys.contains("decodeActive")) {
settings.m_decodeActive = response.getChirpChatDemodSettings()->getDecodeActive() != 0;
}
if (channelSettingsKeys.contains("eomSquelchTenths")) {
settings.m_eomSquelchTenths = response.getChirpChatDemodSettings()->getEomSquelchTenths();
}
if (channelSettingsKeys.contains("nbSymbolsMax")) {
settings.m_nbSymbolsMax = response.getChirpChatDemodSettings()->getNbSymbolsMax();
}
if (channelSettingsKeys.contains("autoNbSymbolsMax")) {
settings.m_autoNbSymbolsMax = response.getChirpChatDemodSettings()->getAutoNbSymbolsMax() != 0;
}
if (channelSettingsKeys.contains("preambleChirps")) {
settings.m_preambleChirps = response.getChirpChatDemodSettings()->getPreambleChirps();
}
if (channelSettingsKeys.contains("nbParityBits")) {
settings.m_nbParityBits = response.getChirpChatDemodSettings()->getNbParityBits();
}
if (channelSettingsKeys.contains("packetLength")) {
settings.m_packetLength = response.getChirpChatDemodSettings()->getPacketLength();
}
if (channelSettingsKeys.contains("hasCRC")) {
settings.m_hasCRC = response.getChirpChatDemodSettings()->getHasCrc() != 0;
}
if (channelSettingsKeys.contains("hasHeader")) {
settings.m_hasHeader = response.getChirpChatDemodSettings()->getHasHeader() != 0;
}
if (channelSettingsKeys.contains("sendViaUDP")) {
settings.m_sendViaUDP = response.getChirpChatDemodSettings()->getSendViaUdp() != 0;
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getChirpChatDemodSettings()->getUdpAddress();
}
if (channelSettingsKeys.contains("udpPort"))
{
uint16_t port = response.getChirpChatDemodSettings()->getUdpPort();
settings.m_udpPort = port < 1024 ? 1024 : port > 65535 ? 65535 : port;
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getChirpChatDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getChirpChatDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getChirpChatDemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getChirpChatDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getChirpChatDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getChirpChatDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getChirpChatDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getChirpChatDemodSettings()->getReverseApiChannelIndex();
}
}
int ChirpChatDemod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setChirpChatDemodReport(new SWGSDRangel::SWGChirpChatDemodReport());
response.getChirpChatDemodReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void ChirpChatDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const ChirpChatDemodSettings& settings)
{
response.getChirpChatDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getChirpChatDemodSettings()->setBandwidthIndex(settings.m_bandwidthIndex);
response.getChirpChatDemodSettings()->setSpreadFactor(settings.m_spreadFactor);
response.getChirpChatDemodSettings()->setDeBits(settings.m_deBits);
response.getChirpChatDemodSettings()->setFftWindow((int) settings.m_fftWindow);
response.getChirpChatDemodSettings()->setCodingScheme((int) settings.m_codingScheme);
response.getChirpChatDemodSettings()->setDecodeActive(settings.m_decodeActive ? 1 : 0);
response.getChirpChatDemodSettings()->setEomSquelchTenths(settings.m_eomSquelchTenths);
response.getChirpChatDemodSettings()->setNbSymbolsMax(settings.m_nbSymbolsMax);
response.getChirpChatDemodSettings()->setAutoNbSymbolsMax(settings.m_autoNbSymbolsMax ? 1 : 0);
response.getChirpChatDemodSettings()->setPreambleChirps(settings.m_preambleChirps);
response.getChirpChatDemodSettings()->setNbParityBits(settings.m_nbParityBits);
response.getChirpChatDemodSettings()->setHasCrc(settings.m_hasCRC ? 1 : 0);
response.getChirpChatDemodSettings()->setHasHeader(settings.m_hasHeader ? 1 : 0);
response.getChirpChatDemodSettings()->setSendViaUdp(settings.m_sendViaUDP ? 1 : 0);
if (response.getChirpChatDemodSettings()->getUdpAddress()) {
*response.getChirpChatDemodSettings()->getUdpAddress() = settings.m_udpAddress;
} else {
response.getChirpChatDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
}
response.getChirpChatDemodSettings()->setUdpPort(settings.m_udpPort);
response.getChirpChatDemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getChirpChatDemodSettings()->getTitle()) {
*response.getChirpChatDemodSettings()->getTitle() = settings.m_title;
} else {
response.getChirpChatDemodSettings()->setTitle(new QString(settings.m_title));
}
response.getChirpChatDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getChirpChatDemodSettings()->getReverseApiAddress()) {
*response.getChirpChatDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getChirpChatDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getChirpChatDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getChirpChatDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getChirpChatDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
}
void ChirpChatDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
response.getChirpChatDemodReport()->setChannelPowerDb(CalcDb::dbPower(getTotalPower()));
response.getChirpChatDemodReport()->setSignalPowerDb(m_lastMsgSignalDb);
response.getChirpChatDemodReport()->setNoisePowerDb(CalcDb::dbPower(getCurrentNoiseLevel()));
response.getChirpChatDemodReport()->setSnrPowerDb(m_lastMsgSignalDb - m_lastMsgNoiseDb);
response.getChirpChatDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());
response.getChirpChatDemodReport()->setHasCrc(m_lastMsgHasCRC);
response.getChirpChatDemodReport()->setNbParityBits(m_lastMsgNbParityBits);
response.getChirpChatDemodReport()->setPacketLength(m_lastMsgPacketLength);
response.getChirpChatDemodReport()->setNbSymbols(m_lastMsgNbSymbols);
response.getChirpChatDemodReport()->setNbCodewords(m_lastMsgNbCodewords);
response.getChirpChatDemodReport()->setHeaderParityStatus(m_lastMsgHeaderParityStatus);
response.getChirpChatDemodReport()->setHeaderCrcStatus(m_lastMsgHeaderCRC);
response.getChirpChatDemodReport()->setPayloadParityStatus(m_lastMsgPayloadParityStatus);
response.getChirpChatDemodReport()->setPayloadCrcStatus(m_lastMsgPayloadCRC);
response.getChirpChatDemodReport()->setMessageTimestamp(new QString(m_lastMsgTimestamp));
response.getChirpChatDemodReport()->setMessageString(new QString(m_lastMsgString));
response.getChirpChatDemodReport()->setMessageBytes(new QList<QString *>);
QList<QString *> *bytesStr = response.getChirpChatDemodReport()->getMessageBytes();
for (QByteArray::const_iterator it = m_lastMsgBytes.begin(); it != m_lastMsgBytes.end(); ++it)
{
unsigned char b = *it;
bytesStr->push_back(new QString(tr("%1").arg(b, 2, 16, QChar('0'))));
}
}
void ChirpChatDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const ChirpChatDemodSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgChannelSettings;
}
void ChirpChatDemod::featuresSendSettings(QList<QString>& channelSettingsKeys, const ChirpChatDemodSettings& settings, bool force)
{
QList<Feature*>::iterator it = m_featuresSettingsFeedback.begin();
MainCore *mainCore = MainCore::instance();
for (; it != m_featuresSettingsFeedback.end(); ++it)
{
if (mainCore->existsFeature(*it))
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
Feature::MsgChannelSettings *msg = Feature::MsgChannelSettings::create(
this,
channelSettingsKeys,
swgChannelSettings,
force
);
(*it)->getInputMessageQueue()->push(msg);
}
else
{
m_featuresSettingsFeedback.removeOne(*it);
}
}
}
void ChirpChatDemod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const ChirpChatDemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString(m_channelId));
swgChannelSettings->setChirpChatDemodSettings(new SWGSDRangel::SWGChirpChatDemodSettings());
SWGSDRangel::SWGChirpChatDemodSettings *swgChirpChatDemodSettings = swgChannelSettings->getChirpChatDemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgChirpChatDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("bandwidthIndex") || force) {
swgChirpChatDemodSettings->setBandwidthIndex(settings.m_bandwidthIndex);
}
if (channelSettingsKeys.contains("spreadFactor") || force) {
swgChirpChatDemodSettings->setSpreadFactor(settings.m_spreadFactor);
}
if (channelSettingsKeys.contains("deBits") || force) {
swgChirpChatDemodSettings->setDeBits(settings.m_deBits);
}
if (channelSettingsKeys.contains("fftWindow") || force) {
swgChirpChatDemodSettings->setFftWindow((int) settings.m_fftWindow);
}
if (channelSettingsKeys.contains("codingScheme") || force) {
swgChirpChatDemodSettings->setCodingScheme((int) settings.m_codingScheme);
}
if (channelSettingsKeys.contains("decodeActive") || force) {
swgChirpChatDemodSettings->setDecodeActive(settings.m_decodeActive ? 1 : 0);
}
if (channelSettingsKeys.contains("eomSquelchTenths") || force) {
swgChirpChatDemodSettings->setEomSquelchTenths(settings.m_eomSquelchTenths);
}
if (channelSettingsKeys.contains("nbSymbolsMax") || force) {
swgChirpChatDemodSettings->setNbSymbolsMax(settings.m_nbSymbolsMax);
}
if (channelSettingsKeys.contains("autoNbSymbolsMax") || force) {
swgChirpChatDemodSettings->setAutoNbSymbolsMax(settings.m_nbSymbolsMax ? 1 : 0);
}
if (channelSettingsKeys.contains("preambleChirps") || force) {
swgChirpChatDemodSettings->setPreambleChirps(settings.m_preambleChirps);
}
if (channelSettingsKeys.contains("nbParityBits") || force) {
swgChirpChatDemodSettings->setNbParityBits(settings.m_nbParityBits);
}
if (channelSettingsKeys.contains("hasCRC") || force) {
swgChirpChatDemodSettings->setHasCrc(settings.m_hasCRC ? 1 : 0);
}
if (channelSettingsKeys.contains("hasHeader") || force) {
swgChirpChatDemodSettings->setHasHeader(settings.m_hasHeader ? 1 : 0);
}
if (channelSettingsKeys.contains("sendViaUDP") || force) {
swgChirpChatDemodSettings->setSendViaUdp(settings.m_sendViaUDP ? 1 : 0);
}
if (channelSettingsKeys.contains("udpAddress") || force) {
swgChirpChatDemodSettings->setUdpAddress(new QString(settings.m_udpAddress));
}
if (channelSettingsKeys.contains("updPort") || force) {
swgChirpChatDemodSettings->setUdpPort(settings.m_udpPort);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgChirpChatDemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgChirpChatDemodSettings->setTitle(new QString(settings.m_title));
}
}
void ChirpChatDemod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "ChirpChatDemod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("ChirpChatDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
bool ChirpChatDemod::getDemodActive() const
{
return m_basebandSink->getDemodActive();
}
double ChirpChatDemod::getCurrentNoiseLevel() const
{
return m_basebandSink->getCurrentNoiseLevel();
}
double ChirpChatDemod::getTotalPower() const
{
return m_basebandSink->getTotalPower();
}

View File

@ -0,0 +1,305 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// (C) 2015 John Greb //
// (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMOD_H
#define INCLUDE_CHIRPCHATDEMOD_H
#include <vector>
#include <QNetworkRequest>
#include "dsp/basebandsamplesink.h"
#include "dsp/spectrumvis.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "util/udpsinkutil.h"
#include "chirpchatdemodbaseband.h"
#include "chirpchatdemoddecoder.h"
class QNetworkAccessManager;
class QNetworkReply;
class DeviceAPI;
class QThread;
class ChirpChatDemod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureChirpChatDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const ChirpChatDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureChirpChatDemod* create(const ChirpChatDemodSettings& settings, bool force)
{
return new MsgConfigureChirpChatDemod(settings, force);
}
private:
ChirpChatDemodSettings m_settings;
bool m_force;
MsgConfigureChirpChatDemod(const ChirpChatDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgReportDecodeBytes : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QByteArray& getBytes() const { return m_bytes; }
unsigned int getSyncWord() const { return m_syncWord; }
float getSingalDb() const { return m_signalDb; }
float getNoiseDb() const { return m_noiseDb; }
unsigned int getPacketSize() const { return m_packetSize; }
unsigned int getNbParityBits() const { return m_nbParityBits; }
unsigned int getNbSymbols() const { return m_nbSymbols; }
unsigned int getNbCodewords() const { return m_nbCodewords; }
bool getHasCRC() const { return m_hasCRC; }
bool getEarlyEOM() const { return m_earlyEOM; }
int getHeaderParityStatus() const { return m_headerParityStatus; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
int getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
static MsgReportDecodeBytes* create(const QByteArray& bytes) {
return new MsgReportDecodeBytes(bytes);
}
void setSyncWord(unsigned int syncWord) {
m_syncWord = syncWord;
}
void setSignalDb(float db) {
m_signalDb = db;
}
void setNoiseDb(float db) {
m_noiseDb = db;
}
void setPacketSize(unsigned int packetSize) {
m_packetSize = packetSize;
}
void setNbParityBits(unsigned int nbParityBits) {
m_nbParityBits = nbParityBits;
}
void setNbSymbols(unsigned int nbSymbols) {
m_nbSymbols = nbSymbols;
}
void setNbCodewords(unsigned int nbCodewords) {
m_nbCodewords = nbCodewords;
}
void setHasCRC(bool hasCRC) {
m_hasCRC = hasCRC;
}
void setEarlyEOM(bool earlyEOM) {
m_earlyEOM = earlyEOM;
}
void setHeaderParityStatus(int headerParityStatus) {
m_headerParityStatus = headerParityStatus;
}
void setHeaderCRCStatus(bool headerCRCStatus) {
m_headerCRCStatus = headerCRCStatus;
}
void setPayloadParityStatus(int payloadParityStatus) {
m_payloadParityStatus = payloadParityStatus;
}
void setPayloadCRCStatus(bool payloadCRCStatus) {
m_payloadCRCStatus = payloadCRCStatus;
}
private:
QByteArray m_bytes;
unsigned int m_syncWord;
float m_signalDb;
float m_noiseDb;
unsigned int m_packetSize;
unsigned int m_nbParityBits;
unsigned int m_nbSymbols;
unsigned int m_nbCodewords;
bool m_hasCRC;
bool m_earlyEOM;
int m_headerParityStatus;
bool m_headerCRCStatus;
int m_payloadParityStatus;
bool m_payloadCRCStatus;
MsgReportDecodeBytes(const QByteArray& bytes) :
Message(),
m_bytes(bytes),
m_syncWord(0),
m_signalDb(0.0),
m_noiseDb(0.0),
m_packetSize(0),
m_nbParityBits(0),
m_nbSymbols(0),
m_nbCodewords(0),
m_hasCRC(false),
m_earlyEOM(false),
m_headerParityStatus(false),
m_headerCRCStatus(false),
m_payloadParityStatus(false),
m_payloadCRCStatus(false)
{ }
};
class MsgReportDecodeString : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QString& getString() const { return m_str; }
unsigned int getSyncWord() const { return m_syncWord; }
float getSingalDb() const { return m_signalDb; }
float getNoiseDb() const { return m_noiseDb; }
static MsgReportDecodeString* create(const QString& str)
{
return new MsgReportDecodeString(str);
}
void setSyncWord(unsigned int syncWord) {
m_syncWord = syncWord;
}
void setSignalDb(float db) {
m_signalDb = db;
}
void setNoiseDb(float db) {
m_noiseDb = db;
}
private:
QString m_str;
unsigned int m_syncWord;
float m_signalDb;
float m_noiseDb;
MsgReportDecodeString(const QString& str) :
Message(),
m_str(str),
m_syncWord(0),
m_signalDb(0.0),
m_noiseDb(0.0)
{ }
};
ChirpChatDemod(DeviceAPI* deviceAPI);
virtual ~ChirpChatDemod();
virtual void destroy() { delete this; }
SpectrumVis *getSpectrumVis() { return &m_spectrumVis; }
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = m_channelId; }
virtual const QString& getURI() const { return m_channelIdURI; }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return 0; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return 0;
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const ChirpChatDemodSettings& settings);
static void webapiUpdateChannelSettings(
ChirpChatDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
bool getDemodActive() const;
double getCurrentNoiseLevel() const;
double getTotalPower() const;
uint32_t getNumberOfDeviceStreams() const;
static const QString m_channelIdURI;
static const QString m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread *m_thread;
ChirpChatDemodBaseband* m_basebandSink;
ChirpChatDemodDecoder m_decoder;
ChirpChatDemodSettings m_settings;
SpectrumVis m_spectrumVis;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
float m_lastMsgSignalDb;
float m_lastMsgNoiseDb;
int m_lastMsgSyncWord;
int m_lastMsgPacketLength;
int m_lastMsgNbParityBits;
bool m_lastMsgHasCRC;
int m_lastMsgNbSymbols;
int m_lastMsgNbCodewords;
bool m_lastMsgEarlyEOM;
bool m_lastMsgHeaderCRC;
int m_lastMsgHeaderParityStatus;
bool m_lastMsgPayloadCRC;
int m_lastMsgPayloadParityStatus;
QString m_lastMsgTimestamp;
QString m_lastMsgString;
QByteArray m_lastMsgBytes;
UDPSinkUtil<uint8_t> m_udpSink;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void applySettings(const ChirpChatDemodSettings& settings, bool force = false);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const ChirpChatDemodSettings& settings, bool force);
void featuresSendSettings(QList<QString>& channelSettingsKeys, const ChirpChatDemodSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const ChirpChatDemodSettings& settings,
bool force
);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_CHIRPCHATDEMOD_H

View File

@ -21,45 +21,45 @@
#include "dsp/dspcommands.h"
#include "dsp/downchannelizer.h"
#include "lorademodbaseband.h"
#include "chirpchatdemodbaseband.h"
MESSAGE_CLASS_DEFINITION(LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband, Message)
MESSAGE_CLASS_DEFINITION(ChirpChatDemodBaseband::MsgConfigureChirpChatDemodBaseband, Message)
LoRaDemodBaseband::LoRaDemodBaseband() :
ChirpChatDemodBaseband::ChirpChatDemodBaseband() :
m_mutex(QMutex::Recursive)
{
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
qDebug("LoRaDemodBaseband::LoRaDemodBaseband");
qDebug("ChirpChatDemodBaseband::ChirpChatDemodBaseband");
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&LoRaDemodBaseband::handleData,
&ChirpChatDemodBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
LoRaDemodBaseband::~LoRaDemodBaseband()
ChirpChatDemodBaseband::~ChirpChatDemodBaseband()
{
delete m_channelizer;
}
void LoRaDemodBaseband::reset()
void ChirpChatDemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_sampleFifo.reset();
}
void LoRaDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
void ChirpChatDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void LoRaDemodBaseband::handleData()
void ChirpChatDemodBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
@ -86,7 +86,7 @@ void LoRaDemodBaseband::handleData()
}
}
void LoRaDemodBaseband::handleInputMessages()
void ChirpChatDemodBaseband::handleInputMessages()
{
Message* message;
@ -98,13 +98,13 @@ void LoRaDemodBaseband::handleInputMessages()
}
}
bool LoRaDemodBaseband::handleMessage(const Message& cmd)
bool ChirpChatDemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureLoRaDemodBaseband::match(cmd))
if (MsgConfigureChirpChatDemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureLoRaDemodBaseband& cfg = (MsgConfigureLoRaDemodBaseband&) cmd;
qDebug() << "LoRaDemodBaseband::handleMessage: MsgConfigureLoRaDemodBaseband";
MsgConfigureChirpChatDemodBaseband& cfg = (MsgConfigureChirpChatDemodBaseband&) cmd;
qDebug() << "ChirpChatDemodBaseband::handleMessage: MsgConfigureChirpChatDemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
@ -114,12 +114,12 @@ bool LoRaDemodBaseband::handleMessage(const Message& cmd)
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "LoRaDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
qDebug() << "ChirpChatDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
m_channelizer->setBasebandSampleRate(notif.getSampleRate());
m_sink.applyChannelSettings(
m_channelizer->getChannelSampleRate(),
LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex],
ChirpChatDemodSettings::bandwidths[m_settings.m_bandwidthIndex],
m_channelizer->getChannelFrequencyOffset()
);
@ -131,18 +131,18 @@ bool LoRaDemodBaseband::handleMessage(const Message& cmd)
}
}
void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool force)
void ChirpChatDemodBaseband::applySettings(const ChirpChatDemodSettings& settings, bool force)
{
if ((settings.m_bandwidthIndex != m_settings.m_bandwidthIndex)
|| (settings.m_centerFrequency != m_settings.m_centerFrequency) || force)
|| (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex],
settings.m_centerFrequency
ChirpChatDemodSettings::bandwidths[settings.m_bandwidthIndex]*ChirpChatDemodSettings::oversampling,
settings.m_inputFrequencyOffset
);
m_sink.applyChannelSettings(
m_channelizer->getChannelSampleRate(),
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex],
ChirpChatDemodSettings::bandwidths[settings.m_bandwidthIndex],
m_channelizer->getChannelFrequencyOffset()
);
}
@ -152,18 +152,18 @@ void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool fo
m_settings = settings;
}
int LoRaDemodBaseband::getChannelSampleRate() const
int ChirpChatDemodBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}
void LoRaDemodBaseband::setBasebandSampleRate(int sampleRate)
void ChirpChatDemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(
m_channelizer->getChannelSampleRate(),
LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex],
ChirpChatDemodSettings::bandwidths[m_settings.m_bandwidthIndex],
m_channelizer->getChannelFrequencyOffset()
);
}

View File

@ -15,8 +15,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODBASEBAND_H
#define INCLUDE_LORADEMODBASEBAND_H
#ifndef INCLUDE_CHIRPCHATDEMODBASEBAND_H
#define INCLUDE_CHIRPCHATDEMODBASEBAND_H
#include <QObject>
#include <QMutex>
@ -25,60 +25,64 @@
#include "util/message.h"
#include "util/messagequeue.h"
#include "lorademodsink.h"
#include "chirpchatdemodsink.h"
class DownChannelizer;
class LoRaDemodBaseband : public QObject
class ChirpChatDemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureLoRaDemodBaseband : public Message {
class MsgConfigureChirpChatDemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const LoRaDemodSettings& getSettings() const { return m_settings; }
const ChirpChatDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureLoRaDemodBaseband* create(const LoRaDemodSettings& settings, bool force)
static MsgConfigureChirpChatDemodBaseband* create(const ChirpChatDemodSettings& settings, bool force)
{
return new MsgConfigureLoRaDemodBaseband(settings, force);
return new MsgConfigureChirpChatDemodBaseband(settings, force);
}
private:
LoRaDemodSettings m_settings;
ChirpChatDemodSettings m_settings;
bool m_force;
MsgConfigureLoRaDemodBaseband(const LoRaDemodSettings& settings, bool force) :
MsgConfigureChirpChatDemodBaseband(const ChirpChatDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
LoRaDemodBaseband();
~LoRaDemodBaseband();
ChirpChatDemodBaseband();
~ChirpChatDemodBaseband();
void reset();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
int getChannelSampleRate() const;
bool getDemodActive() const { return m_sink.getDemodActive(); }
double getCurrentNoiseLevel() const { return m_sink.getCurrentNoiseLevel(); }
double getTotalPower() const { return m_sink.getTotalPower(); }
void setBasebandSampleRate(int sampleRate);
void setDecoderMessageQueue(MessageQueue *messageQueue) { m_sink.setDecoderMessageQueue(messageQueue); }
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_sink.setSpectrumSink(spectrumSink); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
LoRaDemodSink m_sink;
ChirpChatDemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
LoRaDemodSettings m_settings;
ChirpChatDemodSettings m_settings;
QMutex m_mutex;
bool handleMessage(const Message& cmd);
void applySettings(const LoRaDemodSettings& settings, bool force = false);
void applySettings(const ChirpChatDemodSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_LORADEMODBASEBAND_H
#endif // INCLUDE_CHIRPCHATDEMODBASEBAND_H

View File

@ -0,0 +1,99 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "chirpchatdemoddecoder.h"
#include "chirpchatdemoddecodertty.h"
#include "chirpchatdemoddecoderascii.h"
#include "chirpchatdemoddecoderlora.h"
ChirpChatDemodDecoder::ChirpChatDemodDecoder() :
m_codingScheme(ChirpChatDemodSettings::CodingTTY),
m_nbSymbolBits(5),
m_nbParityBits(1),
m_hasCRC(true),
m_hasHeader(true)
{}
ChirpChatDemodDecoder::~ChirpChatDemodDecoder()
{}
void ChirpChatDemodDecoder::setNbSymbolBits(unsigned int spreadFactor, unsigned int deBits)
{
m_spreadFactor = spreadFactor;
if (deBits >= spreadFactor) {
m_deBits = m_spreadFactor - 1;
} else {
m_deBits = deBits;
}
m_nbSymbolBits = m_spreadFactor - m_deBits;
}
void ChirpChatDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
switch(m_codingScheme)
{
case ChirpChatDemodSettings::CodingTTY:
if (m_nbSymbolBits == 5) {
ChirpChatDemodDecoderTTY::decodeSymbols(symbols, str);
}
break;
case ChirpChatDemodSettings::CodingASCII:
if (m_nbSymbolBits == 5) {
ChirpChatDemodDecoderASCII::decodeSymbols(symbols, str);
}
break;
default:
break;
}
}
void ChirpChatDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QByteArray& bytes)
{
switch(m_codingScheme)
{
case ChirpChatDemodSettings::CodingLoRa:
if (m_nbSymbolBits >= 5)
{
ChirpChatDemodDecoderLoRa::decodeBytes(
bytes,
symbols,
m_nbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
ChirpChatDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
}
break;
}
}

View File

@ -0,0 +1,68 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODDECODER_H
#define INCLUDE_CHIRPCHATDEMODDECODER_H
#include <vector>
#include "chirpchatdemodsettings.h"
class ChirpChatDemodDecoder
{
public:
ChirpChatDemodDecoder();
~ChirpChatDemodDecoder();
void setCodingScheme(ChirpChatDemodSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; }
void setNbSymbolBits(unsigned int spreadFactor, unsigned int deBits);
void setLoRaParityBits(unsigned int parityBits) { m_nbParityBits = parityBits; }
void setLoRaHasHeader(bool hasHeader) { m_hasHeader = hasHeader; }
void setLoRaHasCRC(bool hasCRC) { m_hasCRC = hasCRC; }
void setLoRaPacketLength(unsigned int packetLength) { m_packetLength = packetLength; }
void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str); //!< For ASCII and TTY
void decodeSymbols(const std::vector<unsigned short>& symbols, QByteArray& bytes); //!< For raw bytes (original LoRa)
unsigned int getNbParityBits() const { return m_nbParityBits; }
unsigned int getPacketLength() const { return m_packetLength; }
bool getHasCRC() const { return m_hasCRC; }
unsigned int getNbSymbols() const { return m_nbSymbols; }
unsigned int getNbCodewords() const { return m_nbCodewords; }
bool getEarlyEOM() const { return m_earlyEOM; }
int getHeaderParityStatus() const { return m_headerParityStatus; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
int getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
private:
ChirpChatDemodSettings::CodingScheme m_codingScheme;
unsigned int m_spreadFactor;
unsigned int m_deBits;
unsigned int m_nbSymbolBits;
// LoRa attributes
unsigned int m_nbParityBits; //!< 1 to 4 Hamming FEC bits for 4 payload bits
bool m_hasCRC;
bool m_hasHeader;
unsigned int m_packetLength;
unsigned int m_nbSymbols; //!< Number of encoded symbols: this is only dependent of nbSymbolBits, nbParityBits, packetLength, hasHeader and hasCRC
unsigned int m_nbCodewords; //!< Number of encoded codewords: this is only dependent of nbSymbolBits, nbParityBits, packetLength, hasHeader and hasCRC
bool m_earlyEOM;
int m_headerParityStatus;
bool m_headerCRCStatus;
int m_payloadParityStatus;
bool m_payloadCRCStatus;
};
#endif // INCLUDE_CHIRPCHATDEMODDECODER_H

View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "chirpchatdemoddecoderascii.h"
void ChirpChatDemodDecoderASCII::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
std::vector<unsigned short>::const_iterator it = symbols.begin();
QByteArray bytes;
for (; it != symbols.end(); ++it) {
bytes.push_back(*it & 0x7F);
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODDECODERASCII_H
#define INCLUDE_CHIRPCHATDEMODDECODERASCII_H
#include <vector>
#include <QString>
class ChirpChatDemodDecoderASCII
{
public:
static void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str);
};
#endif // INCLUDE_CHIRPCHATDEMODDECODERASCII_H

View File

@ -0,0 +1,367 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Inspired by: https://github.com/myriadrf/LoRa-SDR //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "chirpchatdemoddecoderlora.h"
void ChirpChatDemodDecoderLoRa::decodeHeader(
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
int& headerParityStatus,
bool& headerCRCStatus
)
{
// with header (H: header 8-bit codeword P: payload-8 bit codeword):
// nbSymbolBits = 5 |H|H|H|H|H| codewords => 8 symbols (always) : static headerSymbols = 8
// nbSymbolBits = 7 |H|H|H|H|H|P|P|
// without header (P: payload 8-bit codeword):
// nbSymbolBits = 5 |P|P|P|P|P| codewords => 8 symbols (always)
// nbSymbolBits = 7 |P|P|P|P|P|P|P|
// Actual header is always represented with 5 8-bit codewords : static headerCodewords = 5
// These 8-bit codewords are encoded with Hamming(4,8) FEC : static headerParityBits = 4
const unsigned int numSymbols = roundUp(inSymbols.size(), 4 + nbParityBits);
const unsigned int numCodewords = (numSymbols / (4 + nbParityBits))*nbSymbolBits;
std::vector<uint16_t> symbols(headerSymbols);
std::copy(inSymbols.begin(), inSymbols.begin() + headerSymbols, symbols.begin());
//gray encode
for (auto &sym : symbols) {
sym = binaryToGray16(sym);
}
std::vector<uint8_t> codewords(nbSymbolBits);
// Header symbols de-interleave thus headerSymbols (8) symbols into nbSymbolBits (5..12) codewords using header FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), headerSymbols, codewords.data(), nbSymbolBits, headerParityBits);
// whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, nbSymbolBits - headerCodewords, 0, headerParityBits);
bool error = false;
bool bad = false;
uint8_t bytes[3];
// decode actual header inside 8-bit codewords header with 4/8 FEC (5 first codewords)
bytes[0] = decodeHamming84sx(codewords[1], error, bad) & 0xf;
bytes[0] |= decodeHamming84sx(codewords[0], error, bad) << 4; // length
bytes[1] = decodeHamming84sx(codewords[2], error, bad) & 0xf; // coding rate and crc enable
bytes[2] = decodeHamming84sx(codewords[4], error, bad) & 0xf;
bytes[2] |= decodeHamming84sx(codewords[3], error, bad) << 4; // checksum
bytes[2] ^= headerChecksum(bytes);
if (bad)
{
headerParityStatus = (int) ParityError;
}
else
{
if (error) {
headerParityStatus = (int) ParityCorrected;
} else {
headerParityStatus = (int) ParityOK;
}
if (bytes[2] != 0) {
headerCRCStatus = false;
} else {
headerCRCStatus = true;
}
}
hasCRC = (bytes[1] & 1) != 0;
nbParityBits = (bytes[1] >> 1) & 0x7;
packetLength = bytes[0];
}
void ChirpChatDemodDecoderLoRa::decodeBytes(
QByteArray& inBytes,
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool& earlyEOM,
int& headerParityStatus,
bool& headerCRCStatus,
int& payloadParityStatus,
bool& payloadCRCStatus
)
{
// need at least a header (8 symbols of 8 bit codewords) whether an actual header is sent or not
if (inSymbols.size() < headerSymbols)
{
qDebug("ChirpChatDemodDecoderLoRa::decodeBytes: need at least %u symbols for header", headerSymbols);
earlyEOM = true;
return;
}
else
{
earlyEOM = false;
}
if (hasHeader)
{
decodeHeader(
inSymbols,
nbSymbolBits,
hasCRC,
nbParityBits,
packetLength,
headerParityStatus,
headerCRCStatus
);
}
qDebug("ChirpChatDemodDecoderLoRa::decodeBytes: crc: %s nbParityBits: %u packetLength: %u",
hasCRC ? "on": "off", nbParityBits, packetLength);
if (nbParityBits > 4)
{
qDebug("ChirpChatDemodDecoderLoRa::decodeBytes: invalid parity bits in header: %u", nbParityBits);
return;
}
const unsigned int numSymbols = roundUp(inSymbols.size(), 4 + nbParityBits);
const unsigned int numCodewords = (numSymbols / (4 + nbParityBits))*nbSymbolBits;
std::vector<uint16_t> symbols(numSymbols);
std::copy(inSymbols.begin(), inSymbols.end(), symbols.begin());
//gray encode, when SF > PPM, depad the LSBs with rounding
for (auto &sym : symbols) {
sym = binaryToGray16(sym);
}
std::vector<uint8_t> codewords(numCodewords);
// deinterleave / dewhiten the symbols into codewords
unsigned int sOfs = 0;
unsigned int cOfs = 0;
// the first headerSymbols (8 symbols) are coded with 4/8 FEC (thus 8 bit codewords) whether an actual header is present or not
// this corresponds to nbSymbolBits codewords (therefore LoRa imposes nbSymbolBits >= headerCodewords (5 codewords) this is controlled externally)
if (nbParityBits != 4) // different FEC between header symbols and the rest of the packet
{
// Header symbols de-interleave thus headerSymbols (8) symbols into nbSymbolBits (5..12) codewords using header FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), headerSymbols, codewords.data(), nbSymbolBits, headerParityBits);
if (hasHeader) { // whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, nbSymbolBits - headerCodewords, 0, headerParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), nbSymbolBits, 0, headerParityBits);
}
cOfs += nbSymbolBits; // nbSymbolBits codewords in header
sOfs += headerSymbols; // headerSymbols symbols in header
if (numSymbols - sOfs > 0) // remaining payload symbols after header symbols using their own FEC (4/5..4/7)
{
diagonalDeinterleaveSx(symbols.data() + sOfs, numSymbols - sOfs, codewords.data() + cOfs, nbSymbolBits, nbParityBits);
if (hasHeader) {
Sx1272ComputeWhiteningLfsr(codewords.data() + cOfs, numCodewords - cOfs, nbSymbolBits - headerCodewords, nbParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data() + cOfs, numCodewords - cOfs, nbSymbolBits, nbParityBits);
}
}
}
else // uniform 4/8 FEC for all the packet
{
// De-interleave the whole packet thus numSymbols into nbSymbolBits (5..12) codewords using packet FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), numSymbols, codewords.data(), nbSymbolBits, nbParityBits);
if (hasHeader) { // whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, numCodewords - headerCodewords, 0, nbParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), numCodewords, 0, nbParityBits);
}
}
// Now we have nbSymbolBits 8-bit codewords (4/8 FEC) possibly containing the actual header followed by the rest of payload codewords with their own FEC (4/5..4/8)
std::vector<uint8_t> bytes((codewords.size()+1) / 2);
unsigned int dOfs = 0;
cOfs = 0;
unsigned int dataLength = packetLength + 3 + (hasCRC ? 2 : 0); // include header and CRC
if (hasHeader)
{
cOfs = headerCodewords;
dOfs = 6;
}
else
{
cOfs = 0;
dOfs = 0;
}
if (dataLength > bytes.size())
{
qDebug("ChirpChatDemodDecoderLoRa::decodeBytes: not enough data %lu vs %u", bytes.size(), dataLength);
earlyEOM = true;
return;
}
// decode the rest of the payload inside 8-bit codewords header with 4/8 FEC
bool error = false;
bool bad = false;
for (; cOfs < nbSymbolBits; cOfs++, dOfs++)
{
if (dOfs % 2 == 1) {
bytes[dOfs/2] |= decodeHamming84sx(codewords[cOfs], error, bad) << 4;
} else {
bytes[dOfs/2] = decodeHamming84sx(codewords[cOfs], error, bad) & 0xf;
}
}
if (dOfs % 2 == 1) // decode the start of the payload codewords with their own FEC when not on an even boundary
{
if (nbParityBits == 1) {
bytes[dOfs/2] |= checkParity54(codewords[cOfs++], error) << 4;
} else if (nbParityBits == 2) {
bytes[dOfs/2] |= checkParity64(codewords[cOfs++], error) << 4;
} else if (nbParityBits == 3){
bytes[dOfs/2] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
} else if (nbParityBits == 4){
bytes[dOfs/2] |= decodeHamming84sx(codewords[cOfs++], error, bad) << 4;
} else {
bytes[dOfs/2] |= codewords[cOfs++] << 4;
}
dOfs++;
}
dOfs /= 2;
// decode the rest of the payload codewords with their own FEC
if (nbParityBits == 1)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = checkParity54(codewords[cOfs++],error);
bytes[i] |= checkParity54(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBits == 2)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = checkParity64(codewords[cOfs++], error);
bytes[i] |= checkParity64(codewords[cOfs++],error) << 4;
}
}
else if (nbParityBits == 3)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = decodeHamming74sx(codewords[cOfs++], error) & 0xf;
bytes[i] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBits == 4)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = decodeHamming84sx(codewords[cOfs++], error, bad) & 0xf;
bytes[i] |= decodeHamming84sx(codewords[cOfs++], error, bad) << 4;
}
}
else
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = codewords[cOfs++] & 0xf;
bytes[i] |= codewords[cOfs++] << 4;
}
}
if (bad) {
payloadParityStatus = (int) ParityError;
} else if (error) {
payloadParityStatus = (int) ParityCorrected;
} else {
payloadParityStatus = (int) ParityOK;
}
// finalization:
// adjust offsets dpending on header and CRC presence
// compute and verify payload CRC if present
if (hasHeader)
{
dOfs = 3; // skip header
dataLength -= 3; // remove header
if (hasCRC) // always compute crc if present skipping the header
{
uint16_t crc = sx1272DataChecksum(bytes.data() + dOfs, packetLength);
uint16_t packetCRC = bytes[dOfs + packetLength] | (bytes[dOfs + packetLength + 1] << 8);
if (crc != packetCRC) {
payloadCRCStatus = false;
} else {
payloadCRCStatus = true;
}
}
}
else
{
dOfs = 0; // no header to skip
if (hasCRC)
{
uint16_t crc = sx1272DataChecksum(bytes.data(), packetLength);
uint16_t packetCRC = bytes[packetLength] | (bytes[packetLength + 1] << 8);
if (crc != packetCRC) {
payloadCRCStatus = false;
} else {
payloadCRCStatus = true;
}
}
}
inBytes.resize(dataLength);
std::copy(bytes.data() + dOfs, bytes.data() + dOfs + dataLength, inBytes.data());
}
void ChirpChatDemodDecoderLoRa::getCodingMetrics(
unsigned int nbSymbolBits,
unsigned int nbParityBits,
unsigned int packetLength,
bool hasHeader,
bool hasCRC,
unsigned int& numSymbols,
unsigned int& numCodewords
)
{
numCodewords = roundUp((packetLength + (hasCRC ? 2 : 0))*2 + (hasHeader ? headerCodewords : 0), nbSymbolBits); // uses payload + CRC for encoding size
numSymbols = headerSymbols + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits); // header is always coded with 8 bits and yields exactly 8 symbols (headerSymbols)
}

View File

@ -0,0 +1,357 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Inspired by: https://github.com/myriadrf/LoRa-SDR //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODDECODERLORA_H
#define INCLUDE_CHIRPCHATDEMODDECODERLORA_H
#include <vector>
#include <QByteArray>
class ChirpChatDemodDecoderLoRa
{
public:
enum ParityStatus
{
ParityUndefined,
ParityError,
ParityCorrected,
ParityOK
};
static void decodeBytes(
QByteArray& bytes,
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool& earlyEOM,
int& headerParityStatus,
bool& headerCRCStatus,
int& payloadParityStatus,
bool& payloadCRCStatus
);
static void getCodingMetrics(
unsigned int nbSymbolBits,
unsigned int nbParityBits,
unsigned int packetLength,
bool hasHeader,
bool hasCRC,
unsigned int& numSymbols,
unsigned int& numCodewords
);
private:
static void decodeHeader(
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
int& headerParityStatus,
bool& headerCRCStatus
);
static const unsigned int headerParityBits = 4;
static const unsigned int headerSymbols = 8;
static const unsigned int headerCodewords = 5;
/***********************************************************************
* Round functions
**********************************************************************/
static inline unsigned roundUp(unsigned num, unsigned factor)
{
return ((num + factor - 1) / factor) * factor;
}
/***********************************************************************
* https://en.wikipedia.org/wiki/Gray_code
**********************************************************************/
/*
* This function converts an unsigned binary
* number to reflected binary Gray code.
*
* The operator >> is shift right. The operator ^ is exclusive or.
*/
static inline unsigned short binaryToGray16(unsigned short num)
{
return num ^ (num >> 1);
}
/***********************************************************************
* Diagonal deinterleaver
**********************************************************************/
static inline void diagonalDeinterleaveSx(
const uint16_t *symbols,
const unsigned int numSymbols,
uint8_t *codewords,
const unsigned int nbSymbolBits,
const unsigned int nbParityBits)
{
for (unsigned int x = 0; x < numSymbols / (4 + nbParityBits); x++)
{
const unsigned int cwOff = x*nbSymbolBits;
const unsigned int symOff = x*(4 + nbParityBits);
for (unsigned int k = 0; k < 4 + nbParityBits; k++)
{
for (unsigned int m = 0; m < nbSymbolBits; m++)
{
const unsigned int i = (m + k) % nbSymbolBits;
const auto bit = (symbols[symOff + k] >> m) & 0x1;
codewords[cwOff + i] |= (bit << k);
}
}
}
}
/***********************************************************************
* Whitening generator reverse engineered from Sx1272 data stream.
* Same as above but using the actual interleaved LFSRs.
**********************************************************************/
static inline void Sx1272ComputeWhiteningLfsr(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const unsigned int nbParityBits)
{
static const uint64_t seed1[2] = {0x6572D100E85C2EFF,0xE85C2EFFFFFFFFFF}; // lfsr start values
static const uint64_t seed2[2] = {0x05121100F8ECFEEF,0xF8ECFEEFEFEFEFEF}; // lfsr start values for single parity mode (1 == nbParityBits)
const uint8_t m = 0xff >> (4 - nbParityBits);
uint64_t r[2] = {(1 == nbParityBits)?seed2[0]:seed1[0],(1 == nbParityBits)?seed2[1]:seed1[1]};
int i,j;
for (i = 0; i < bitOfs;i++)
{
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56); // poly: 0x1D
}
for (j = 0; j < bufferSize; j++,i++)
{
buffer[j] ^= r[i & 1] & m;
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56);
}
}
/***********************************************************************
* Decode 8 bits into a 4 bit word with single bit correction.
* Non standard version used in sx1272.
* Set error to true when a parity error was detected
* Set bad to true when the result could not be corrected
**********************************************************************/
static inline unsigned char decodeHamming84sx(const unsigned char b, bool &error, bool &bad)
{
auto b0 = (b >> 0) & 0x1;
auto b1 = (b >> 1) & 0x1;
auto b2 = (b >> 2) & 0x1;
auto b3 = (b >> 3) & 0x1;
auto b4 = (b >> 4) & 0x1;
auto b5 = (b >> 5) & 0x1;
auto b6 = (b >> 6) & 0x1;
auto b7 = (b >> 7) & 0x1;
auto p0 = (b0 ^ b1 ^ b2 ^ b4);
auto p1 = (b1 ^ b2 ^ b3 ^ b5);
auto p2 = (b0 ^ b1 ^ b3 ^ b6);
auto p3 = (b0 ^ b2 ^ b3 ^ b7);
auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2) | (p3 << 3);
if (parity != 0) error = true;
switch (parity & 0xf)
{
case 0xD: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0xB: return (b ^ 4) & 0xf;
case 0xE: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4:
case 0x8: return b & 0xf;
default: bad = true; return b & 0xf;
}
}
/***********************************************************************
* Simple 8-bit checksum routine
**********************************************************************/
static inline uint8_t checksum8(const uint8_t *p, const size_t len)
{
uint8_t acc = 0;
for (size_t i = 0; i < len; i++)
{
acc = (acc >> 1) + ((acc & 0x1) << 7); //rotate
acc += p[i]; //add
}
return acc;
}
static inline uint8_t headerChecksum(const uint8_t *h)
{
auto a0 = (h[0] >> 4) & 0x1;
auto a1 = (h[0] >> 5) & 0x1;
auto a2 = (h[0] >> 6) & 0x1;
auto a3 = (h[0] >> 7) & 0x1;
auto b0 = (h[0] >> 0) & 0x1;
auto b1 = (h[0] >> 1) & 0x1;
auto b2 = (h[0] >> 2) & 0x1;
auto b3 = (h[0] >> 3) & 0x1;
auto c0 = (h[1] >> 0) & 0x1;
auto c1 = (h[1] >> 1) & 0x1;
auto c2 = (h[1] >> 2) & 0x1;
auto c3 = (h[1] >> 3) & 0x1;
uint8_t res;
res = (a0 ^ a1 ^ a2 ^ a3) << 4;
res |= (a3 ^ b1 ^ b2 ^ b3 ^ c0) << 3;
res |= (a2 ^ b0 ^ b3 ^ c1 ^ c3) << 2;
res |= (a1 ^ b0 ^ b2 ^ c0 ^ c1 ^ c2) << 1;
res |= a0 ^ b1 ^ c0 ^ c1 ^ c2 ^ c3;
return res;
}
/***********************************************************************
* Check parity for 5/4 code.
* return true if parity is valid.
**********************************************************************/
static inline unsigned char checkParity54(const unsigned char b, bool &error)
{
auto x = b ^ (b >> 2);
x = x ^ (x >> 1) ^ (b >> 4);
if (x & 1) {
error = true;
}
return b & 0xf;
}
/***********************************************************************
* Check parity for 6/4 code.
* return true if parity is valid.
**********************************************************************/
static inline unsigned char checkParity64(const unsigned char b, bool &error)
{
auto x = b ^ (b >> 1) ^ (b >> 2);
auto y = x ^ b ^ (b >> 3);
x ^= b >> 4;
y ^= b >> 5;
if ((x | y) & 1) {
error = true;
}
return b & 0xf;
}
/***********************************************************************
* Decode 7 bits into a 4 bit word with single bit correction.
* Non standard version used in sx1272.
* Set error to true when a parity error was detected
* Non correctable errors are indistinguishable from single or no errors
* therefore no 'bad' variable is proposed
**********************************************************************/
static inline unsigned char decodeHamming74sx(const unsigned char b, bool &error)
{
auto b0 = (b >> 0) & 0x1;
auto b1 = (b >> 1) & 0x1;
auto b2 = (b >> 2) & 0x1;
auto b3 = (b >> 3) & 0x1;
auto b4 = (b >> 4) & 0x1;
auto b5 = (b >> 5) & 0x1;
auto b6 = (b >> 6) & 0x1;
auto p0 = (b0 ^ b1 ^ b2 ^ b4);
auto p1 = (b1 ^ b2 ^ b3 ^ b5);
auto p2 = (b0 ^ b1 ^ b3 ^ b6);
auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2);
if (parity != 0) {
error = true;
}
switch (parity)
{
case 0x5: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0x3: return (b ^ 4) & 0xf;
case 0x6: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4: break;
}
return b & 0xf;
}
/***********************************************************************
* CRC reverse engineered from Sx1272 data stream.
* Modified CCITT crc with masking of the output with an 8bit lfsr
**********************************************************************/
static inline uint16_t crc16sx(uint16_t crc, const uint16_t poly)
{
for (int i = 0; i < 8; i++)
{
if (crc & 0x8000) {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
}
return crc;
}
static inline uint8_t xsum8(uint8_t t)
{
t ^= t >> 4;
t ^= t >> 2;
t ^= t >> 1;
return (t & 1);
}
static inline uint16_t sx1272DataChecksum(const uint8_t *data, int length)
{
uint16_t res = 0;
uint8_t v = 0xff;
uint16_t crc = 0;
for (int i = 0; i < length; i++)
{
crc = crc16sx(res, 0x1021);
v = xsum8(v & 0xB8) | (v << 1);
res = crc ^ data[i];
}
res ^= v;
v = xsum8(v & 0xB8) | (v << 1);
res ^= v << 8;
return res;
}
};
#endif // INCLUDE_CHIRPCHATDEMODDECODERLORA_H

View File

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "chirpchatdemoddecodertty.h"
const char ChirpChatDemodDecoderTTY::ttyLetters[32] = {
'_', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
'\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
};
const char ChirpChatDemodDecoderTTY::ttyFigures[32] = { // U.S. standard
'_', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', ' ', '.', '/', ';', ' '
};
void ChirpChatDemodDecoderTTY::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
std::vector<unsigned short>::const_iterator it = symbols.begin();
QByteArray bytes;
TTYState ttyState = TTYLetters;
for (; it != symbols.end(); ++it)
{
char ttyChar = *it & 0x1F;
if (ttyChar == lettersTag) {
ttyState = TTYLetters;
} else if (ttyChar == figuresTag) {
ttyState = TTYFigures;
}
else
{
char asciiChar = -1;
if (ttyState == TTYLetters) {
asciiChar = ttyLetters[ttyChar];
} else if (ttyState == TTYFigures) {
asciiChar = ttyFigures[ttyChar];
}
if (asciiChar >= 0) {
bytes.push_back(asciiChar);
}
}
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -0,0 +1,42 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODDECODERTTY_H
#define INCLUDE_CHIRPCHATDEMODDECODERTTY_H
#include <vector>
#include <QString>
class ChirpChatDemodDecoderTTY
{
public:
static void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str);
private:
enum TTYState
{
TTYLetters,
TTYFigures
};
static const char ttyLetters[32];
static const char ttyFigures[32];
static const char lettersTag = 0x1f;
static const char figuresTag = 0x1b;
};
#endif // INCLUDE_CHIRPCHATDEMODDECODERTTY_H

View File

@ -0,0 +1,761 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "device/deviceuiset.h"
#include <QScrollBar>
#include <QDebug>
#include <QDateTime>
#include "ui_chirpchatdemodgui.h"
#include "dsp/spectrumvis.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "gui/glspectrum.h"
#include "gui/glspectrumgui.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "maincore.h"
#include "chirpchatdemod.h"
#include "chirpchatdemodgui.h"
ChirpChatDemodGUI* ChirpChatDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
ChirpChatDemodGUI* gui = new ChirpChatDemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void ChirpChatDemodGUI::destroy()
{
delete this;
}
void ChirpChatDemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray ChirpChatDemodGUI::serialize() const
{
return m_settings.serialize();
}
bool ChirpChatDemodGUI::deserialize(const QByteArray& data)
{
resetLoRaStatus();
if (m_settings.deserialize(data))
{
displaySettings();
applySettings(true);
return true;
}
else
{
resetToDefaults();
return false;
}
}
bool ChirpChatDemodGUI::handleMessage(const Message& message)
{
if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (DSPSignalNotification&) message;
int basebandSampleRate = notif.getSampleRate();
qDebug() << "ChirpChatDemodGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << basebandSampleRate;
if (basebandSampleRate != m_basebandSampleRate)
{
m_basebandSampleRate = basebandSampleRate;
setBandwidths();
}
return true;
}
else if (ChirpChatDemod::MsgReportDecodeBytes::match(message))
{
if (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingLoRa) {
showLoRaMessage(message);
}
return true;
}
else if (ChirpChatDemod::MsgReportDecodeString::match(message))
{
if ((m_settings.m_codingScheme == ChirpChatDemodSettings::CodingASCII)
|| (m_settings.m_codingScheme == ChirpChatDemodSettings::CodingTTY)) {
showTextMessage(message);
}
return true;
}
else if (ChirpChatDemod::MsgConfigureChirpChatDemod::match(message))
{
qDebug("ChirpChatDemodGUI::handleMessage: NFMDemod::MsgConfigureChirpChatDemod");
const ChirpChatDemod::MsgConfigureChirpChatDemod& cfg = (ChirpChatDemod::MsgConfigureChirpChatDemod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else
{
return false;
}
}
void ChirpChatDemodGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message)) {
delete message;
}
}
}
void ChirpChatDemodGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void ChirpChatDemodGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void ChirpChatDemodGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void ChirpChatDemodGUI::on_BW_valueChanged(int value)
{
if (value < 0) {
m_settings.m_bandwidthIndex = 0;
} else if (value < ChirpChatDemodSettings::nbBandwidths) {
m_settings.m_bandwidthIndex = value;
} else {
m_settings.m_bandwidthIndex = ChirpChatDemodSettings::nbBandwidths - 1;
}
int thisBW = ChirpChatDemodSettings::bandwidths[value];
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
m_channelMarker.setBandwidth(thisBW);
ui->glSpectrum->setSampleRate(thisBW);
ui->glSpectrum->setCenterFrequency(thisBW/2);
applySettings();
}
void ChirpChatDemodGUI::on_Spread_valueChanged(int value)
{
m_settings.m_spreadFactor = value;
ui->SpreadText->setText(tr("%1").arg(value));
ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
applySettings();
}
void ChirpChatDemodGUI::on_deBits_valueChanged(int value)
{
m_settings.m_deBits = value;
ui->deBitsText->setText(tr("%1").arg(m_settings.m_deBits));
applySettings();
}
void ChirpChatDemodGUI::on_fftWindow_currentIndexChanged(int index)
{
m_settings.m_fftWindow = (FFTWindow::Function) index;
applySettings();
}
void ChirpChatDemodGUI::on_preambleChirps_valueChanged(int value)
{
m_settings.m_preambleChirps = value;
ui->preambleChirpsText->setText(tr("%1").arg(m_settings.m_preambleChirps));
applySettings();
}
void ChirpChatDemodGUI::on_scheme_currentIndexChanged(int index)
{
m_settings.m_codingScheme = (ChirpChatDemodSettings::CodingScheme) index;
if (m_settings.m_codingScheme != ChirpChatDemodSettings::CodingLoRa) {
resetLoRaStatus();
}
applySettings();
}
void ChirpChatDemodGUI::on_mute_toggled(bool checked)
{
m_settings.m_decodeActive = !checked;
applySettings();
}
void ChirpChatDemodGUI::on_clear_clicked(bool checked)
{
(void) checked;
ui->messageText->clear();
ui->messageText->clear();
}
void ChirpChatDemodGUI::on_eomSquelch_valueChanged(int value)
{
m_settings.m_eomSquelchTenths = value;
displaySquelch();
applySettings();
}
void ChirpChatDemodGUI::on_messageLength_valueChanged(int value)
{
m_settings.m_nbSymbolsMax = value;
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
applySettings();
}
void ChirpChatDemodGUI::on_messageLengthAuto_stateChanged(int state)
{
m_settings.m_autoNbSymbolsMax = (state == Qt::Checked);
applySettings();
}
void ChirpChatDemodGUI::on_header_stateChanged(int state)
{
m_settings.m_hasHeader = (state == Qt::Checked);
if (!m_settings.m_hasHeader) // put back values from settings
{
ui->fecParity->blockSignals(true);
ui->crc->blockSignals(true);
ui->fecParity->setValue(m_settings.m_nbParityBits);
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
ui->crc->setChecked(m_settings.m_hasCRC);
ui->fecParity->blockSignals(false);
ui->crc->blockSignals(false);
}
ui->fecParity->setEnabled(state != Qt::Checked);
ui->crc->setEnabled(state != Qt::Checked);
applySettings();
}
void ChirpChatDemodGUI::on_fecParity_valueChanged(int value)
{
m_settings.m_nbParityBits = value;
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
applySettings();
}
void ChirpChatDemodGUI::on_crc_stateChanged(int state)
{
m_settings.m_hasCRC = (state == Qt::Checked);
applySettings();
}
void ChirpChatDemodGUI::on_packetLength_valueChanged(int value)
{
m_settings.m_packetLength = value;
ui->packetLengthText->setText(tr("%1").arg(m_settings.m_packetLength));
applySettings();
}
void ChirpChatDemodGUI::on_udpSend_stateChanged(int state)
{
m_settings.m_sendViaUDP = (state == Qt::Checked);
applySettings();
}
void ChirpChatDemodGUI::on_udpAddress_editingFinished()
{
m_settings.m_udpAddress = ui->udpAddress->text();
applySettings();
}
void ChirpChatDemodGUI::on_udpPort_editingFinished()
{
bool ok;
quint16 udpPort = ui->udpPort->text().toInt(&ok);
if((!ok) || (udpPort < 1024)) {
udpPort = 9998;
}
ui->udpPort->setText(tr("%1").arg(m_settings.m_udpPort));
m_settings.m_udpPort = udpPort;
applySettings();
}
void ChirpChatDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
void ChirpChatDemodGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.move(p);
dialog.exec();
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine))
{
DeviceStreamSelectionDialog dialog(this);
dialog.setNumberOfStreams(m_chirpChatDemod->getNumberOfDeviceStreams());
dialog.setStreamIndex(m_settings.m_streamIndex);
dialog.move(p);
dialog.exec();
m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
m_channelMarker.clearStreamIndexes();
m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
displayStreamIndex();
applySettings();
}
resetContextMenuType();
}
ChirpChatDemodGUI::ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::ChirpChatDemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_basebandSampleRate(250000),
m_doApplySettings(true),
m_tickCount(0)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_chirpChatDemod = (ChirpChatDemod*) rxChannel;
m_spectrumVis = m_chirpChatDemod->getSpectrumVis();
m_spectrumVis->setGLSpectrum(ui->glSpectrum);
m_chirpChatDemod->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
ui->glSpectrum->setDisplayWaterfall(true);
ui->glSpectrum->setDisplayMaxHold(true);
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->messageText->setReadOnly(true);
ui->messageText->setReadOnly(true);
m_channelMarker.setMovable(true);
m_channelMarker.setVisible(true);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
m_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum);
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setSpectrumGUI(ui->spectrumGUI);
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
setBandwidths();
displaySettings();
resetLoRaStatus();
applySettings(true);
}
ChirpChatDemodGUI::~ChirpChatDemodGUI()
{
delete ui;
}
void ChirpChatDemodGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void ChirpChatDemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
setTitleColor(m_channelMarker.getColor());
ChirpChatDemod::MsgConfigureChirpChatDemod* message = ChirpChatDemod::MsgConfigureChirpChatDemod::create( m_settings, force);
m_chirpChatDemod->getInputMessageQueue()->push(message);
}
}
void ChirpChatDemodGUI::displaySettings()
{
int thisBW = ChirpChatDemodSettings::bandwidths[m_settings.m_bandwidthIndex];
m_channelMarker.blockSignals(true);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setBandwidth(thisBW);
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor);
setTitleColor(m_settings.m_rgbColor);
ui->glSpectrum->setSampleRate(thisBW);
ui->glSpectrum->setCenterFrequency(thisBW/2);
ui->fecParity->setEnabled(!m_settings.m_hasHeader);
ui->crc->setEnabled(!m_settings.m_hasHeader);
ui->packetLength->setEnabled(!m_settings.m_hasHeader);
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
ui->BW->setValue(m_settings.m_bandwidthIndex);
ui->Spread->setValue(m_settings.m_spreadFactor);
ui->SpreadText->setText(tr("%1").arg(m_settings.m_spreadFactor));
ui->deBits->setValue(m_settings.m_deBits);
ui->fftWindow->setCurrentIndex((int) m_settings.m_fftWindow);
ui->deBitsText->setText(tr("%1").arg(m_settings.m_deBits));
ui->preambleChirps->setValue(m_settings.m_preambleChirps);
ui->preambleChirpsText->setText(tr("%1").arg(m_settings.m_preambleChirps));
ui->scheme->setCurrentIndex((int) m_settings.m_codingScheme);
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
ui->messageLength->setValue(m_settings.m_nbSymbolsMax);
ui->udpSend->setChecked(m_settings.m_sendViaUDP);
ui->udpAddress->setText(m_settings.m_udpAddress);
ui->udpPort->setText(tr("%1").arg(m_settings.m_udpPort));
ui->header->setChecked(m_settings.m_hasHeader);
if (!m_settings.m_hasHeader)
{
ui->fecParity->setValue(m_settings.m_nbParityBits);
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
ui->crc->setChecked(m_settings.m_hasCRC);
ui->packetLength->setValue(m_settings.m_packetLength);
ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
}
ui->messageLengthAuto->setChecked(m_settings.m_autoNbSymbolsMax);
displaySquelch();
blockApplySettings(false);
}
void ChirpChatDemodGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
} else {
setStreamIndicator("S"); // single channel indicator
}
}
void ChirpChatDemodGUI::displaySquelch()
{
ui->eomSquelch->setValue(m_settings.m_eomSquelchTenths);
if (m_settings.m_eomSquelchTenths == ui->eomSquelch->maximum()) {
ui->eomSquelchText->setText("---");
} else {
ui->eomSquelchText->setText(tr("%1").arg(m_settings.m_eomSquelchTenths / 10.0, 0, 'f', 1));
}
}
void ChirpChatDemodGUI::displayLoRaStatus(int headerParityStatus, bool headerCRCStatus, int payloadParityStatus, bool payloadCRCStatus)
{
if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityOK)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityError)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : red; }");
} else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityCorrected)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : blue; }");
} else {
ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (m_settings.m_hasHeader && headerCRCStatus) {
ui->headerCRCStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (m_settings.m_hasHeader && !headerCRCStatus) {
ui->headerCRCStatus->setStyleSheet("QLabel { background-color : red; }");
} else {
ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (payloadParityStatus == (int) ParityOK) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (payloadParityStatus == (int) ParityError) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : red; }");
} else if (payloadParityStatus == (int) 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::resetLoRaStatus()
{
ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->payloadFECStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->payloadCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->nbSymbolsText->setText("---");
ui->nbCodewordsText->setText("---");
}
void ChirpChatDemodGUI::setBandwidths()
{
int maxBandwidth = m_basebandSampleRate/ChirpChatDemodSettings::oversampling;
int maxIndex = 0;
for (; (maxIndex < ChirpChatDemodSettings::nbBandwidths) && (ChirpChatDemodSettings::bandwidths[maxIndex] <= maxBandwidth); maxIndex++)
{}
if (maxIndex != 0)
{
qDebug("ChirpChatDemodGUI::setBandwidths: avl: %d max: %d", maxBandwidth, ChirpChatDemodSettings::bandwidths[maxIndex-1]);
ui->BW->setMaximum(maxIndex - 1);
int index = ui->BW->value();
ui->BWText->setText(QString("%1 Hz").arg(ChirpChatDemodSettings::bandwidths[index]));
}
}
void ChirpChatDemodGUI::showLoRaMessage(const Message& message)
{
const ChirpChatDemod::MsgReportDecodeBytes& msg = (ChirpChatDemod::MsgReportDecodeBytes&) message;
QByteArray bytes = msg.getBytes();
QString syncWordStr((tr("%1").arg(msg.getSyncWord(), 2, 16, QChar('0'))));
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
unsigned int packetLength;
if (m_settings.m_hasHeader)
{
ui->fecParity->setValue(msg.getNbParityBits());
ui->fecParityText->setText(tr("%1").arg(msg.getNbParityBits()));
ui->crc->setChecked(msg.getHasCRC());
ui->packetLength->setValue(msg.getPacketSize());
ui->packetLengthText->setText(tr("%1").arg(msg.getPacketSize()));
packetLength = msg.getPacketSize();
}
else
{
packetLength = m_settings.m_packetLength;
}
QDateTime dt = QDateTime::currentDateTime();
QString dateStr = dt.toString("HH:mm:ss");
if (msg.getEarlyEOM())
{
QString loRaStatus = tr("%1 %2 S:%3 SN:%4 HF:%5 HC:%6 EOM:too early")
.arg(dateStr)
.arg(syncWordStr)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1)
.arg(getParityStr(msg.getHeaderParityStatus()))
.arg(msg.getHeaderCRCStatus() ? "ok" : "err");
displayStatus(loRaStatus);
displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), (int) ParityUndefined, true);
ui->payloadCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); // reset payload CRC
}
else
{
QString loRaHeader = tr("%1 %2 S:%3 SN:%4 HF:%5 HC:%6 FEC:%7 CRC:%8")
.arg(dateStr)
.arg(syncWordStr)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1)
.arg(getParityStr(msg.getHeaderParityStatus()))
.arg(msg.getHeaderCRCStatus() ? "ok" : "err")
.arg(getParityStr(msg.getPayloadParityStatus()))
.arg(msg.getPayloadCRCStatus() ? "ok" : "err");
displayStatus(loRaHeader);
displayBytes(bytes);
QByteArray bytesCopy(bytes);
bytesCopy.truncate(packetLength);
bytesCopy.replace('\0', " ");
QString str = QString(bytesCopy.toStdString().c_str());
QString textHeader(tr("%1 (%2)").arg(dateStr).arg(syncWordStr));
displayText(str);
displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), msg.getPayloadParityStatus(), msg.getPayloadCRCStatus());
}
ui->nbSymbolsText->setText(tr("%1").arg(msg.getNbSymbols()));
ui->nbCodewordsText->setText(tr("%1").arg(msg.getNbCodewords()));
}
void ChirpChatDemodGUI::showTextMessage(const Message& message)
{
const ChirpChatDemod::MsgReportDecodeString& msg = (ChirpChatDemod::MsgReportDecodeString&) 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")
.arg(dateStr)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1);
displayStatus(status);
displayText(msg.getString());
}
void ChirpChatDemodGUI::displayText(const QString& text)
{
QTextCursor cursor = ui->messageText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (!ui->messageText->document()->isEmpty()) {
cursor.insertText("\n");
}
cursor.insertText(tr("TXT|%1").arg(text));
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
}
void ChirpChatDemodGUI::displayBytes(const QByteArray& bytes)
{
QTextCursor cursor = ui->messageText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (!ui->messageText->document()->isEmpty()) {
cursor.insertText("\n");
}
QByteArray::const_iterator it = bytes.begin();
unsigned int i = 0;
for (;it != bytes.end(); ++it, i++)
{
unsigned char b = *it;
if (i%16 == 0) {
cursor.insertText(tr("%1|").arg(i, 3, 10, QChar('0')));
}
cursor.insertText(tr("%1").arg(b, 2, 16, QChar('0')));
if (i%16 == 15) {
cursor.insertText("\n");
} else if (i%4 == 3) {
cursor.insertText("|");
} else {
cursor.insertText(" ");
}
}
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
}
void ChirpChatDemodGUI::displayStatus(const QString& status)
{
QTextCursor cursor = ui->messageText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (!ui->messageText->document()->isEmpty()) {
cursor.insertText("\n");
}
cursor.insertText(tr(">%1").arg(status));
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
}
QString ChirpChatDemodGUI::getParityStr(int parityStatus)
{
if (parityStatus == (int) ParityError) {
return "err";
} else if (parityStatus == (int) ParityCorrected) {
return "fix";
} else if (parityStatus == (int) ParityOK) {
return "ok";
} else {
return "n/a";
}
}
void ChirpChatDemodGUI::tick()
{
if (m_tickCount < 10)
{
m_tickCount++;
}
else
{
m_tickCount = 0;
ui->nText->setText(tr("%1").arg(CalcDb::dbPower(m_chirpChatDemod->getCurrentNoiseLevel()), 0, 'f', 1));
ui->channelPower->setText(tr("%1 dB").arg(CalcDb::dbPower(m_chirpChatDemod->getTotalPower()), 0, 'f', 1));
if (m_chirpChatDemod->getDemodActive()) {
ui->mute->setStyleSheet("QToolButton { background-color : green; }");
} else {
ui->mute->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
}
}
}

View File

@ -0,0 +1,118 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODGUI_H
#define INCLUDE_CHIRPCHATDEMODGUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "util/messagequeue.h"
#include "chirpchatdemodsettings.h"
class PluginAPI;
class DeviceUISet;
class ChirpChatDemod;
class SpectrumVis;
class BasebandSampleSink;
namespace Ui {
class ChirpChatDemodGUI;
}
class ChirpChatDemodGUI : public ChannelGUI {
Q_OBJECT
public:
static ChirpChatDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceAPI, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
private slots:
void channelMarkerChangedByCursor();
void on_deltaFrequency_changed(qint64 value);
void on_BW_valueChanged(int value);
void on_Spread_valueChanged(int value);
void on_deBits_valueChanged(int value);
void on_fftWindow_currentIndexChanged(int index);
void on_preambleChirps_valueChanged(int value);
void on_scheme_currentIndexChanged(int index);
void on_mute_toggled(bool checked);
void on_clear_clicked(bool checked);
void on_eomSquelch_valueChanged(int value);
void on_messageLength_valueChanged(int value);
void on_messageLengthAuto_stateChanged(int state);
void on_header_stateChanged(int state);
void on_fecParity_valueChanged(int value);
void on_crc_stateChanged(int state);
void on_packetLength_valueChanged(int value);
void on_udpSend_stateChanged(int state);
void on_udpAddress_editingFinished();
void on_udpPort_editingFinished();
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void channelMarkerHighlightedByCursor();
void handleInputMessages();
void tick();
private:
enum ParityStatus // matches decoder status
{
ParityUndefined,
ParityError,
ParityCorrected,
ParityOK
};
Ui::ChirpChatDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
ChirpChatDemodSettings m_settings;
int m_basebandSampleRate;
bool m_doApplySettings;
ChirpChatDemod* m_chirpChatDemod;
SpectrumVis* m_spectrumVis;
MessageQueue m_inputMessageQueue;
unsigned int m_tickCount;
explicit ChirpChatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~ChirpChatDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayStreamIndex();
void displaySquelch();
void setBandwidths();
void showLoRaMessage(const Message& message);
void showTextMessage(const Message& message); //!< For TTY and ASCII
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);
QString getParityStr(int parityStatus);
void resetLoRaStatus();
bool handleMessage(const Message& message);
};
#endif // INCLUDE_CHIRPCHATDEMODGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "chirpchatdemodmsg.h"
MESSAGE_CLASS_DEFINITION(ChirpChatDemodMsg::MsgDecodeSymbols, Message)

View File

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODMSG_H
#define INCLUDE_CHIRPCHATDEMODMSG_H
#include <QObject>
#include "util/message.h"
namespace ChirpChatDemodMsg
{
class MsgDecodeSymbols : public Message {
MESSAGE_CLASS_DECLARATION
public:
const std::vector<unsigned short>& getSymbols() const { return m_symbols; }
unsigned int getSyncWord() const { return m_syncWord; }
float getSingalDb() const { return m_signalDb; }
float getNoiseDb() const { return m_noiseDb; }
void pushBackSymbol(unsigned short symbol) {
m_symbols.push_back(symbol);
}
void popSymbol() {
m_symbols.pop_back();
}
void setSyncWord(unsigned char syncWord) {
m_syncWord = syncWord;
}
void setSignalDb(float db) {
m_signalDb = db;
}
void setNoiseDb(float db) {
m_noiseDb = db;
}
static MsgDecodeSymbols* create() {
return new MsgDecodeSymbols();
}
static MsgDecodeSymbols* create(const std::vector<unsigned short> symbols) {
return new MsgDecodeSymbols(symbols);
}
private:
std::vector<unsigned short> m_symbols;
unsigned int m_syncWord;
float m_signalDb;
float m_noiseDb;
MsgDecodeSymbols() : //!< create an empty message
Message(),
m_syncWord(0),
m_signalDb(0.0),
m_noiseDb(0.0)
{}
MsgDecodeSymbols(const std::vector<unsigned short> symbols) : //!< create a message with symbols copy
Message(),
m_syncWord(0),
m_signalDb(0.0),
m_noiseDb(0.0)
{ m_symbols = symbols; }
};
}
#endif // INCLUDE_CHIRPCHATDEMODMSG_H

View File

@ -0,0 +1,226 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "chirpchatdemodsettings.h"
const int ChirpChatDemodSettings::bandwidths[] = {
325, // 384k / 1024
750, // 384k / 512
1500, // 384k / 256
2604, // 333k / 128
3125, // 400k / 128
3906, // 500k / 128
5208, // 333k / 64
6250, // 400k / 64
7813, // 500k / 64
10417, // 333k / 32
12500, // 400k / 32
15625, // 500k / 32
20833, // 333k / 16
25000, // 400k / 16
31250, // 500k / 16
41667, // 333k / 8
50000, // 400k / 8
62500, // 500k / 8
83333, // 333k / 4
100000, // 400k / 4
125000, // 500k / 4
166667, // 333k / 2
200000, // 400k / 2
250000, // 500k / 2
333333, // 333k / 1
400000, // 400k / 1
500000 // 500k / 1
};
const int ChirpChatDemodSettings::nbBandwidths = 3*8 + 3;
const int ChirpChatDemodSettings::oversampling = 2;
ChirpChatDemodSettings::ChirpChatDemodSettings() :
m_inputFrequencyOffset(0),
m_channelMarker(0),
m_spectrumGUI(0)
{
resetToDefaults();
}
void ChirpChatDemodSettings::resetToDefaults()
{
m_bandwidthIndex = 5;
m_spreadFactor = 7;
m_deBits = 0;
m_codingScheme = CodingLoRa;
m_decodeActive = true;
m_fftWindow = FFTWindow::Rectangle;
m_eomSquelchTenths = 60;
m_nbSymbolsMax = 255;
m_preambleChirps = 8;
m_packetLength = 32;
m_nbParityBits = 1;
m_hasCRC = true;
m_hasHeader = true;
m_sendViaUDP = false;
m_udpAddress = "127.0.0.1";
m_udpPort = 9999;
m_rgbColor = QColor(255, 0, 255).rgb();
m_title = "ChirpChat Demodulator";
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
}
QByteArray ChirpChatDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_bandwidthIndex);
s.writeS32(3, m_spreadFactor);
if (m_spectrumGUI) {
s.writeBlob(4, m_spectrumGUI->serialize());
}
if (m_channelMarker) {
s.writeBlob(5, m_channelMarker->serialize());
}
s.writeString(6, m_title);
s.writeS32(7, m_deBits);
s.writeS32(8, m_codingScheme);
s.writeBool(9, m_decodeActive);
s.writeS32(10, m_eomSquelchTenths);
s.writeS32(11, m_nbSymbolsMax);
s.writeS32(12, m_packetLength);
s.writeS32(13, m_nbParityBits);
s.writeBool(14, m_hasCRC);
s.writeBool(15, m_hasHeader);
s.writeS32(17, m_preambleChirps);
s.writeS32(18, (int) m_fftWindow);
s.writeBool(20, m_useReverseAPI);
s.writeString(21, m_reverseAPIAddress);
s.writeU32(22, m_reverseAPIPort);
s.writeU32(23, m_reverseAPIDeviceIndex);
s.writeU32(24, m_reverseAPIChannelIndex);
s.writeS32(25, m_streamIndex);
s.writeBool(26, m_sendViaUDP);
s.writeString(27, m_udpAddress);
s.writeU32(28, m_udpPort);
return s.final();
}
bool ChirpChatDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
int tmp;
unsigned int utmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_bandwidthIndex, 0);
d.readS32(3, &m_spreadFactor, 0);
if (m_spectrumGUI) {
d.readBlob(4, &bytetmp);
m_spectrumGUI->deserialize(bytetmp);
}
if (m_channelMarker) {
d.readBlob(5, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readString(6, &m_title, "ChirpChat Demodulator");
d.readS32(7, &m_deBits, 0);
d.readS32(8, &tmp);
m_codingScheme = (CodingScheme) tmp;
d.readBool(9, &m_decodeActive, true);
d.readS32(10, &m_eomSquelchTenths, 60);
d.readS32(11, &m_nbSymbolsMax, 255);
d.readS32(12, &m_packetLength, 32);
d.readS32(13, &m_nbParityBits, 1);
d.readBool(14, &m_hasCRC, true);
d.readBool(15, &m_hasHeader, true);
d.readS32(17, &m_preambleChirps, 8);
d.readS32(18, &tmp, (int) FFTWindow::Rectangle);
m_fftWindow = (FFTWindow::Function) tmp;
d.readBool(20, &m_useReverseAPI, false);
d.readString(21, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(22, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(23, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(24, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readS32(25, &m_streamIndex, 0);
d.readBool(26, &m_sendViaUDP, false);
d.readString(27, &m_udpAddress, "127.0.0.1");
d.readU32(28, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_udpPort = utmp;
} else {
m_udpPort = 9999;
}
return true;
}
else
{
resetToDefaults();
return false;
}
}
unsigned int ChirpChatDemodSettings::getNbSFDFourths() const
{
switch (m_codingScheme)
{
case CodingLoRa:
return 9;
default:
return 8;
}
}
bool ChirpChatDemodSettings::hasSyncWord() const
{
return m_codingScheme == CodingLoRa;
}

View File

@ -0,0 +1,86 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_CHANNELRX_DEMODCHIRPCHAT_CHIRPCHATDEMODSETTINGS_H_
#define PLUGINS_CHANNELRX_DEMODCHIRPCHAT_CHIRPCHATDEMODSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <stdint.h>
#include "dsp/fftwindow.h"
class Serializable;
struct ChirpChatDemodSettings
{
enum CodingScheme
{
CodingLoRa, //!< Standard LoRa
CodingASCII, //!< plain ASCII (7 bits)
CodingTTY //!< plain TTY (5 bits)
};
int m_inputFrequencyOffset;
int m_bandwidthIndex;
int m_spreadFactor;
int m_deBits; //!< Low data rate optmize (DE) bits
FFTWindow::Function m_fftWindow;
CodingScheme m_codingScheme;
bool m_decodeActive;
int m_eomSquelchTenths; //!< Squelch factor to trigger end of message (/10)
int m_nbSymbolsMax; //!< Maximum number of symbols in a payload
bool m_autoNbSymbolsMax; //!< Set maximum number of symbols in a payload automatically using last messag value
int m_preambleChirps; //!< Number of expected preamble chirps
int m_nbParityBits; //!< Hamming parity bits (LoRa)
int m_packetLength; //!< Payload packet length in bytes or characters (LoRa)
bool m_hasCRC; //!< Payload has CRC (LoRa)
bool m_hasHeader; //!< Header present before actual payload (LoRa)
bool m_sendViaUDP; //!< Send decoded message via UDP
QString m_udpAddress; //!< UDP address where to send message
uint16_t m_udpPort; //!< UDP port where to send message
uint32_t m_rgbColor;
QString m_title;
int m_streamIndex;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
Serializable *m_channelMarker;
Serializable *m_spectrumGUI;
static const int bandwidths[];
static const int nbBandwidths;
static const int oversampling;
ChirpChatDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setSpectrumGUI(Serializable *spectrumGUI) { m_spectrumGUI = spectrumGUI; }
unsigned int getNbSFDFourths() const; //!< Get the number of SFD period fourths (depends on coding scheme)
bool hasSyncWord() const; //!< Only LoRa has a syncword (for the moment)
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* PLUGINS_CHANNELRX_DEMODCHIRPCHAT_CHIRPCHATDEMODSETTINGS_H_ */

View File

@ -0,0 +1,644 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QTime>
#include <QDebug>
#include <stdio.h>
#include "dsp/dsptypes.h"
#include "dsp/basebandsamplesink.h"
#include "dsp/dspengine.h"
#include "dsp/fftfactory.h"
#include "dsp/fftengine.h"
#include "util/db.h"
#include "chirpchatdemodmsg.h"
#include "chirpchatdemodsink.h"
ChirpChatDemodSink::ChirpChatDemodSink() :
m_decodeMsg(nullptr),
m_decoderMsgQueue(nullptr),
m_spectrumSink(nullptr),
m_spectrumBuffer(nullptr),
m_downChirps(nullptr),
m_upChirps(nullptr),
m_spectrumLine(nullptr),
m_fftSequence(-1),
m_fftSFDSequence(-1)
{
m_demodActive = false;
m_bandwidth = ChirpChatDemodSettings::bandwidths[0];
m_channelSampleRate = 96000;
m_channelFrequencyOffset = 0;
m_nco.setFreq(m_channelFrequencyOffset, m_channelSampleRate);
m_interpolator.create(16, m_channelSampleRate, m_bandwidth / 1.9f);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_bandwidth;
m_sampleDistanceRemain = 0;
m_state = ChirpChatStateReset;
m_chirp = 0;
m_chirp0 = 0;
initSF(m_settings.m_spreadFactor, m_settings.m_deBits, m_settings.m_fftWindow);
}
ChirpChatDemodSink::~ChirpChatDemodSink()
{
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0)
{
fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSequence);
fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSFDSequence);
}
delete[] m_downChirps;
delete[] m_upChirps;
delete[] m_spectrumBuffer;
delete[] m_spectrumLine;
}
void ChirpChatDemodSink::initSF(unsigned int sf, unsigned int deBits, FFTWindow::Function fftWindow)
{
if (m_downChirps) {
delete[] m_downChirps;
}
if (m_upChirps) {
delete[] m_upChirps;
}
if (m_spectrumBuffer) {
delete[] m_spectrumBuffer;
}
if (m_spectrumLine) {
delete[] m_spectrumLine;
}
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0)
{
fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSequence);
fftFactory->releaseEngine(m_interpolatedFFTLength, false, m_fftSFDSequence);
}
m_nbSymbols = 1 << sf;
m_nbSymbolsEff = 1 << (sf - deBits);
m_deLength = 1 << deBits;
m_fftLength = m_nbSymbols;
m_fftWindow.create(fftWindow, m_fftLength);
m_fftWindow.setKaiserAlpha(M_PI);
m_interpolatedFFTLength = m_fftInterpolation*m_fftLength;
m_preambleTolerance = (m_deLength*m_fftInterpolation)/2;
m_fftSequence = fftFactory->getEngine(m_interpolatedFFTLength, false, &m_fft);
m_fftSFDSequence = fftFactory->getEngine(m_interpolatedFFTLength, false, &m_fftSFD);
m_state = ChirpChatStateReset;
m_sfdSkip = m_fftLength / 4;
m_downChirps = new Complex[2*m_nbSymbols]; // Each table is 2 chirps long to allow processing from arbitrary offsets.
m_upChirps = new Complex[2*m_nbSymbols];
m_spectrumBuffer = new Complex[m_nbSymbols];
m_spectrumLine = new Complex[m_nbSymbols];
std::fill(m_spectrumLine, m_spectrumLine+m_nbSymbols, Complex(std::polar(1e-6*SDR_RX_SCALED, 0.0)));
float halfAngle = M_PI;
float phase = -halfAngle;
double accumulator = 0;
for (int i = 0; i < m_fftLength; i++)
{
accumulator = fmod(accumulator + phase, 2*M_PI);
m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator)));
m_upChirps[i] = Complex(std::polar(1.0, accumulator));
phase += (2*halfAngle) / m_nbSymbols;
}
// Duplicate table to allow processing from arbitrary offsets
std::copy(m_downChirps, m_downChirps+m_fftLength, m_downChirps+m_fftLength);
std::copy(m_upChirps, m_upChirps+m_fftLength, m_upChirps+m_fftLength);
}
void ChirpChatDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
int newangle;
Complex ci;
for (SampleVector::const_iterator it = begin; it < end; ++it)
{
Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF);
c *= m_nco.nextIQ();
if (m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci))
{
processSample(ci);
m_sampleDistanceRemain += m_interpolatorDistance;
}
}
}
void ChirpChatDemodSink::processSample(const Complex& ci)
{
if (m_state == ChirpChatStateReset) // start over
{
m_demodActive = false;
reset();
std::queue<double>().swap(m_magsqQueue); // this clears the queue
m_state = ChirpChatStateDetectPreamble;
}
else if (m_state == ChirpChatStateDetectPreamble) // look for preamble
{
m_fft->in()[m_fftCounter++] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
if (m_fftCounter == m_fftLength)
{
m_fftWindow.apply(m_fft->in());
std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
m_fft->transform();
m_fftCounter = 0;
double magsq, magsqTotal;
unsigned int imax = argmax(
m_fft->out(),
m_fftInterpolation,
m_fftLength,
magsq,
magsqTotal,
m_spectrumBuffer,
m_fftInterpolation
) / m_fftInterpolation;
if (m_magsqQueue.size() > m_settings.m_preambleChirps) {
m_magsqQueue.pop();
}
m_magsqTotalAvg(magsqTotal);
m_magsqQueue.push(magsq);
m_argMaxHistory[m_argMaxHistoryCounter++] = imax;
if (m_argMaxHistoryCounter == m_requiredPreambleChirps)
{
m_argMaxHistoryCounter = 0;
bool preambleFound = true;
for (int i = 1; i < m_requiredPreambleChirps; i++)
{
int delta = m_argMaxHistory[i] - m_argMaxHistory[i-1];
// qDebug("ChirpChatDemodSink::processSample: search: delta: %d / %d", delta, m_deLength);
if ((delta < -m_preambleTolerance) || (delta > m_preambleTolerance))
{
preambleFound = false;
break;
}
// if (m_argMaxHistory[0] != m_argMaxHistory[i])
// {
// preambleFound = false;
// break;
// }
}
if ((preambleFound) && (magsq > 1e-9))
{
if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
}
qDebug("ChirpChatDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], magsq);
m_chirp = m_argMaxHistory[0];
m_fftCounter = m_chirp;
m_chirp0 = 0;
m_chirpCount = 0;
m_state = ChirpChatStatePreambleResyc;
}
else
{
m_magsqOffAvg(m_magsqQueue.front());
}
}
}
}
else if (m_state == ChirpChatStatePreambleResyc)
{
m_fftCounter++;
if (m_fftCounter == m_fftLength)
{
if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumLine, m_nbSymbols);
}
m_fftCounter = 0;
m_demodActive = true;
m_state = ChirpChatStatePreamble;
}
}
else if (m_state == ChirpChatStatePreamble) // preamble found look for SFD start
{
m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
m_fftSFD->in()[m_fftCounter] = ci * m_upChirps[m_chirp]; // de-chirp the down ramp
m_fftCounter++;
if (m_fftCounter == m_fftLength)
{
m_fftWindow.apply(m_fft->in());
std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
m_fft->transform();
m_fftWindow.apply(m_fftSFD->in());
std::fill(m_fftSFD->in()+m_fftLength, m_fftSFD->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
m_fftSFD->transform();
m_fftCounter = 0;
double magsqPre, magsqSFD;
double magsqTotal, magsqSFDTotal;
unsigned int imaxSFD = argmax(
m_fftSFD->out(),
m_fftInterpolation,
m_fftLength,
magsqSFD,
magsqTotal,
nullptr,
m_fftInterpolation
) / m_fftInterpolation;
unsigned int imax = argmax(
m_fft->out(),
m_fftInterpolation,
m_fftLength,
magsqPre,
magsqSFDTotal,
m_spectrumBuffer,
m_fftInterpolation
) / m_fftInterpolation;
m_preambleHistory[m_chirpCount] = imax;
m_chirpCount++;
if (magsqPre < magsqSFD) // preamble drop
{
m_magsqTotalAvg(magsqSFDTotal);
if (m_chirpCount < 1 + (m_settings.hasSyncWord() ? 2 : 0)) // too early
{
m_state = ChirpChatStateReset;
qDebug("ChirpChatDemodSink::processSample: SFD search: signal drop is too early");
}
else
{
if (m_settings.hasSyncWord())
{
m_syncWord = round(m_preambleHistory[m_chirpCount-2] / 8.0);
m_syncWord += 16 * round(m_preambleHistory[m_chirpCount-3] / 8.0);
qDebug("ChirpChatDemodSink::processSample: SFD found: pre: %4u|%11.6f - sfd: %4u|%11.6f sync: %x", imax, magsqPre, imaxSFD, magsqSFD, m_syncWord);
}
else
{
qDebug("ChirpChatDemodSink::processSample: SFD found: pre: %4u|%11.6f - sfd: %4u|%11.6f", imax, magsqPre, imaxSFD, magsqSFD);
}
int sadj = 0;
int nadj = 0;
int zadj;
int sfdSkip = m_sfdSkip;
for (int i = 0; i < m_chirpCount - 1 - (m_settings.hasSyncWord() ? 2 : 0); i++)
{
sadj += m_preambleHistory[i] > m_nbSymbols/2 ? m_preambleHistory[i] - m_nbSymbols : m_preambleHistory[i];
nadj++;
}
zadj = nadj == 0 ? 0 : sadj / nadj;
zadj = zadj < -(sfdSkip/2) ? -(sfdSkip/2) : zadj > sfdSkip/2 ? sfdSkip/2 : zadj;
qDebug("ChirpChatDemodSink::processSample: zero adjust: %d (%d)", zadj, nadj);
m_sfdSkipCounter = 0;
m_fftCounter = m_fftLength - m_sfdSkip + zadj;
m_chirp += zadj;
m_state = ChirpChatStateSkipSFD; //ChirpChatStateSlideSFD;
}
}
else if (m_chirpCount > (m_settings.m_preambleChirps - m_requiredPreambleChirps + 2)) // SFD missed start over
{
qDebug("ChirpChatDemodSink::processSample: SFD search: number of possible chirps exceeded");
m_magsqTotalAvg(magsqTotal);
m_state = ChirpChatStateReset;
}
else
{
if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
}
qDebug("ChirpChatDemodSink::processSample: SFD search: pre: %4u|%11.6f - sfd: %4u|%11.6f", imax, magsqPre, imaxSFD, magsqSFD);
m_magsqTotalAvg(magsqTotal);
m_magsqOnAvg(magsqPre);
}
}
}
else if (m_state == ChirpChatStateSkipSFD) // Just skip the rest of SFD
{
m_fftCounter++;
if (m_fftCounter == m_fftLength)
{
m_fftCounter = m_fftLength - m_sfdSkip;
m_sfdSkipCounter++;
if (m_sfdSkipCounter == m_settings.getNbSFDFourths() - 4U) // SFD chips fourths less one full period
{
qDebug("ChirpChatDemodSink::processSample: SFD skipped");
m_chirp = m_chirp0;
m_fftCounter = 0;
m_chirpCount = 0;
m_magsqMax = 0.0;
m_decodeMsg = ChirpChatDemodMsg::MsgDecodeSymbols::create();
m_decodeMsg->setSyncWord(m_syncWord);
m_state = ChirpChatStateReadPayload;
}
}
}
else if (m_state == ChirpChatStateReadPayload)
{
m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
m_fftCounter++;
if (m_fftCounter == m_fftLength)
{
m_fftWindow.apply(m_fft->in());
std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_interpolatedFFTLength, Complex{0.0, 0.0});
m_fft->transform();
m_fftCounter = 0;
double magsq, magsqTotal;
unsigned short symbol = evalSymbol(
argmax(
m_fft->out(),
m_fftInterpolation,
m_fftLength,
magsq,
magsqTotal,
m_spectrumBuffer,
m_fftInterpolation
)
) % m_nbSymbolsEff;
if (m_spectrumSink) {
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
}
if (magsq > m_magsqMax) {
m_magsqMax = magsq;
}
m_magsqTotalAvg(magsq);
m_decodeMsg->pushBackSymbol(symbol);
if ((m_chirpCount == 0)
|| (m_settings.m_eomSquelchTenths == 121) // max - disable squelch
|| ((m_settings.m_eomSquelchTenths*magsq)/10.0 > m_magsqMax))
{
qDebug("ChirpChatDemodSink::processSample: symbol %02u: %4u|%11.6f", m_chirpCount, symbol, magsq);
m_magsqOnAvg(magsq);
m_chirpCount++;
if (m_chirpCount > m_settings.m_nbSymbolsMax)
{
qDebug("ChirpChatDemodSink::processSample: message length exceeded");
m_state = ChirpChatStateReset;
m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
if (m_decoderMsgQueue && m_settings.m_decodeActive) {
m_decoderMsgQueue->push(m_decodeMsg);
} else {
delete m_decodeMsg;
}
}
}
else
{
qDebug("ChirpChatDemodSink::processSample: end of message");
m_state = ChirpChatStateReset;
m_decodeMsg->popSymbol(); // last symbol is garbage
m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
if (m_decoderMsgQueue && m_settings.m_decodeActive) {
m_decoderMsgQueue->push(m_decodeMsg);
} else {
delete m_decodeMsg;
}
}
}
}
else
{
m_state = ChirpChatStateReset;
}
m_chirp++;
if (m_chirp >= m_chirp0 + m_nbSymbols) {
m_chirp = m_chirp0;
}
}
void ChirpChatDemodSink::reset()
{
m_chirp = 0;
m_chirp0 = 0;
m_fftCounter = 0;
m_argMaxHistoryCounter = 0;
m_sfdSkipCounter = 0;
}
unsigned int ChirpChatDemodSink::argmax(
const Complex *fftBins,
unsigned int fftMult,
unsigned int fftLength,
double& magsqMax,
double& magsqTotal,
Complex *specBuffer,
unsigned int specDecim)
{
magsqMax = 0.0;
magsqTotal = 0.0;
unsigned int imax;
double magSum = 0.0;
for (unsigned int i = 0; i < fftMult*fftLength; i++)
{
double magsq = std::norm(fftBins[i]);
magsqTotal += magsq;
if (magsq > magsqMax)
{
imax = i;
magsqMax = magsq;
}
if (specBuffer)
{
magSum += magsq;
if (i % specDecim == specDecim - 1)
{
specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
magSum = 0.0;
}
}
}
magsqTotal /= fftMult*fftLength;
return imax;
}
unsigned int ChirpChatDemodSink::argmaxSpreaded(
const Complex *fftBins,
unsigned int fftMult,
unsigned int fftLength,
double& magsqMax,
double& magsqNoise,
double& magSqTotal,
Complex *specBuffer,
unsigned int specDecim)
{
magsqMax = 0.0;
magsqNoise = 0.0;
magSqTotal = 0.0;
unsigned int imax = 0;
double magSum = 0.0;
double magSymbol = 0.0;
unsigned int spread = fftMult * (1<<m_settings.m_deBits);
unsigned int istart = fftMult*fftLength - spread/2 + 1;
for (unsigned int i2 = istart; i2 < istart + fftMult*fftLength; i2++)
{
unsigned int i = i2 % (fftMult*fftLength);
double magsq = std::norm(fftBins[i]);
magSymbol += magsq;
magSqTotal += magsq;
if (i % spread == spread/2) // boundary (inclusive)
{
if (magSymbol > magsqMax)
{
magsqMax = magSymbol;
imax = i;
}
magsqNoise += magSymbol;
magSymbol = 0.0;
}
if (specBuffer)
{
magSum += magsq;
if (i % specDecim == specDecim - 1)
{
specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
magSum = 0.0;
}
}
}
magsqNoise -= magsqMax;
magsqNoise /= fftLength;
magSqTotal /= fftMult*fftLength;
return imax / spread;
}
void ChirpChatDemodSink::decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation)
{
for (unsigned int i = 0; i < size; i++)
{
if (i % decimation == 0) {
out[i/decimation] = in[i];
}
}
}
int ChirpChatDemodSink::toSigned(int u, int intSize)
{
if (u > intSize/2) {
return u - intSize;
} else {
return u;
}
}
unsigned int ChirpChatDemodSink::evalSymbol(unsigned int rawSymbol)
{
unsigned int spread = m_fftInterpolation * (1<<m_settings.m_deBits);
if (spread < 2 ) {
return rawSymbol;
} else {
return (rawSymbol + spread/2 - 1) / spread; // middle point goes to symbol below (smear to the right)
}
}
void ChirpChatDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
{
qDebug() << "ChirpChatDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset
<< " bandwidth: " << bandwidth;
if ((channelFrequencyOffset != m_channelFrequencyOffset) ||
(channelSampleRate != m_channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((channelSampleRate != m_channelSampleRate) ||
(bandwidth != m_bandwidth) || force)
{
m_interpolator.create(16, channelSampleRate, bandwidth / 1.25f);
m_interpolatorDistance = (Real) channelSampleRate / (Real) bandwidth;
m_sampleDistanceRemain = 0;
qDebug() << "ChirpChatDemodSink::applyChannelSettings: m_interpolator.create:"
<< " m_interpolatorDistance: " << m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_bandwidth = bandwidth;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void ChirpChatDemodSink::applySettings(const ChirpChatDemodSettings& settings, bool force)
{
qDebug() << "ChirpChatDemodSink::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spreadFactor: " << settings.m_spreadFactor
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|| (settings.m_deBits != m_settings.m_deBits)
|| (settings.m_fftWindow != m_settings.m_fftWindow) || force) {
initSF(settings.m_spreadFactor, settings.m_deBits, settings.m_fftWindow);
}
m_settings = settings;
}

View File

@ -0,0 +1,146 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMODSINK_H
#define INCLUDE_CHIRPCHATDEMODSINK_H
#include <vector>
#include <queue>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/fftwindow.h"
#include "util/message.h"
#include "util/movingaverage.h"
#include "chirpchatdemodsettings.h"
class BasebandSampleSink;
class FFTEngine;
namespace ChirpChatDemodMsg {
class MsgDecodeSymbols;
}
class MessageQueue;
class ChirpChatDemodSink : public ChannelSampleSink {
public:
ChirpChatDemodSink();
~ChirpChatDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
bool getDemodActive() const { return m_demodActive; }
void setDecoderMessageQueue(MessageQueue *messageQueue) { m_decoderMsgQueue = messageQueue; }
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false);
void applySettings(const ChirpChatDemodSettings& settings, bool force = false);
double getCurrentNoiseLevel() const { return m_magsqOffAvg.instantAverage() / (1<<m_settings.m_spreadFactor); }
double getTotalPower() const { return m_magsqTotalAvg.instantAverage() / (1<<m_settings.m_spreadFactor); }
private:
enum ChirpChatState
{
ChirpChatStateReset, //!< Reset everything to start all over
ChirpChatStateDetectPreamble, //!< Look for preamble
ChirpChatStatePreambleResyc, //!< Synchronize with what is left of preamble chirp
ChirpChatStatePreamble, //!< Preamble is found and look for SFD start
ChirpChatStateSkipSFD, //!< Skip SFD
ChirpChatStateReadPayload,
ChirpChatStateTest
};
ChirpChatDemodSettings m_settings;
ChirpChatState m_state;
bool m_demodActive;
ChirpChatDemodMsg::MsgDecodeSymbols *m_decodeMsg;
MessageQueue *m_decoderMsgQueue;
int m_bandwidth;
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_chirp;
int m_chirp0;
static const unsigned int m_requiredPreambleChirps = 4; //!< Number of chirps required to estimate preamble
static const unsigned int m_maxSFDSearchChirps = 8; //!< Maximum number of chirps when looking for SFD after preamble detection
static const unsigned int m_fftInterpolation = 2; //!< FFT interpolation factor (usually a power of 2)
FFTEngine *m_fft;
FFTEngine *m_fftSFD;
int m_fftSequence;
int m_fftSFDSequence;
FFTWindow m_fftWindow;
Complex *m_downChirps;
Complex *m_upChirps;
Complex *m_spectrumLine;
unsigned int m_fftCounter;
int m_argMaxHistory[m_requiredPreambleChirps];
unsigned int m_argMaxHistoryCounter;
unsigned int m_preambleHistory[m_maxSFDSearchChirps];
unsigned int m_syncWord;
double m_magsqMax;
MovingAverageUtil<double, double, 10> m_magsqOnAvg;
MovingAverageUtil<double, double, 10> m_magsqOffAvg;
MovingAverageUtil<double, double, 10> m_magsqTotalAvg;
std::queue<double> m_magsqQueue;
unsigned int m_chirpCount; //!< Generic chirp counter
unsigned int m_sfdSkip; //!< Number of samples in a SFD skip or slide (1/4) period
unsigned int m_sfdSkipCounter; //!< Counter of skip or slide periods
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
Real m_interpolatorDistance;
BasebandSampleSink* m_spectrumSink;
Complex *m_spectrumBuffer;
unsigned int m_nbSymbols; //!< Number of symbols = length of base FFT
unsigned int m_nbSymbolsEff; //!< effective symbols considering DE bits
unsigned int m_fftLength; //!< Length of base FFT
unsigned int m_interpolatedFFTLength; //!< Length of interpolated FFT
int m_deLength; //!< Number of FFT bins collated to represent one symbol
int m_preambleTolerance; //!< Number of FFT bins to collate when looking for preamble
void processSample(const Complex& ci);
void initSF(unsigned int sf, unsigned int deBits, FFTWindow::Function fftWindow); //!< Init tables, FFTs, depending on spread factor
void reset();
unsigned int argmax(
const Complex *fftBins,
unsigned int fftMult,
unsigned int fftLength,
double& magsqMax,
double& magSqTotal,
Complex *specBuffer,
unsigned int specDecim
);
unsigned int argmaxSpreaded( //!< count energy in adjacent bins for same symbol (needs DE bits > 0)
const Complex *fftBins,
unsigned int fftMult,
unsigned int fftLength,
double& magsqMax,
double& magsqNoise,
double& magSqTotal,
Complex *specBuffer,
unsigned int specDecim
);
void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
int toSigned(int u, int intSize);
unsigned int evalSymbol(unsigned int rawSymbol);
};
#endif // INCLUDE_CHIRPCHATDEMODSINK_H

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGChannelSettings.h"
#include "chirpchatdemod.h"
#include "chirpchatdemodwebapiadapter.h"
ChirpChatDemodWebAPIAdapter::ChirpChatDemodWebAPIAdapter()
{}
ChirpChatDemodWebAPIAdapter::~ChirpChatDemodWebAPIAdapter()
{}
int ChirpChatDemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setChirpChatDemodSettings(new SWGSDRangel::SWGChirpChatDemodSettings());
response.getChirpChatDemodSettings()->init();
ChirpChatDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int ChirpChatDemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
ChirpChatDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
ChirpChatDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATDEMOD_WEBAPIADAPTER_H
#define INCLUDE_CHIRPCHATDEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "chirpchatdemodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class ChirpChatDemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
ChirpChatDemodWebAPIAdapter();
virtual ~ChirpChatDemodWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
private:
ChirpChatDemodSettings m_settings;
};
#endif // INCLUDE_CHIRPCHATDEMOD_WEBAPIADAPTER_H

View File

@ -0,0 +1,84 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#include "chirpchatplugin.h"
#ifndef SERVER_MODE
#include "chirpchatdemodgui.h"
#endif
#include "chirpchatdemod.h"
const PluginDescriptor ChirpChatPlugin::m_pluginDescriptor = {
ChirpChatDemod::m_channelId,
QString("ChirpChat Demodulator"),
QString("5.13.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
ChirpChatPlugin::ChirpChatPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& ChirpChatPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void ChirpChatPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register demodulator
m_pluginAPI->registerRxChannel(ChirpChatDemod::m_channelIdURI, ChirpChatDemod::m_channelId, this);
}
void ChirpChatPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
ChirpChatDemod *instance = new ChirpChatDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* ChirpChatPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
return 0;
}
#else
ChannelGUI* ChirpChatPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return ChirpChatDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif

View File

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019-2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_CHIRPCHATPLUGIN_H
#define INCLUDE_CHIRPCHATPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class ChirpChatPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.chirpchatdemod")
public:
explicit ChirpChatPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_LoRaPLUGIN_H

View File

@ -0,0 +1,274 @@
<h1>ChirpChat demodulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to demodulate and decode transmissions based on Chirp Spread Spectrum (CSS). The basic idea is to transform each symbol of a MFSK modulation to an ascending frequency ramp shifted in time. It could equally be a descending ramp but this one is reserved to detect a break in the preamble sequence (synchronization). This plugin has been designed to work in conjunction with the ChirpChat modulator plugin that should be used ideally on the transmission side.
It has clearly been inspired by the LoRa technique but is designed for experimentation and extension to other protocols mostly inspired by amateur radio techniques using chirp modulation to transmit symbols. Thanks to the MFSK to chirp translation it is possible to adapt any MFSK based mode.
LoRa is a property of Semtech and the details of the protocol are not made public. However a LoRa compatible protocol has been implemented based on the reverse engineering performed by the community. It is mainly based on the work done in https://github.com/myriadrf/LoRa-SDR. You can find more information about LoRa and chirp modulation here:
- To get an idea of what is LoRa: [here](https://www.link-labs.com/blog/what-is-lora)
- A detailed inspection of LoRa modulation and protocol: [here](https://static1.squarespace.com/static/54cecce7e4b054df1848b5f9/t/57489e6e07eaa0105215dc6c/1464376943218/Reversing-Lora-Knight.pdf)
Transmissions from the RN2483 module with the Distance Enhancement feature (spread factors 11 and 12) could be successfully received. It has not been tested with Semtech SX127x hardware. This LoRa decoder is designed for experimentation. For production grade applications it is recommended to use dedicated hardware instead.
Modulation characteristics from LoRa have been augmented with more bandwidths and FFT bin collations (DE factor). Plain TTY and ASCII have also been added and there are plans to add some more complex typically amateur radio MFSK based modes like JT65.
Note: this plugin is available in version 5 only (since 5.2.0).
<h2>Interface</h2>
![ChirpChat Demodulator plugin GUI](../../../doc/img/ChirpChatDemod_plugin.png)
<h3>1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>2: De-chirped channel power</h3>
This is the total power in the FFT of the de-chirped signal in dB. When no ChirpChat signal is detected this corresponds to the power received in the bandwidth (3). It will show a significant increase in presence of a ChirpChat signal that can be detected.
<h3>3: Bandwidth</h3>
This is the bandwidth of the ChirpChat signal. Similarly to LoRa the signal sweeps between the lower and the upper frequency of this bandwidth. The sample rate of the ChirpChat signal in seconds is exactly one over this bandwidth in Hertz.
In the LoRa standard there are 2 base bandwidths: 500 and 333.333 kHz. A 400 kHz base has been added. Possible bandwidths are obtained by a division of these base bandwidths by a power of two from 1 to 64. Extra divisor of 128 is provided to achieve smaller bandwidths that can fit in a SSB channel. Finally special divisors from a 384 kHz base are provided to allow even more narrow bandwidths.
Thus available bandwidths are:
- **500000** (500000 / 1) Hz
- **400000** (400000 / 1) Hz not in LoRa standard
- **333333** (333333 / 1) Hz
- **250000** (500000 / 2) Hz
- **200000** (400000 / 2) Hz not in LoRa standard
- **166667** (333333 / 2) Hz
- **125000** (500000 / 4) Hz
- **100000** (400000 / 4) Hz not in LoRa standard
- **83333** (333333 / 4) Hz
- **62500** (500000 / 8) Hz
- **50000** (400000 / 8) Hz not in LoRa standard
- **41667** (333333 / 8) Hz
- **31250** (500000 / 16) Hz
- **25000** (400000 / 16) Hz not in LoRa standard
- **20833** (333333 / 16) Hz
- **15625** (500000 / 32) Hz
- **12500** (400000 / 32) Hz not in LoRa standard
- **10417** (333333 / 32) Hz
- **7813** (500000 / 64) Hz
- **6250** (400000 / 64) Hz not in LoRa standard
- **5208** (333333 / 64) Hz
- **3906** (500000 / 128) Hz not in LoRa standard
- **3125** (400000 / 128) Hz not in LoRa standard
- **2604** (333333 / 128) Hz not in LoRa standard
- **1500** (384000 / 256) Hz not in LoRa standard
- **750** (384000 / 512) Hz not in LoRa standard
- **375** (384000 / 1024) Hz not in LoRa standard
The ChirpChat signal is oversampled by two therefore it needs a baseband of at least twice the bandwidth. This drives the maximum value on the slider automatically.
<h3>4: De-chirped noise maximum power</h3>
This is the maximum power received in one FFT bin (the argmax bin) in dB when no signal is detected. It is averaged over 10 values.
<h3>5. De-chirped signal maximum power</h3>
This is the maximum power received in one FFT bin (the argmax bin) in dB when a signal is detected. It is averaged over 10 values.
<h3>6: De-chirped signal over noise ratio</h3>
The noise level reference is the noise maximum power just before the detected signal starts and the signal level the signal maximum power just before the detected signal stops. To get a significant reading you have to adjust correctly the number of preamble chirps (9) and the End Of Message squelch level (A.3) and/or the message length (A.4) so that signal boundaries are determined correctly.
Decode errors are very likely to happen when this value falls below 4 dB.
<h3>7: FFT Window</h3>
A choice of FFT Windows to apply to the FFT performed on the de-chirped signal is provided. These are the same windows as those used in the spectrum display. The effect of windowing is to reduce the spill over in adjacent bins at the expense of a flatter top and fatter main lobe. When the purpose is frequency detection this is not what is desired necessarily and thus the "Rectangular" window (i.e. no window) should be chosen. However a variety of windows is provided to experiment with. Experimentally the best alternative to "Rectangular" is "Kaiser" then "Bartlett" and "Hanning". The complete list is:
- **Bart**: Bartlett
- **B-H**:
- **FT**: Flat Top
- **Ham**: Hamming
- **Han**: Hanning
- **Rec**: Rectangular (no window)
- **Kai**: Kaiser with &alpha; = &pi;
<h3>8: Spread Factor</h3>
This is the Spread Factor parameter of the ChirpChat signal. This is the log2 of the FFT size used over the bandwidth (3). The number of symbols is 2<sup>SF-DE</sup> where SF is the spread factor and DE the Distance Enhancement factor (8)
<h3>9: Distance Enhancement factor</h3>
The LoRa standard specifies 0 (no DE) or 2 (DE active). The ChirpChat DE range is extended to all values between 0 and 4 bits.
This is the log2 of the number of FFT bins used for one symbol. Extending the numbe of FFT bins per symbol decreases the probabilty to detect the wrong symbol as an adjacent bin. It can also overcome frequency drift on long messages.
In practice it is difficult to make correct decodes if only one FFT bin is used to code one symbol (DE=0) therefore it is recommended to use a DE factor of 2 or more. With medium SNR DE=1 can still achieve good results.
<h3>10: Number of expected preamble chirps</h3>
This is the number of chirps expected in the preamble and has to be agreed between the transmitter and receiver.
<h3>A: Payload controls and indicators</h3>
![ChirpChat Demodulator payload controls](../../../doc/img/ChirpChatDemod_payload.png)
<h4>A.1: Coding scheme</h4>
In addition to the LoRa standard plain ASCII and TTY have been added for pure text messages. ASCII and TTY have no form of FEC.
- **LoRa**: LoRa standard (see LoRa documentation)
- **ASCII**: This is plain 7 bit ASCII coded characters. It needs exactly 7 effective bits per symbols (SF - DE = 7)
- **TTY**: Baudot (Teletype) 5 bit encoded characters. It needs exactly 5 effective bits per symbols (SF - DE = 5)
<h4>A.2: Start/Stop decoder</h4>
You can suspend and resume decoding activity using this button. This is useful if you want to freeze the payload content display.
<h4>A.3: End Of Message squelch</h4>
This is used to determine the end of message automatically. It can be de-activated by turning the button completely to the right (as shown on the picture). In this case it relies on the message length set with (A.4).
During paylaod detection the maximum power value in the FFT (at argmax) P<sub>max</sub> is stored and compared to the current argmax power value P<sub>i</sub> if S<sub>EOM</sub> is this squelch value the end of message is detected if S<sub>EOM</sub> &times; S<sub>i</sub> &lt; S<sub>max</sub>
<h4>A.4: Expected message length in symbols</h4>
This is the expected number of symbols in a message. When a header is present in the payload it should match the size given in the header (A.11).
<h4>A.5: Auto mesasge length</h4>
LoRa mode only. Set message length (A.4) equal to the number of symbols specified in the message just received. When messages are sent repeatedly this helps adjusting in possible message length changes automatically.
<h4>A.6: Sync word</h4>
This is the message 1 byte sync word displayed in hexadecimal.
<h4>A.7: Expect header in message</h4>
LoRa mode only. Use this checkbox to tell if you expect or not a header in the message.
<h4>A.8: Number of FEC parity bits</h4>
LoRa mode only. This is the number of parity bits in the Hamming code used in the FEC. The standard values are 1 to 4 for H(4,5) to H(4,8) encoding. 0 is a non-standard value to specify no FEC.
When a header is expected this control is disabled because the value used is the one found in the header.
<h4>A.9: Payload CRC presence</h4>
Use this checkbox to tell if you expect a 2 byte CRC at the end of the payload.
When a header is expected this control is disabled because the value used is the one found in the header.
<h4>A.10: Packet length</h4>
This is the expected packet length in bytes without header and CRC.
When a header is expected this control is disabled because the value used is the one found in the header.
<h4>A.11: Number of symbols and codewords</h4>
This is the number of symbols (left of slash) and codewords (right of slash) used for the payload including header and CRC.
<h4>A.12: Header FEC indicator</h4>
Header uses a H(4,8) FEC. The color of the indicator gives the status of header parity checks:
- **Grey**: undefined
- **Red**: unrecoverable error
- **Blue**: recovered error
- **Green**: no errors
<h4>A.13: Header CRC indicator</h4>
The header has a one byte CRC. The color of this indicator gives the CRC status:
- **Green**: CRC OK
- **Red**: CRC error
<h4>A.14: Payload FEC indicator</h4>
The color of the indicator gives the status of payload parity checks:
- **Grey**: undefined
- **Red**: unrecoverable error. H(4,7) cannot distinguish between recoverable and unrecoverable error. Therefore this is never displayed for H(4,7)
- **Blue**: recovered error
- **Green**: no errors
<h4>A.15: Payload CRC indicator</h4>
The payload can have a two byte CRC. The color of this indicator gives the CRC status:
- **Grey**: No CRC
- **Green**: CRC OK
- **Red**: CRC error
<h3>11: Clear message window</h3>
Use this push button to clear the message window (12)
<h3>12: Message window</h3>
This is where the message and status data are displayed. The display varies if the coding scheme is purely text based (TTY, ASCII) or text/binary mixed based (LoRa). The text vs binary consideration concerns the content of the message not the way it is transmitted on air that is by itself binary.
<h4>12.a: Text messages</h4>
The format of a message line is the following:
![ChirpChat Demodulator message string window](../../../doc/img/ChirpChatDemod_message_string.png)
- 1: Timestamp in HH:MM:SS format
- 2: Signal level. Same as (5) for the current message
- 3: Signal Over Noise. Same as (6) for the current message
- 4: Start of text indicator
- 5: Text
<h4>12.b: Binary messages</h4>
![ChirpChat Demodulator message bytes window](../../../doc/img/ChirpChatDemod_message_binary.png)
- 1: Timestamp in HH:NN:SS format
- 2: Sync word. This is the sync word (byte) displayed in hex. Corrsponds to (A.5) in the current message.
- 3: De-chirped signal level. This is the de-chirped signal level in dB. Corrsponds to (5) in the current message.
- 4: De-chirped signal to noise ratio. This is the de-chirped signal to noise ratio in dB. Corrsponds to (6) in the current message.
- 5: Header FEC status. Corresponds to (A.12) indicator in the current message:
- **n/a**: unknown or not applicable
- **err**: unrecoverable error
- **fix**: corrected error
- **ok**: OK
- 6: Header CRC status. Corresponds to (A.13) indicator in the current message
- **ok**: CRC OK
- **err**: CRC error
- **n/a**: not applicable
- 7: Payload FEC status. Corresponds to (A.14) indicator in the current message. If the end of message is reached before expectation then `ERR: too early` is displayed instead and no payload CRC status (next) is displayed.
- **n/a**: unknown or not applicable
- **err**: unrecoverable error
- **fix**: corrected error
- **ok**: OK
- 8: Payload CRC status. Corresponds to (A.15) indicator in the current message:
- **ok**: CRC OK
- **err**: CRC error
- **n/a**: not applicable
- 9: Displacement at start of line. 16 bytes in 4 groups of 4 bytes are displayed per line starting with the displacement in decimal.
- 10: Bytes group. This is a group of 4 bytes displayed as hexadecimal values. The payload is displayed with its possible CRC and without the header.
- 11: Message as text with "TXT" as prefix indicating it is the translation of the message to character representation
<h3>13: Send message via UDP</h3>
Select to send the decoded message via UDP.
<h3>14: UDP address and port</h3>
This is the UDP address and port to where the decoded message is sent when (12) is selected.
<h3>B: De-chirped spectrum</h3>
This is the spectrum of the de-chirped signal when a ChirpChat signal can be decoded.
The frequency span corresponds to the bandwidth of the ChirpChat signal (3). Default FFT size is 2<sup>SF</sup> where SF is the spread factor (7).
Sequences of successful ChirpChat signal demodulation are separated by blank lines (genreated with a string of high value bins).
Controls are the usual controls of spectrum displays with the following restrictions:
- The window type is non operating because the FFT window is chosen by (7)
- The FFT size can be changed however it is set to 2<sup>SF</sup> where SF is the spread factor and thus displays correctly

View File

@ -1,36 +0,0 @@
project(lora)
set(lora_SOURCES
lorademod.cpp
lorademodgui.cpp
lorademodsettings.cpp
lorademodsink.cpp
lorademodbaseband.cpp
loraplugin.cpp
lorademodgui.ui
)
set(lora_HEADERS
lorademod.h
lorademodgui.h
lorademodsettings.h
lorademodsink.h
lorademodbaseband.h
loraplugin.h
)
include_directories(
)
add_library(demodlora SHARED
${lora_SOURCES}
)
target_link_libraries(demodlora
Qt5::Core
Qt5::Widgets
sdrbase
sdrgui
)
install(TARGETS demodlora DESTINATION ${INSTALL_PLUGINS_DIR})

View File

@ -1,152 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// (c) 2015 John Greb
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <QTime>
#include <QDebug>
#include <QThread>
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "lorademod.h"
MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureLoRaDemod, Message)
const QString LoRaDemod::m_channelIdURI = "sdrangel.channel.lorademod";
const QString LoRaDemod::m_channelId = "LoRaDemod";
LoRaDemod::LoRaDemod(DeviceAPI* deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_spectrumVis(SDR_RX_SCALEF)
{
setObjectName(m_channelId);
m_thread = new QThread(this);
m_basebandSink = new LoRaDemodBaseband();
m_basebandSink->setSpectrumSink(&m_spectrumVis);
m_basebandSink->moveToThread(m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
}
LoRaDemod::~LoRaDemod()
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
delete m_basebandSink;
delete m_thread;
}
void LoRaDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO)
{
(void) pO;
m_basebandSink->feed(begin, end);
}
void LoRaDemod::start()
{
qDebug() << "LoRaDemod::start";
if (m_basebandSampleRate != 0) {
m_basebandSink->setBasebandSampleRate(m_basebandSampleRate);
}
m_basebandSink->reset();
m_thread->start();
}
void LoRaDemod::stop()
{
qDebug() << "LoRaDemod::stop";
m_thread->exit();
m_thread->wait();
}
bool LoRaDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureLoRaDemod::match(cmd))
{
qDebug() << "LoRaDemod::handleMessage: MsgConfigureLoRaDemod";
MsgConfigureLoRaDemod& cfg = (MsgConfigureLoRaDemod&) cmd;
LoRaDemodSettings settings = cfg.getSettings();
applySettings(settings, cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "LoRaDemod::handleMessage: DSPSignalNotification";
m_basebandSink->getInputMessageQueue()->push(rep);
return true;
}
else
{
return false;
}
}
QByteArray LoRaDemod::serialize() const
{
return m_settings.serialize();
}
bool LoRaDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureLoRaDemod *msg = MsgConfigureLoRaDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureLoRaDemod *msg = MsgConfigureLoRaDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
{
qDebug() << "LoRaDemod::applySettings:"
<< " m_centerFrequency: " << settings.m_centerFrequency
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spread: " << settings.m_spread
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband *msg = LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
m_settings = settings;
}

View File

@ -1,101 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// (C) 2015 John Greb //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMOD_H
#define INCLUDE_LORADEMOD_H
#include <vector>
#include "dsp/basebandsamplesink.h"
#include "dsp/spectrumvis.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "lorademodbaseband.h"
class DeviceAPI;
class QThread;
class LoRaDemod : public BasebandSampleSink, public ChannelAPI {
public:
class MsgConfigureLoRaDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const LoRaDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureLoRaDemod* create(const LoRaDemodSettings& settings, bool force)
{
return new MsgConfigureLoRaDemod(settings, force);
}
private:
LoRaDemodSettings m_settings;
bool m_force;
MsgConfigureLoRaDemod(const LoRaDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
LoRaDemod(DeviceAPI* deviceAPI);
virtual ~LoRaDemod();
virtual void destroy() { delete this; }
SpectrumVis *getSpectrumVis() { return &m_spectrumVis; }
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual const QString& getURI() const { return m_channelIdURI; }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return 0; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return 0;
}
static const QString m_channelIdURI;
static const QString m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread *m_thread;
LoRaDemodBaseband* m_basebandSink;
LoRaDemodSettings m_settings;
SpectrumVis m_spectrumVis;
int m_basebandSampleRate;
void applySettings(const LoRaDemodSettings& settings, bool force = false);
};
#endif // INCLUDE_LORADEMOD_H

View File

@ -1,183 +0,0 @@
#include "device/deviceuiset.h"
#include <QDockWidget>
#include <QMainWindow>
#include "ui_lorademodgui.h"
#include "dsp/spectrumvis.h"
#include "gui/glspectrum.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "dsp/dspengine.h"
#include "lorademod.h"
#include "lorademodgui.h"
LoRaDemodGUI* LoRaDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
LoRaDemodGUI* gui = new LoRaDemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void LoRaDemodGUI::destroy()
{
delete this;
}
void LoRaDemodGUI::setName(const QString& name)
{
setObjectName(name);
}
QString LoRaDemodGUI::getName() const
{
return objectName();
}
qint64 LoRaDemodGUI::getCenterFrequency() const {
return m_channelMarker.getCenterFrequency();
}
void LoRaDemodGUI::setCenterFrequency(qint64 centerFrequency)
{
m_channelMarker.setCenterFrequency(centerFrequency);
applySettings();
}
void LoRaDemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray LoRaDemodGUI::serialize() const
{
return m_settings.serialize();
}
bool LoRaDemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
bool LoRaDemodGUI::handleMessage(const Message& message)
{
(void) message;
return false;
}
void LoRaDemodGUI::viewChanged()
{
applySettings();
}
void LoRaDemodGUI::on_BW_valueChanged(int value)
{
if (value < 0) {
m_settings.m_bandwidthIndex = 0;
} else if (value < LoRaDemodSettings::nb_bandwidths) {
m_settings.m_bandwidthIndex = value;
} else {
m_settings.m_bandwidthIndex = LoRaDemodSettings::nb_bandwidths - 1;
}
int thisBW = LoRaDemodSettings::bandwidths[value];
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
m_channelMarker.setBandwidth(thisBW);
applySettings();
}
void LoRaDemodGUI::on_Spread_valueChanged(int value)
{
(void) value;
}
void LoRaDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::LoRaDemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_doApplySettings(true)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
m_LoRaDemod = (LoRaDemod*) rxChannel; //new LoRaDemod(m_deviceUISet->m_deviceSourceAPI);
m_spectrumVis = m_LoRaDemod->getSpectrumVis();
m_spectrumVis->setGLSpectrum(ui->glSpectrum);
ui->glSpectrum->setCenterFrequency(16000);
ui->glSpectrum->setSampleRate(32000);
ui->glSpectrum->setDisplayWaterfall(true);
ui->glSpectrum->setDisplayMaxHold(true);
m_channelMarker.setMovable(false);
m_channelMarker.setVisible(true);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(viewChanged()));
m_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
ui->spectrumGUI->setBuddies(m_spectrumVis, ui->glSpectrum);
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setSpectrumGUI(ui->spectrumGUI);
displaySettings();
applySettings(true);
}
LoRaDemodGUI::~LoRaDemodGUI()
{
delete ui;
}
void LoRaDemodGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void LoRaDemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
setTitleColor(m_channelMarker.getColor());
LoRaDemod::MsgConfigureLoRaDemod* message = LoRaDemod::MsgConfigureLoRaDemod::create( m_settings, force);
m_LoRaDemod->getInputMessageQueue()->push(message);
}
}
void LoRaDemodGUI::displaySettings()
{
int thisBW = LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex];
m_channelMarker.blockSignals(true);
m_channelMarker.setBandwidth(thisBW);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setColor(m_settings.m_rgbColor);
setTitleColor(m_settings.m_rgbColor);
m_channelMarker.blockSignals(false);
blockApplySettings(true);
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
ui->BW->setValue(m_settings.m_bandwidthIndex);
blockApplySettings(false);
}

View File

@ -1,64 +0,0 @@
#ifndef INCLUDE_LoRaDEMODGUI_H
#define INCLUDE_LoRaDEMODGUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "util/messagequeue.h"
#include "lorademodsettings.h"
class PluginAPI;
class DeviceUISet;
class LoRaDemod;
class SpectrumVis;
class BasebandSampleSink;
namespace Ui {
class LoRaDemodGUI;
}
class LoRaDemodGUI : public ChannelGUI {
Q_OBJECT
public:
static LoRaDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceAPI, BasebandSampleSink *rxChannel);
virtual void destroy();
void setName(const QString& name);
QString getName() const;
virtual qint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual bool handleMessage(const Message& message);
private slots:
void viewChanged();
void on_BW_valueChanged(int value);
void on_Spread_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
private:
Ui::LoRaDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
LoRaDemodSettings m_settings;
bool m_doApplySettings;
LoRaDemod* m_LoRaDemod;
SpectrumVis* m_spectrumVis;
MessageQueue m_inputMessageQueue;
explicit LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~LoRaDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
};
#endif // INCLUDE_LoRaDEMODGUI_H

View File

@ -1,208 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoRaDemodGUI</class>
<widget class="RollupWidget" name="LoRaDemodGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>302</width>
<height>410</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>LoRa Demodulator</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>35</x>
<y>35</y>
<width>242</width>
<height>96</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Bandwidth</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mabel">
<property name="text">
<string>Spreading</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="BW">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>4</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="Spread">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="BWText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>7813 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="SpreadText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>6:4 2^8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
<rect>
<x>40</x>
<y>140</y>
<width>218</width>
<height>184</height>
</rect>
</property>
<property name="windowTitle">
<string>Channel Spectrum</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="GLSpectrum" name="glSpectrum" native="true">
<property name="minimumSize">
<size>
<width>200</width>
<height>150</height>
</size>
</property>
<property name="font">
<font>
<family>Sans Serif</family>
<pointsize>9</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="GLSpectrumGUI" name="spectrumGUI" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrum</class>
<extends>QWidget</extends>
<header>gui/glspectrum.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLSpectrumGUI</class>
<extends>QWidget</extends>
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,86 +0,0 @@
#include "lorademodsettings.h"
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "lorademodsettings.h"
const int LoRaDemodSettings::bandwidths[] = {7813,15625,20833,31250,62500};
const int LoRaDemodSettings::nb_bandwidths = 5;
LoRaDemodSettings::LoRaDemodSettings() :
m_centerFrequency(0),
m_channelMarker(0),
m_spectrumGUI(0)
{
resetToDefaults();
}
void LoRaDemodSettings::resetToDefaults()
{
m_bandwidthIndex = 0;
m_spread = 0;
m_rgbColor = QColor(255, 0, 255).rgb();
m_title = "LoRa Demodulator";
}
QByteArray LoRaDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_centerFrequency);
s.writeS32(2, m_bandwidthIndex);
s.writeS32(3, m_spread);
if (m_spectrumGUI) {
s.writeBlob(4, m_spectrumGUI->serialize());
}
if (m_channelMarker) {
s.writeBlob(5, m_channelMarker->serialize());
}
s.writeString(6, m_title);
return s.final();
}
bool LoRaDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
d.readS32(1, &m_centerFrequency, 0);
d.readS32(2, &m_bandwidthIndex, 0);
d.readS32(3, &m_spread, 0);
if (m_spectrumGUI) {
d.readBlob(4, &bytetmp);
m_spectrumGUI->deserialize(bytetmp);
}
if (m_channelMarker) {
d.readBlob(5, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readString(6, &m_title, "LoRa Demodulator");
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -1,36 +0,0 @@
#ifndef PLUGINS_CHANNELRX_DEMODLORA_LORADEMODSETTINGS_H_
#define PLUGINS_CHANNELRX_DEMODLORA_LORADEMODSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <stdint.h>
class Serializable;
struct LoRaDemodSettings
{
int m_centerFrequency;
int m_bandwidthIndex;
int m_spread;
uint32_t m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
Serializable *m_spectrumGUI;
static const int bandwidths[];
static const int nb_bandwidths;
LoRaDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setSpectrumGUI(Serializable *spectrumGUI) { m_spectrumGUI = spectrumGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* PLUGINS_CHANNELRX_DEMODLORA_LORADEMODSETTINGS_H_ */

View File

@ -1,286 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QTime>
#include <QDebug>
#include <stdio.h>
#include "dsp/dsptypes.h"
#include "dsp/basebandsamplesink.h"
#include "lorademodsink.h"
const int LoRaDemodSink::DATA_BITS = 6;
const int LoRaDemodSink::SAMPLEBITS = LoRaDemodSink::DATA_BITS + 2;
const int LoRaDemodSink::SPREADFACTOR = (1 << LoRaDemodSink::SAMPLEBITS);
const int LoRaDemodSink::LORA_SFFT_LEN = (LoRaDemodSink::SPREADFACTOR / 2);
const int LoRaDemodSink::LORA_SQUELCH = 3;
LoRaDemodSink::LoRaDemodSink() :
m_spectrumSink(nullptr)
{
m_Bandwidth = LoRaDemodSettings::bandwidths[0];
m_channelSampleRate = 96000;
m_channelFrequencyOffset = 0;
m_nco.setFreq(m_channelFrequencyOffset, m_channelSampleRate);
m_interpolator.create(16, m_channelSampleRate, m_Bandwidth/1.9);
m_sampleDistanceRemain = (Real) m_channelSampleRate / m_Bandwidth;
m_chirp = 0;
m_angle = 0;
m_bin = 0;
m_result = 0;
m_count = 0;
m_header = 0;
m_time = 0;
m_tune = 0;
loraFilter = new sfft(LORA_SFFT_LEN);
negaFilter = new sfft(LORA_SFFT_LEN);
mov = new float[4*LORA_SFFT_LEN];
history = new short[1024];
finetune = new short[16];
}
LoRaDemodSink::~LoRaDemodSink()
{
delete loraFilter;
delete negaFilter;
delete [] mov;
delete [] history;
delete [] finetune;
}
void LoRaDemodSink::dumpRaw()
{
short bin, j, max;
char text[256];
max = m_time / 4 - 3;
if (max > 140) {
max = 140; // about 2 symbols to each char
}
for ( j=0; j < max; j++)
{
bin = (history[(j + 1) * 4] + m_tune ) & (LORA_SFFT_LEN - 1);
text[j] = toGray(bin >> 1);
}
prng6(text, max);
// First block is always 8 symbols
interleave6(text, 6);
interleave6(&text[8], max);
hamming6(text, 6);
hamming6(&text[8], max);
for ( j=0; j < max / 2; j++)
{
text[j] = (text[j * 2 + 1] << 4) | (0xf & text[j * 2 + 0]);
if ((text[j] < 32 )||( text[j] > 126)) {
text[j] = 0x5f;
}
}
text[3] = text[2];
text[2] = text[1];
text[1] = text[0];
text[j] = 0;
qDebug("LoRaDemodSink::dumpRaw: %s", &text[1]);
}
short LoRaDemodSink::synch(short bin)
{
short i, j;
if (bin < 0)
{
if (m_time > 70) {
dumpRaw();
}
m_time = 0;
return -1;
}
history[m_time] = bin;
if (m_time > 12)
{
if (bin == history[m_time - 6])
{
if (bin == history[m_time - 12])
{
m_tune = LORA_SFFT_LEN - bin;
j = 0;
for (i=0; i<12; i++) {
j += finetune[15 & (m_time - i)];
}
if (j < 0) {
m_tune += 1;
}
m_tune &= (LORA_SFFT_LEN - 1);
m_time = 0;
return -1;
}
}
}
m_time++;
m_time &= 1023;
if (m_time & 3) {
return -1;
}
return (bin + m_tune) & (LORA_SFFT_LEN - 1);
}
int LoRaDemodSink::detect(Complex c, Complex a)
{
int p, q;
short i, result, negresult, movpoint;
float peak, negpeak, tfloat;
float mag[LORA_SFFT_LEN];
float rev[LORA_SFFT_LEN];
loraFilter->run(c * a);
negaFilter->run(c * conj(a));
// process spectrum twice in FFTLEN
if (++m_count & ((1 << DATA_BITS) - 1)) {
return m_result;
}
movpoint = 3 & (m_count >> DATA_BITS);
loraFilter->fetch(mag);
negaFilter->fetch(rev);
peak = negpeak = 0.0f;
result = negresult = 0;
for (i = 0; i < LORA_SFFT_LEN; i++)
{
if (rev[i] > negpeak)
{
negpeak = rev[i];
negresult = i;
}
tfloat = mov[i] + mov[LORA_SFFT_LEN + i] +mov[2 * LORA_SFFT_LEN + i]
+ mov[3 * LORA_SFFT_LEN + i] + mag[i];
if (tfloat > peak)
{
peak = tfloat;
result = i;
}
mov[movpoint * LORA_SFFT_LEN + i] = mag[i];
}
p = (result - 1 + LORA_SFFT_LEN) & (LORA_SFFT_LEN -1);
q = (result + 1) & (LORA_SFFT_LEN -1);
finetune[15 & m_time] = (mag[p] > mag[q]) ? -1 : 1;
if (peak < negpeak * LORA_SQUELCH)
{
result = -1;
}
result = synch(result);
if (result >= 0) {
m_result = result;
}
return m_result;
}
void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
int newangle;
Complex ci;
m_sampleBuffer.clear();
for (SampleVector::const_iterator it = begin; it < end; ++it)
{
Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF);
c *= m_nco.nextIQ();
if (m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci))
{
m_chirp = (m_chirp + 1) & (SPREADFACTOR - 1);
m_angle = (m_angle + m_chirp) & (SPREADFACTOR - 1);
Complex cangle(cos(M_PI*2*m_angle/SPREADFACTOR),-sin(M_PI*2*m_angle/SPREADFACTOR));
newangle = detect(ci, cangle);
m_bin = (m_bin + newangle) & (LORA_SFFT_LEN - 1);
Complex nangle(cos(M_PI*2*m_bin/LORA_SFFT_LEN),sin(M_PI*2*m_bin/LORA_SFFT_LEN));
m_sampleBuffer.push_back(Sample(nangle.real() * 100, nangle.imag() * 100));
m_sampleDistanceRemain += (Real) m_channelSampleRate / m_Bandwidth;
}
}
if (m_spectrumSink) {
m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false);
}
}
void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
{
qDebug() << "LoRaDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if((channelFrequencyOffset != m_channelFrequencyOffset) ||
(channelSampleRate != m_channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((channelSampleRate != m_channelSampleRate) || force)
{
qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create";
m_interpolator.create(16, channelSampleRate, bandwidth / 1.9f);
m_sampleDistanceRemain = (Real) channelSampleRate / bandwidth;
}
m_channelSampleRate = channelSampleRate;
m_Bandwidth = bandwidth;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void LoRaDemodSink::applySettings(const LoRaDemodSettings& settings, bool force)
{
qDebug() << "LoRaDemodSink::applySettings:"
<< " m_centerFrequency: " << settings.m_centerFrequency
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spread: " << settings.m_spread
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
m_settings = settings;
}

View File

@ -1,163 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODSINK_H
#define INCLUDE_LORADEMODSINK_H
#include <vector>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/message.h"
#include "dsp/fftfilt.h"
#include "lorademodsettings.h"
class BasebandSampleSink;
class LoRaDemodSink : public ChannelSampleSink {
public:
LoRaDemodSink();
~LoRaDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false);
void applySettings(const LoRaDemodSettings& settings, bool force = false);
private:
LoRaDemodSettings m_settings;
Real m_Bandwidth;
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_chirp;
int m_angle;
int m_bin;
int m_result;
int m_count;
int m_header;
int m_time;
short m_tune;
sfft* loraFilter;
sfft* negaFilter;
float* mov;
short* history;
short* finetune;
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
BasebandSampleSink* m_spectrumSink;
SampleVector m_sampleBuffer;
static const int DATA_BITS;
static const int SAMPLEBITS;
static const int SPREADFACTOR;
static const int LORA_SFFT_LEN;
static const int LORA_SQUELCH;
int detect(Complex sample, Complex angle);
void dumpRaw(void);
short synch (short bin);
/*
Interleaving is "easiest" if the same number of bits is used per symbol as for FEC
Chosen mode "spreading 8, low rate" has 6 bits per symbol, so use 4:6 FEC
More spreading needs higher frequency resolution and longer time on air, increasing drift errors.
Want higher bandwidth when using more spreading, which needs more CPU and a better FFT.
Six bit Hamming can only correct long runs of drift errors when not using interleaving. Interleaving defeats the point of using Gray code and puts multiple bit errors into single FEC blocks. Hardware decoding uses RSSI to detect the symbols most likely to be damaged, so that individual bits can be repaired after de-interleaving.
Using Implicit Mode: explicit starts with a 4:8 block and seems to have a different whitening sequence.
*/
// Six bits per symbol, six chars per block
inline void interleave6(char* inout, int size)
{
int i, j;
char in[6 * 2];
short s;
for (j = 0; j < size; j+=6) {
for (i = 0; i < 6; i++)
in[i] = in[i + 6] = inout[i + j];
// top bits are swapped
for (i = 0; i < 6; i++) {
s = (32 & in[2 + i]) | (16 & in[1 + i]) | (8 & in[3 + i])
| (4 & in[4 + i]) | (2 & in[5 + i]) | (1 & in[6 + i]);
// bits are also rotated
s = (s << 3) | (s >> 3);
s &= 63;
s = (s >> i) | (s << (6 - i));
inout[i + j] = s & 63;
}
}
}
inline short toGray(short num)
{
return (num >> 1) ^ num;
}
// Ignore the FEC bits, just extract the data bits
inline void hamming6(char* c, int size)
{
int i;
for (i = 0; i < size; i++) {
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<0) | ((c[i] & 4)>>0) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<2) | ((c[i] & 2)<<2) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] &32)>>2) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] &16)>>4);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>2) | ((c[i] & 8)>>2);
}
c[i] = 0;
}
// data whitening (6 bit)
inline void prng6(char* inout, int size)
{
const char otp[] = {
//explicit mode
"cOGGg7CM2=b5a?<`i;T2of5jDAB=2DoQ9ko?h_RLQR4@Z\\`9jY\\PX89lHX8h_R]c_^@OB<0`W08ik?Mg>dQZf3kn5Je5R=R4h[<Ph90HHh9j;h:mS^?f:lQ:GG;nU:b?WFU20Lf4@A?`hYJMnW\\QZ\\AMIZ<h:jQk[PP<`6[Z"
#if 0
// implicit mode (offset 2 symbols)
"5^ZSm0=cOGMgUB=bNcb<@a^T;_f=6DEB]2ImPIKg:j]RlYT4YZ<`9hZ\\PPb;@8X8i]Zmc_6B52\\8oUPHIcBOc>dY?d9[n5Lg]b]R8hR<0`T008h9c9QJm[c?a:lQEGa;nU=b_WfUV2?V4@c=8h9B9njlQZDC@9Z<Q8\\iiX\\Rb6k:iY"
#endif
};
int i, maxchars;
maxchars = sizeof( otp );
if (size < maxchars)
maxchars = size;
for (i = 0; i < maxchars; i++)
inout[i] ^= (otp[i] - 48);
}
};
#endif // INCLUDE_LORADEMODSINK_H

View File

@ -1,56 +0,0 @@
#include <QtPlugin>
#include "plugin/pluginapi.h"
#include "loraplugin.h"
#include "lorademodgui.h"
#include "lorademod.h"
const PluginDescriptor LoRaPlugin::m_pluginDescriptor = {
LoRaDemod::m_channelId,
QString("LoRa Demodulator"),
QString("4.19.0"),
QString("(c) 2015 John Greb"),
QString("http://www.maintech.de"),
true,
QString("github.com/hexameron/rtl-sdrangelove")
};
LoRaPlugin::LoRaPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& LoRaPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void LoRaPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register demodulator
m_pluginAPI->registerRxChannel(LoRaDemod::m_channelIdURI, LoRaDemod::m_channelId, this);
}
void LoRaPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
LoRaDemod *instance = new LoRaDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
ChannelGUI* LoRaPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return LoRaDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}

View File

@ -1,30 +0,0 @@
#ifndef INCLUDE_LoRaPLUGIN_H
#define INCLUDE_LoRaPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class LoRaPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.lorademod")
public:
explicit LoRaPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_LoRaPLUGIN_H

View File

@ -17,6 +17,7 @@
<file>webapi/doc/swagger/include/ChannelAnalyzer.yaml</file>
<file>webapi/doc/swagger/include/ChannelSettings.yaml</file>
<file>webapi/doc/swagger/include/ChannelReport.yaml</file>
<file>webapi/doc/swagger/include/ChirpChatDemod.yaml</file>
<file>webapi/doc/swagger/include/Command.yaml</file>
<file>webapi/doc/swagger/include/CWKeyer.yaml</file>
<file>webapi/doc/swagger/include/DATVDemod.yaml</file>

View File

@ -2344,6 +2344,9 @@ margin-bottom: 20px;
"BFMDemodReport" : {
"$ref" : "#/definitions/BFMDemodReport"
},
"ChirpChatDemodReport" : {
"$ref" : "#/definitions/ChirpChatDemodReport"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -2439,6 +2442,9 @@ margin-bottom: 20px;
"ChannelAnalyzerSettings" : {
"$ref" : "#/definitions/ChannelAnalyzerSettings"
},
"ChirpChatDemodSettings" : {
"$ref" : "#/definitions/ChirpChatDemodSettings"
},
"DATVDemodSettings" : {
"$ref" : "#/definitions/DATVDemodSettings"
},
@ -2521,6 +2527,191 @@ margin-bottom: 20px;
}
},
"description" : "All channels detailed information"
};
defs.ChirpChatDemodReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "current de-chirped total channel power (dB)"
},
"noisePowerDB" : {
"type" : "number",
"format" : "float",
"description" : "current de-chirped noise argmax power (dB)"
},
"signalPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "last message de-chirped signal argmax power (dB)"
},
"snrPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "last message de-chirped signal to noise ratio power (dB)"
},
"channelSampleRate" : {
"type" : "integer"
},
"syncWord" : {
"type" : "integer",
"description" : "2 bytes sync word (0..65535)"
},
"hasCRC" : {
"type" : "integer",
"description" : "boolean 1 if payload CRC is present else 0 (LoRa)"
},
"nbParityBits" : {
"type" : "integer",
"description" : "Hamming FEC parity bits (LoRa)"
},
"packetLength" : {
"type" : "integer",
"description" : "Packet length in number of bytes (LoRa)"
},
"nbSymbols" : {
"type" : "integer",
"description" : "Number of symbols in the payload with header and CRC (LoRa)"
},
"nbCodewords" : {
"type" : "integer",
"description" : "Number of codewords in the payload with header and CRC (LoRa)"
},
"headerParityStatus" : {
"type" : "integer",
"description" : "Header FEC parity status:\n * 0 - Undefined\n * 1 - Uncorrectable error\n * 2 - Corrected error\n * 3 - OK\n"
},
"headerCRCStatus" : {
"type" : "integer",
"description" : "header CRC check status. Boolean 1 if OK else 0"
},
"payloadParityStatus" : {
"type" : "integer",
"description" : "Payload FEC parity status:\n * 0 - Undefined\n * 1 - Uncorrectable error\n * 2 - Corrected error\n * 3 - OK\n"
},
"payloadCRCStatus" : {
"type" : "integer",
"description" : "payload CRC check status. Boolean 1 if OK else 0"
},
"messageTimestamp" : {
"type" : "string",
"description" : "timestamp of the last decoded message"
},
"messageString" : {
"type" : "string",
"description" : "string representation of the last decoded message"
},
"messageBytes" : {
"type" : "array",
"description" : "bytes of the last decoded message as an array of hex string represented bytes (00..FF)",
"items" : {
"type" : "string"
}
}
},
"description" : "ChirpChatDemod"
};
defs.ChirpChatDemodSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"bandwidthIndex" : {
"type" : "integer",
"description" : "standard bandwidths index:\n * 0 - 375 Hz (384000 / 1024)\n * 1 - 750 Hz (384000 / 512)\n * 2 - 1500 Hz (384000 / 256)\n * 3 - 2604 Hz (333333 / 128)\n * 4 - 3125 Hz (400000 / 128)\n * 5 - 3906 Hz (500000 / 128)\n * 6 - 5208 Hz (333333 / 64)\n * 7 - 6250 Hz (400000 / 64)\n * 8 - 7813 Hz (500000 / 64)\n * 9 - 10417 Hz (333333 / 32)\n * 10 - 12500 Hz (400000 / 32)\n * 11 - 15625 Hz (500000 / 32)\n * 12 - 20833 Hz (333333 / 16)\n * 13 - 25000 Hz (400000 / 16)\n * 14 - 31250 Hz (500000 / 16)\n * 15 - 41667 Hz (333333 / 8)\n * 16 - 50000 Hz (400000 / 8)\n * 17 - 62500 Hz (500000 / 8)\n * 18 - 83333 Hz (333333 / 4)\n * 19 - 100000 Hz (400000 / 4)\n * 20 - 125000 Hz (500000 / 4)\n * 21 - 166667 Hz (333333 / 2)\n * 22 - 200000 Hz (400000 / 2)\n * 23 - 250000 Hz (500000 / 2)\n * 24 - 333333 Hz (333333 / 1)\n * 25 - 400000 Hz (400000 / 1)\n * 26 - 500000 Hz (500000 / 1)\n"
},
"spreadFactor" : {
"type" : "integer"
},
"deBits" : {
"type" : "integer",
"description" : "Low data rate optmize (DE) bits i.e. nb of FFT bins per effective symbol"
},
"fftWindow" : {
"type" : "integer",
"description" : "FFT Window index (FFTWindow::Function):\n * 0 - Bartlett\n * 1 - BlackmanHarris\n * 2 - Flattop\n * 3 - Hamming\n * 4 - Hanning\n * 5 - Rectangle\n * 6 - Kaiser\n"
},
"codingScheme" : {
"type" : "integer",
"description" : "message encoding scheme (ChirpChatDemodSettings::CodingScheme):\n * 0 - LoRa\n * 1 - Plain ASCII (7 bit)\n * 2 - Teletype (5 bit Baudot) a.k.a TTY\n"
},
"decodeActive" : {
"type" : "integer",
"description" : "boolean 1 to activate 0 to de-activate decoder"
},
"eomSquelchTenths" : {
"type" : "integer",
"description" : "argmax squared magnitude is compared between current multiplied by this factor and maximum during decoding. This value is divided by 10"
},
"nbSymbolsMax" : {
"type" : "integer",
"description" : "expected maximum number of symbols in a payload"
},
"autoNbSymbolsMax" : {
"type" : "integer",
"description" : "adjust maximum number of symbols in a payload to the value just received (LoRa)"
},
"preambleChirps" : {
"type" : "integer",
"description" : "Number of expected preamble chirps"
},
"nbParityBits" : {
"type" : "integer",
"description" : "Hamming FEC parity bits (LoRa)"
},
"packetLength" : {
"type" : "integer",
"description" : "expected packet length in number of bytes (LoRa)"
},
"hasCRC" : {
"type" : "integer",
"description" : "Payload has CRC (LoRa)"
},
"hasHeader" : {
"type" : "integer",
"description" : "Header present before actual payload (LoRa)"
},
"sendViaUDP" : {
"type" : "integer",
"description" : "boolean 1 to send decoded message via UDP else 0"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP destination udpAddress"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP destination properties"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "ChirpChatDemod"
};
defs.Command = {
"properties" : {
@ -40214,7 +40405,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2020-10-27T23:03:34.026+01:00
Generated 2020-11-09T11:07:00.865+01:00
</div>
</div>
</div>

View File

@ -21,6 +21,8 @@ ChannelReport:
$ref: "/doc/swagger/include/ATVMod.yaml#/ATVModReport"
BFMDemodReport:
$ref: "/doc/swagger/include/BFMDemod.yaml#/BFMDemodReport"
ChirpChatDemodReport:
$ref: "/doc/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodReport"
DSDDemodReport:
$ref: "/doc/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

View File

@ -31,6 +31,8 @@ ChannelSettings:
$ref: "/doc/swagger/include/BFMDemod.yaml#/BFMDemodSettings"
ChannelAnalyzerSettings:
$ref: "/doc/swagger/include/ChannelAnalyzer.yaml#/ChannelAnalyzerSettings"
ChirpChatDemodSettings:
$ref: "/doc/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodSettings"
DATVDemodSettings:
$ref: "/doc/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DSDDemodSettings:

View File

@ -0,0 +1,187 @@
ChirpChatDemodSettings:
description: ChirpChatDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
bandwidthIndex:
type: integer
description: >
standard bandwidths index:
* 0 - 375 Hz (384000 / 1024)
* 1 - 750 Hz (384000 / 512)
* 2 - 1500 Hz (384000 / 256)
* 3 - 2604 Hz (333333 / 128)
* 4 - 3125 Hz (400000 / 128)
* 5 - 3906 Hz (500000 / 128)
* 6 - 5208 Hz (333333 / 64)
* 7 - 6250 Hz (400000 / 64)
* 8 - 7813 Hz (500000 / 64)
* 9 - 10417 Hz (333333 / 32)
* 10 - 12500 Hz (400000 / 32)
* 11 - 15625 Hz (500000 / 32)
* 12 - 20833 Hz (333333 / 16)
* 13 - 25000 Hz (400000 / 16)
* 14 - 31250 Hz (500000 / 16)
* 15 - 41667 Hz (333333 / 8)
* 16 - 50000 Hz (400000 / 8)
* 17 - 62500 Hz (500000 / 8)
* 18 - 83333 Hz (333333 / 4)
* 19 - 100000 Hz (400000 / 4)
* 20 - 125000 Hz (500000 / 4)
* 21 - 166667 Hz (333333 / 2)
* 22 - 200000 Hz (400000 / 2)
* 23 - 250000 Hz (500000 / 2)
* 24 - 333333 Hz (333333 / 1)
* 25 - 400000 Hz (400000 / 1)
* 26 - 500000 Hz (500000 / 1)
spreadFactor:
type: integer
deBits:
description: Low data rate optmize (DE) bits i.e. nb of FFT bins per effective symbol
type: integer
fftWindow:
type: integer
description: >
FFT Window index (FFTWindow::Function):
* 0 - Bartlett
* 1 - BlackmanHarris
* 2 - Flattop
* 3 - Hamming
* 4 - Hanning
* 5 - Rectangle
* 6 - Kaiser
codingScheme:
type: integer
description: >
message encoding scheme (ChirpChatDemodSettings::CodingScheme):
* 0 - LoRa
* 1 - Plain ASCII (7 bit)
* 2 - Teletype (5 bit Baudot) a.k.a TTY
decodeActive:
description: boolean 1 to activate 0 to de-activate decoder
type: integer
eomSquelchTenths:
description: argmax squared magnitude is compared between current multiplied by this factor and maximum during decoding. This value is divided by 10
type: integer
nbSymbolsMax:
description: expected maximum number of symbols in a payload
type: integer
autoNbSymbolsMax:
description: adjust maximum number of symbols in a payload to the value just received (LoRa)
type: integer
preambleChirps:
description: Number of expected preamble chirps
type: integer
nbParityBits:
description: Hamming FEC parity bits (LoRa)
type: integer
packetLength:
description: expected packet length in number of bytes (LoRa)
type: integer
hasCRC:
description: Payload has CRC (LoRa)
type: integer
hasHeader:
description: Header present before actual payload (LoRa)
type: integer
sendViaUDP:
description: boolean 1 to send decoded message via UDP else 0
type: integer
udpAddress:
description: UDP destination udpAddress
type: string
udpPort:
description: UDP destination properties
type: integer
rgbColor:
type: integer
title:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
ChirpChatDemodReport:
description: ChirpChatDemod
properties:
channelPowerDB:
description: current de-chirped total channel power (dB)
type: number
format: float
noisePowerDB:
description: current de-chirped noise argmax power (dB)
type: number
format: float
signalPowerDB:
description: last message de-chirped signal argmax power (dB)
type: number
format: float
snrPowerDB:
description: last message de-chirped signal to noise ratio power (dB)
type: number
format: float
channelSampleRate:
type: integer
syncWord:
description: 2 bytes sync word (0..65535)
type: integer
hasCRC:
description: boolean 1 if payload CRC is present else 0 (LoRa)
type: integer
nbParityBits:
description: Hamming FEC parity bits (LoRa)
type: integer
packetLength:
description: Packet length in number of bytes (LoRa)
type: integer
nbSymbols:
description: Number of symbols in the payload with header and CRC (LoRa)
type: integer
nbCodewords:
description: Number of codewords in the payload with header and CRC (LoRa)
type: integer
headerParityStatus:
type: integer
description: >
Header FEC parity status:
* 0 - Undefined
* 1 - Uncorrectable error
* 2 - Corrected error
* 3 - OK
headerCRCStatus:
description: header CRC check status. Boolean 1 if OK else 0
type: integer
payloadParityStatus:
type: integer
description: >
Payload FEC parity status:
* 0 - Undefined
* 1 - Uncorrectable error
* 2 - Corrected error
* 3 - OK
payloadCRCStatus:
description: payload CRC check status. Boolean 1 if OK else 0
type: integer
messageTimestamp:
description: timestamp of the last decoded message
type: string
messageString:
description: string representation of the last decoded message
type: string
messageBytes:
description: bytes of the last decoded message as an array of hex string represented bytes (00..FF)
type: array
items:
type: string

View File

@ -72,7 +72,7 @@ public:
m_socket->moveToThread(thread);
}
void setAddress(QString& address) { m_address.setAddress(address); }
void setAddress(const QString& address) { m_address.setAddress(address); }
void setPort(unsigned int port) { m_port = port; }
void setDestination(const QString& address, int port)
@ -125,6 +125,14 @@ public:
memcpy(&m_sampleBuffer[m_sampleBufferIndex], &samples[samplesIndex], nbSamples*sizeof(T)); // copy remainder of input to buffer
}
/**
* Write a bunch of samples unbuffered
*/
void writeUnbuffered(const T *samples, int nbSamples)
{
m_socket->writeDatagram((const char*)samples, (qint64 ) nbSamples, m_address, m_port); // send given samples
}
private:
int m_udpSize;
int m_udpSamples;

View File

@ -3559,6 +3559,11 @@ bool WebAPIRequestMapper::getChannelSettings(
{
processChannelAnalyzerSettings(channelSettings, settingsJsonObject, channelSettingsKeys);
}
else if (channelSettingsKey == "ChirpChatDemodSettings")
{
channelSettings->setChirpChatDemodSettings(new SWGSDRangel::SWGChirpChatDemodSettings());
channelSettings->getChirpChatDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DATVDemodSettings")
{
channelSettings->setDatvDemodSettings(new SWGSDRangel::SWGDATVDemodSettings());

View File

@ -21,6 +21,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/ATVMod.yaml#/ATVModReport"
BFMDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/BFMDemod.yaml#/BFMDemodReport"
ChirpChatDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodReport"
DSDDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

View File

@ -31,6 +31,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/BFMDemod.yaml#/BFMDemodSettings"
ChannelAnalyzerSettings:
$ref: "http://swgserver:8081/api/swagger/include/ChannelAnalyzer.yaml#/ChannelAnalyzerSettings"
ChirpChatDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodSettings"
DATVDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DSDDemodSettings:

View File

@ -0,0 +1,187 @@
ChirpChatDemodSettings:
description: ChirpChatDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
bandwidthIndex:
type: integer
description: >
standard bandwidths index:
* 0 - 375 Hz (384000 / 1024)
* 1 - 750 Hz (384000 / 512)
* 2 - 1500 Hz (384000 / 256)
* 3 - 2604 Hz (333333 / 128)
* 4 - 3125 Hz (400000 / 128)
* 5 - 3906 Hz (500000 / 128)
* 6 - 5208 Hz (333333 / 64)
* 7 - 6250 Hz (400000 / 64)
* 8 - 7813 Hz (500000 / 64)
* 9 - 10417 Hz (333333 / 32)
* 10 - 12500 Hz (400000 / 32)
* 11 - 15625 Hz (500000 / 32)
* 12 - 20833 Hz (333333 / 16)
* 13 - 25000 Hz (400000 / 16)
* 14 - 31250 Hz (500000 / 16)
* 15 - 41667 Hz (333333 / 8)
* 16 - 50000 Hz (400000 / 8)
* 17 - 62500 Hz (500000 / 8)
* 18 - 83333 Hz (333333 / 4)
* 19 - 100000 Hz (400000 / 4)
* 20 - 125000 Hz (500000 / 4)
* 21 - 166667 Hz (333333 / 2)
* 22 - 200000 Hz (400000 / 2)
* 23 - 250000 Hz (500000 / 2)
* 24 - 333333 Hz (333333 / 1)
* 25 - 400000 Hz (400000 / 1)
* 26 - 500000 Hz (500000 / 1)
spreadFactor:
type: integer
deBits:
description: Low data rate optmize (DE) bits i.e. nb of FFT bins per effective symbol
type: integer
fftWindow:
type: integer
description: >
FFT Window index (FFTWindow::Function):
* 0 - Bartlett
* 1 - BlackmanHarris
* 2 - Flattop
* 3 - Hamming
* 4 - Hanning
* 5 - Rectangle
* 6 - Kaiser
codingScheme:
type: integer
description: >
message encoding scheme (ChirpChatDemodSettings::CodingScheme):
* 0 - LoRa
* 1 - Plain ASCII (7 bit)
* 2 - Teletype (5 bit Baudot) a.k.a TTY
decodeActive:
description: boolean 1 to activate 0 to de-activate decoder
type: integer
eomSquelchTenths:
description: argmax squared magnitude is compared between current multiplied by this factor and maximum during decoding. This value is divided by 10
type: integer
nbSymbolsMax:
description: expected maximum number of symbols in a payload
type: integer
autoNbSymbolsMax:
description: adjust maximum number of symbols in a payload to the value just received (LoRa)
type: integer
preambleChirps:
description: Number of expected preamble chirps
type: integer
nbParityBits:
description: Hamming FEC parity bits (LoRa)
type: integer
packetLength:
description: expected packet length in number of bytes (LoRa)
type: integer
hasCRC:
description: Payload has CRC (LoRa)
type: integer
hasHeader:
description: Header present before actual payload (LoRa)
type: integer
sendViaUDP:
description: boolean 1 to send decoded message via UDP else 0
type: integer
udpAddress:
description: UDP destination udpAddress
type: string
udpPort:
description: UDP destination properties
type: integer
rgbColor:
type: integer
title:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
ChirpChatDemodReport:
description: ChirpChatDemod
properties:
channelPowerDB:
description: current de-chirped total channel power (dB)
type: number
format: float
noisePowerDB:
description: current de-chirped noise argmax power (dB)
type: number
format: float
signalPowerDB:
description: last message de-chirped signal argmax power (dB)
type: number
format: float
snrPowerDB:
description: last message de-chirped signal to noise ratio power (dB)
type: number
format: float
channelSampleRate:
type: integer
syncWord:
description: 2 bytes sync word (0..65535)
type: integer
hasCRC:
description: boolean 1 if payload CRC is present else 0 (LoRa)
type: integer
nbParityBits:
description: Hamming FEC parity bits (LoRa)
type: integer
packetLength:
description: Packet length in number of bytes (LoRa)
type: integer
nbSymbols:
description: Number of symbols in the payload with header and CRC (LoRa)
type: integer
nbCodewords:
description: Number of codewords in the payload with header and CRC (LoRa)
type: integer
headerParityStatus:
type: integer
description: >
Header FEC parity status:
* 0 - Undefined
* 1 - Uncorrectable error
* 2 - Corrected error
* 3 - OK
headerCRCStatus:
description: header CRC check status. Boolean 1 if OK else 0
type: integer
payloadParityStatus:
type: integer
description: >
Payload FEC parity status:
* 0 - Undefined
* 1 - Uncorrectable error
* 2 - Corrected error
* 3 - OK
payloadCRCStatus:
description: payload CRC check status. Boolean 1 if OK else 0
type: integer
messageTimestamp:
description: timestamp of the last decoded message
type: string
messageString:
description: string representation of the last decoded message
type: string
messageBytes:
description: bytes of the last decoded message as an array of hex string represented bytes (00..FF)
type: array
items:
type: string

View File

@ -2344,6 +2344,9 @@ margin-bottom: 20px;
"BFMDemodReport" : {
"$ref" : "#/definitions/BFMDemodReport"
},
"ChirpChatDemodReport" : {
"$ref" : "#/definitions/ChirpChatDemodReport"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -2439,6 +2442,9 @@ margin-bottom: 20px;
"ChannelAnalyzerSettings" : {
"$ref" : "#/definitions/ChannelAnalyzerSettings"
},
"ChirpChatDemodSettings" : {
"$ref" : "#/definitions/ChirpChatDemodSettings"
},
"DATVDemodSettings" : {
"$ref" : "#/definitions/DATVDemodSettings"
},
@ -2521,6 +2527,191 @@ margin-bottom: 20px;
}
},
"description" : "All channels detailed information"
};
defs.ChirpChatDemodReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "current de-chirped total channel power (dB)"
},
"noisePowerDB" : {
"type" : "number",
"format" : "float",
"description" : "current de-chirped noise argmax power (dB)"
},
"signalPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "last message de-chirped signal argmax power (dB)"
},
"snrPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "last message de-chirped signal to noise ratio power (dB)"
},
"channelSampleRate" : {
"type" : "integer"
},
"syncWord" : {
"type" : "integer",
"description" : "2 bytes sync word (0..65535)"
},
"hasCRC" : {
"type" : "integer",
"description" : "boolean 1 if payload CRC is present else 0 (LoRa)"
},
"nbParityBits" : {
"type" : "integer",
"description" : "Hamming FEC parity bits (LoRa)"
},
"packetLength" : {
"type" : "integer",
"description" : "Packet length in number of bytes (LoRa)"
},
"nbSymbols" : {
"type" : "integer",
"description" : "Number of symbols in the payload with header and CRC (LoRa)"
},
"nbCodewords" : {
"type" : "integer",
"description" : "Number of codewords in the payload with header and CRC (LoRa)"
},
"headerParityStatus" : {
"type" : "integer",
"description" : "Header FEC parity status:\n * 0 - Undefined\n * 1 - Uncorrectable error\n * 2 - Corrected error\n * 3 - OK\n"
},
"headerCRCStatus" : {
"type" : "integer",
"description" : "header CRC check status. Boolean 1 if OK else 0"
},
"payloadParityStatus" : {
"type" : "integer",
"description" : "Payload FEC parity status:\n * 0 - Undefined\n * 1 - Uncorrectable error\n * 2 - Corrected error\n * 3 - OK\n"
},
"payloadCRCStatus" : {
"type" : "integer",
"description" : "payload CRC check status. Boolean 1 if OK else 0"
},
"messageTimestamp" : {
"type" : "string",
"description" : "timestamp of the last decoded message"
},
"messageString" : {
"type" : "string",
"description" : "string representation of the last decoded message"
},
"messageBytes" : {
"type" : "array",
"description" : "bytes of the last decoded message as an array of hex string represented bytes (00..FF)",
"items" : {
"type" : "string"
}
}
},
"description" : "ChirpChatDemod"
};
defs.ChirpChatDemodSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"bandwidthIndex" : {
"type" : "integer",
"description" : "standard bandwidths index:\n * 0 - 375 Hz (384000 / 1024)\n * 1 - 750 Hz (384000 / 512)\n * 2 - 1500 Hz (384000 / 256)\n * 3 - 2604 Hz (333333 / 128)\n * 4 - 3125 Hz (400000 / 128)\n * 5 - 3906 Hz (500000 / 128)\n * 6 - 5208 Hz (333333 / 64)\n * 7 - 6250 Hz (400000 / 64)\n * 8 - 7813 Hz (500000 / 64)\n * 9 - 10417 Hz (333333 / 32)\n * 10 - 12500 Hz (400000 / 32)\n * 11 - 15625 Hz (500000 / 32)\n * 12 - 20833 Hz (333333 / 16)\n * 13 - 25000 Hz (400000 / 16)\n * 14 - 31250 Hz (500000 / 16)\n * 15 - 41667 Hz (333333 / 8)\n * 16 - 50000 Hz (400000 / 8)\n * 17 - 62500 Hz (500000 / 8)\n * 18 - 83333 Hz (333333 / 4)\n * 19 - 100000 Hz (400000 / 4)\n * 20 - 125000 Hz (500000 / 4)\n * 21 - 166667 Hz (333333 / 2)\n * 22 - 200000 Hz (400000 / 2)\n * 23 - 250000 Hz (500000 / 2)\n * 24 - 333333 Hz (333333 / 1)\n * 25 - 400000 Hz (400000 / 1)\n * 26 - 500000 Hz (500000 / 1)\n"
},
"spreadFactor" : {
"type" : "integer"
},
"deBits" : {
"type" : "integer",
"description" : "Low data rate optmize (DE) bits i.e. nb of FFT bins per effective symbol"
},
"fftWindow" : {
"type" : "integer",
"description" : "FFT Window index (FFTWindow::Function):\n * 0 - Bartlett\n * 1 - BlackmanHarris\n * 2 - Flattop\n * 3 - Hamming\n * 4 - Hanning\n * 5 - Rectangle\n * 6 - Kaiser\n"
},
"codingScheme" : {
"type" : "integer",
"description" : "message encoding scheme (ChirpChatDemodSettings::CodingScheme):\n * 0 - LoRa\n * 1 - Plain ASCII (7 bit)\n * 2 - Teletype (5 bit Baudot) a.k.a TTY\n"
},
"decodeActive" : {
"type" : "integer",
"description" : "boolean 1 to activate 0 to de-activate decoder"
},
"eomSquelchTenths" : {
"type" : "integer",
"description" : "argmax squared magnitude is compared between current multiplied by this factor and maximum during decoding. This value is divided by 10"
},
"nbSymbolsMax" : {
"type" : "integer",
"description" : "expected maximum number of symbols in a payload"
},
"autoNbSymbolsMax" : {
"type" : "integer",
"description" : "adjust maximum number of symbols in a payload to the value just received (LoRa)"
},
"preambleChirps" : {
"type" : "integer",
"description" : "Number of expected preamble chirps"
},
"nbParityBits" : {
"type" : "integer",
"description" : "Hamming FEC parity bits (LoRa)"
},
"packetLength" : {
"type" : "integer",
"description" : "expected packet length in number of bytes (LoRa)"
},
"hasCRC" : {
"type" : "integer",
"description" : "Payload has CRC (LoRa)"
},
"hasHeader" : {
"type" : "integer",
"description" : "Header present before actual payload (LoRa)"
},
"sendViaUDP" : {
"type" : "integer",
"description" : "boolean 1 to send decoded message via UDP else 0"
},
"udpAddress" : {
"type" : "string",
"description" : "UDP destination udpAddress"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP destination properties"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "ChirpChatDemod"
};
defs.Command = {
"properties" : {
@ -40214,7 +40405,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2020-10-27T23:03:34.026+01:00
Generated 2020-11-09T11:07:00.865+01:00
</div>
</div>
</div>

View File

@ -42,6 +42,8 @@ SWGChannelReport::SWGChannelReport() {
m_atv_mod_report_isSet = false;
bfm_demod_report = nullptr;
m_bfm_demod_report_isSet = false;
chirp_chat_demod_report = nullptr;
m_chirp_chat_demod_report_isSet = false;
dsd_demod_report = nullptr;
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = nullptr;
@ -98,6 +100,8 @@ SWGChannelReport::init() {
m_atv_mod_report_isSet = false;
bfm_demod_report = new SWGBFMDemodReport();
m_bfm_demod_report_isSet = false;
chirp_chat_demod_report = new SWGChirpChatDemodReport();
m_chirp_chat_demod_report_isSet = false;
dsd_demod_report = new SWGDSDDemodReport();
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = new SWGIEEE_802_15_4_ModReport();
@ -155,6 +159,9 @@ SWGChannelReport::cleanup() {
if(bfm_demod_report != nullptr) {
delete bfm_demod_report;
}
if(chirp_chat_demod_report != nullptr) {
delete chirp_chat_demod_report;
}
if(dsd_demod_report != nullptr) {
delete dsd_demod_report;
}
@ -233,6 +240,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&bfm_demod_report, pJson["BFMDemodReport"], "SWGBFMDemodReport", "SWGBFMDemodReport");
::SWGSDRangel::setValue(&chirp_chat_demod_report, pJson["ChirpChatDemodReport"], "SWGChirpChatDemodReport", "SWGChirpChatDemodReport");
::SWGSDRangel::setValue(&dsd_demod_report, pJson["DSDDemodReport"], "SWGDSDDemodReport", "SWGDSDDemodReport");
::SWGSDRangel::setValue(&ieee_802_15_4_mod_report, pJson["IEEE_802_15_4_ModReport"], "SWGIEEE_802_15_4_ModReport", "SWGIEEE_802_15_4_ModReport");
@ -304,6 +313,9 @@ SWGChannelReport::asJsonObject() {
if((bfm_demod_report != nullptr) && (bfm_demod_report->isSet())){
toJsonValue(QString("BFMDemodReport"), bfm_demod_report, obj, QString("SWGBFMDemodReport"));
}
if((chirp_chat_demod_report != nullptr) && (chirp_chat_demod_report->isSet())){
toJsonValue(QString("ChirpChatDemodReport"), chirp_chat_demod_report, obj, QString("SWGChirpChatDemodReport"));
}
if((dsd_demod_report != nullptr) && (dsd_demod_report->isSet())){
toJsonValue(QString("DSDDemodReport"), dsd_demod_report, obj, QString("SWGDSDDemodReport"));
}
@ -429,6 +441,16 @@ SWGChannelReport::setBfmDemodReport(SWGBFMDemodReport* bfm_demod_report) {
this->m_bfm_demod_report_isSet = true;
}
SWGChirpChatDemodReport*
SWGChannelReport::getChirpChatDemodReport() {
return chirp_chat_demod_report;
}
void
SWGChannelReport::setChirpChatDemodReport(SWGChirpChatDemodReport* chirp_chat_demod_report) {
this->chirp_chat_demod_report = chirp_chat_demod_report;
this->m_chirp_chat_demod_report_isSet = true;
}
SWGDSDDemodReport*
SWGChannelReport::getDsdDemodReport() {
return dsd_demod_report;
@ -625,6 +647,9 @@ SWGChannelReport::isSet(){
if(bfm_demod_report && bfm_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(chirp_chat_demod_report && chirp_chat_demod_report->isSet()){
isObjectUpdated = true; break;
}
if(dsd_demod_report && dsd_demod_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -27,6 +27,7 @@
#include "SWGAMModReport.h"
#include "SWGATVModReport.h"
#include "SWGBFMDemodReport.h"
#include "SWGChirpChatDemodReport.h"
#include "SWGDSDDemodReport.h"
#include "SWGFileSinkReport.h"
#include "SWGFileSourceReport.h"
@ -85,6 +86,9 @@ public:
SWGBFMDemodReport* getBfmDemodReport();
void setBfmDemodReport(SWGBFMDemodReport* bfm_demod_report);
SWGChirpChatDemodReport* getChirpChatDemodReport();
void setChirpChatDemodReport(SWGChirpChatDemodReport* chirp_chat_demod_report);
SWGDSDDemodReport* getDsdDemodReport();
void setDsdDemodReport(SWGDSDDemodReport* dsd_demod_report);
@ -161,6 +165,9 @@ private:
SWGBFMDemodReport* bfm_demod_report;
bool m_bfm_demod_report_isSet;
SWGChirpChatDemodReport* chirp_chat_demod_report;
bool m_chirp_chat_demod_report_isSet;
SWGDSDDemodReport* dsd_demod_report;
bool m_dsd_demod_report_isSet;

View File

@ -50,6 +50,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_bfm_demod_settings_isSet = false;
channel_analyzer_settings = nullptr;
m_channel_analyzer_settings_isSet = false;
chirp_chat_demod_settings = nullptr;
m_chirp_chat_demod_settings_isSet = false;
datv_demod_settings = nullptr;
m_datv_demod_settings_isSet = false;
dsd_demod_settings = nullptr;
@ -122,6 +124,8 @@ SWGChannelSettings::init() {
m_bfm_demod_settings_isSet = false;
channel_analyzer_settings = new SWGChannelAnalyzerSettings();
m_channel_analyzer_settings_isSet = false;
chirp_chat_demod_settings = new SWGChirpChatDemodSettings();
m_chirp_chat_demod_settings_isSet = false;
datv_demod_settings = new SWGDATVDemodSettings();
m_datv_demod_settings_isSet = false;
dsd_demod_settings = new SWGDSDDemodSettings();
@ -195,6 +199,9 @@ SWGChannelSettings::cleanup() {
if(channel_analyzer_settings != nullptr) {
delete channel_analyzer_settings;
}
if(chirp_chat_demod_settings != nullptr) {
delete chirp_chat_demod_settings;
}
if(datv_demod_settings != nullptr) {
delete datv_demod_settings;
}
@ -293,6 +300,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_analyzer_settings, pJson["ChannelAnalyzerSettings"], "SWGChannelAnalyzerSettings", "SWGChannelAnalyzerSettings");
::SWGSDRangel::setValue(&chirp_chat_demod_settings, pJson["ChirpChatDemodSettings"], "SWGChirpChatDemodSettings", "SWGChirpChatDemodSettings");
::SWGSDRangel::setValue(&datv_demod_settings, pJson["DATVDemodSettings"], "SWGDATVDemodSettings", "SWGDATVDemodSettings");
::SWGSDRangel::setValue(&dsd_demod_settings, pJson["DSDDemodSettings"], "SWGDSDDemodSettings", "SWGDSDDemodSettings");
@ -384,6 +393,9 @@ SWGChannelSettings::asJsonObject() {
if((channel_analyzer_settings != nullptr) && (channel_analyzer_settings->isSet())){
toJsonValue(QString("ChannelAnalyzerSettings"), channel_analyzer_settings, obj, QString("SWGChannelAnalyzerSettings"));
}
if((chirp_chat_demod_settings != nullptr) && (chirp_chat_demod_settings->isSet())){
toJsonValue(QString("ChirpChatDemodSettings"), chirp_chat_demod_settings, obj, QString("SWGChirpChatDemodSettings"));
}
if((datv_demod_settings != nullptr) && (datv_demod_settings->isSet())){
toJsonValue(QString("DATVDemodSettings"), datv_demod_settings, obj, QString("SWGDATVDemodSettings"));
}
@ -561,6 +573,16 @@ SWGChannelSettings::setChannelAnalyzerSettings(SWGChannelAnalyzerSettings* chann
this->m_channel_analyzer_settings_isSet = true;
}
SWGChirpChatDemodSettings*
SWGChannelSettings::getChirpChatDemodSettings() {
return chirp_chat_demod_settings;
}
void
SWGChannelSettings::setChirpChatDemodSettings(SWGChirpChatDemodSettings* chirp_chat_demod_settings) {
this->chirp_chat_demod_settings = chirp_chat_demod_settings;
this->m_chirp_chat_demod_settings_isSet = true;
}
SWGDATVDemodSettings*
SWGChannelSettings::getDatvDemodSettings() {
return datv_demod_settings;
@ -809,6 +831,9 @@ SWGChannelSettings::isSet(){
if(channel_analyzer_settings && channel_analyzer_settings->isSet()){
isObjectUpdated = true; break;
}
if(chirp_chat_demod_settings && chirp_chat_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(datv_demod_settings && datv_demod_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -29,6 +29,7 @@
#include "SWGATVModSettings.h"
#include "SWGBFMDemodSettings.h"
#include "SWGChannelAnalyzerSettings.h"
#include "SWGChirpChatDemodSettings.h"
#include "SWGDATVDemodSettings.h"
#include "SWGDSDDemodSettings.h"
#include "SWGFileSinkSettings.h"
@ -103,6 +104,9 @@ public:
SWGChannelAnalyzerSettings* getChannelAnalyzerSettings();
void setChannelAnalyzerSettings(SWGChannelAnalyzerSettings* channel_analyzer_settings);
SWGChirpChatDemodSettings* getChirpChatDemodSettings();
void setChirpChatDemodSettings(SWGChirpChatDemodSettings* chirp_chat_demod_settings);
SWGDATVDemodSettings* getDatvDemodSettings();
void setDatvDemodSettings(SWGDATVDemodSettings* datv_demod_settings);
@ -203,6 +207,9 @@ private:
SWGChannelAnalyzerSettings* channel_analyzer_settings;
bool m_channel_analyzer_settings_isSet;
SWGChirpChatDemodSettings* chirp_chat_demod_settings;
bool m_chirp_chat_demod_settings_isSet;
SWGDATVDemodSettings* datv_demod_settings;
bool m_datv_demod_settings_isSet;

View File

@ -0,0 +1,509 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1, USRP and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.15.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGChirpChatDemodReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGChirpChatDemodReport::SWGChirpChatDemodReport(QString* json) {
init();
this->fromJson(*json);
}
SWGChirpChatDemodReport::SWGChirpChatDemodReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
noise_power_db = 0.0f;
m_noise_power_db_isSet = false;
signal_power_db = 0.0f;
m_signal_power_db_isSet = false;
snr_power_db = 0.0f;
m_snr_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
sync_word = 0;
m_sync_word_isSet = false;
has_crc = 0;
m_has_crc_isSet = false;
nb_parity_bits = 0;
m_nb_parity_bits_isSet = false;
packet_length = 0;
m_packet_length_isSet = false;
nb_symbols = 0;
m_nb_symbols_isSet = false;
nb_codewords = 0;
m_nb_codewords_isSet = false;
header_parity_status = 0;
m_header_parity_status_isSet = false;
header_crc_status = 0;
m_header_crc_status_isSet = false;
payload_parity_status = 0;
m_payload_parity_status_isSet = false;
payload_crc_status = 0;
m_payload_crc_status_isSet = false;
message_timestamp = nullptr;
m_message_timestamp_isSet = false;
message_string = nullptr;
m_message_string_isSet = false;
message_bytes = nullptr;
m_message_bytes_isSet = false;
}
SWGChirpChatDemodReport::~SWGChirpChatDemodReport() {
this->cleanup();
}
void
SWGChirpChatDemodReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
noise_power_db = 0.0f;
m_noise_power_db_isSet = false;
signal_power_db = 0.0f;
m_signal_power_db_isSet = false;
snr_power_db = 0.0f;
m_snr_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
sync_word = 0;
m_sync_word_isSet = false;
has_crc = 0;
m_has_crc_isSet = false;
nb_parity_bits = 0;
m_nb_parity_bits_isSet = false;
packet_length = 0;
m_packet_length_isSet = false;
nb_symbols = 0;
m_nb_symbols_isSet = false;
nb_codewords = 0;
m_nb_codewords_isSet = false;
header_parity_status = 0;
m_header_parity_status_isSet = false;
header_crc_status = 0;
m_header_crc_status_isSet = false;
payload_parity_status = 0;
m_payload_parity_status_isSet = false;
payload_crc_status = 0;
m_payload_crc_status_isSet = false;
message_timestamp = new QString("");
m_message_timestamp_isSet = false;
message_string = new QString("");
m_message_string_isSet = false;
message_bytes = new QList<QString*>();
m_message_bytes_isSet = false;
}
void
SWGChirpChatDemodReport::cleanup() {
if(message_timestamp != nullptr) {
delete message_timestamp;
}
if(message_string != nullptr) {
delete message_string;
}
if(message_bytes != nullptr) {
auto arr = message_bytes;
for(auto o: *arr) {
delete o;
}
delete message_bytes;
}
}
SWGChirpChatDemodReport*
SWGChirpChatDemodReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGChirpChatDemodReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&noise_power_db, pJson["noisePowerDB"], "float", "");
::SWGSDRangel::setValue(&signal_power_db, pJson["signalPowerDB"], "float", "");
::SWGSDRangel::setValue(&snr_power_db, pJson["snrPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
::SWGSDRangel::setValue(&sync_word, pJson["syncWord"], "qint32", "");
::SWGSDRangel::setValue(&has_crc, pJson["hasCRC"], "qint32", "");
::SWGSDRangel::setValue(&nb_parity_bits, pJson["nbParityBits"], "qint32", "");
::SWGSDRangel::setValue(&packet_length, pJson["packetLength"], "qint32", "");
::SWGSDRangel::setValue(&nb_symbols, pJson["nbSymbols"], "qint32", "");
::SWGSDRangel::setValue(&nb_codewords, pJson["nbCodewords"], "qint32", "");
::SWGSDRangel::setValue(&header_parity_status, pJson["headerParityStatus"], "qint32", "");
::SWGSDRangel::setValue(&header_crc_status, pJson["headerCRCStatus"], "qint32", "");
::SWGSDRangel::setValue(&payload_parity_status, pJson["payloadParityStatus"], "qint32", "");
::SWGSDRangel::setValue(&payload_crc_status, pJson["payloadCRCStatus"], "qint32", "");
::SWGSDRangel::setValue(&message_timestamp, pJson["messageTimestamp"], "QString", "QString");
::SWGSDRangel::setValue(&message_string, pJson["messageString"], "QString", "QString");
::SWGSDRangel::setValue(&message_bytes, pJson["messageBytes"], "QList", "QString");
}
QString
SWGChirpChatDemodReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGChirpChatDemodReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_noise_power_db_isSet){
obj->insert("noisePowerDB", QJsonValue(noise_power_db));
}
if(m_signal_power_db_isSet){
obj->insert("signalPowerDB", QJsonValue(signal_power_db));
}
if(m_snr_power_db_isSet){
obj->insert("snrPowerDB", QJsonValue(snr_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
if(m_sync_word_isSet){
obj->insert("syncWord", QJsonValue(sync_word));
}
if(m_has_crc_isSet){
obj->insert("hasCRC", QJsonValue(has_crc));
}
if(m_nb_parity_bits_isSet){
obj->insert("nbParityBits", QJsonValue(nb_parity_bits));
}
if(m_packet_length_isSet){
obj->insert("packetLength", QJsonValue(packet_length));
}
if(m_nb_symbols_isSet){
obj->insert("nbSymbols", QJsonValue(nb_symbols));
}
if(m_nb_codewords_isSet){
obj->insert("nbCodewords", QJsonValue(nb_codewords));
}
if(m_header_parity_status_isSet){
obj->insert("headerParityStatus", QJsonValue(header_parity_status));
}
if(m_header_crc_status_isSet){
obj->insert("headerCRCStatus", QJsonValue(header_crc_status));
}
if(m_payload_parity_status_isSet){
obj->insert("payloadParityStatus", QJsonValue(payload_parity_status));
}
if(m_payload_crc_status_isSet){
obj->insert("payloadCRCStatus", QJsonValue(payload_crc_status));
}
if(message_timestamp != nullptr && *message_timestamp != QString("")){
toJsonValue(QString("messageTimestamp"), message_timestamp, obj, QString("QString"));
}
if(message_string != nullptr && *message_string != QString("")){
toJsonValue(QString("messageString"), message_string, obj, QString("QString"));
}
if(message_bytes && message_bytes->size() > 0){
toJsonArray((QList<void*>*)message_bytes, obj, "messageBytes", "QString");
}
return obj;
}
float
SWGChirpChatDemodReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGChirpChatDemodReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
float
SWGChirpChatDemodReport::getNoisePowerDb() {
return noise_power_db;
}
void
SWGChirpChatDemodReport::setNoisePowerDb(float noise_power_db) {
this->noise_power_db = noise_power_db;
this->m_noise_power_db_isSet = true;
}
float
SWGChirpChatDemodReport::getSignalPowerDb() {
return signal_power_db;
}
void
SWGChirpChatDemodReport::setSignalPowerDb(float signal_power_db) {
this->signal_power_db = signal_power_db;
this->m_signal_power_db_isSet = true;
}
float
SWGChirpChatDemodReport::getSnrPowerDb() {
return snr_power_db;
}
void
SWGChirpChatDemodReport::setSnrPowerDb(float snr_power_db) {
this->snr_power_db = snr_power_db;
this->m_snr_power_db_isSet = true;
}
qint32
SWGChirpChatDemodReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGChirpChatDemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
qint32
SWGChirpChatDemodReport::getSyncWord() {
return sync_word;
}
void
SWGChirpChatDemodReport::setSyncWord(qint32 sync_word) {
this->sync_word = sync_word;
this->m_sync_word_isSet = true;
}
qint32
SWGChirpChatDemodReport::getHasCrc() {
return has_crc;
}
void
SWGChirpChatDemodReport::setHasCrc(qint32 has_crc) {
this->has_crc = has_crc;
this->m_has_crc_isSet = true;
}
qint32
SWGChirpChatDemodReport::getNbParityBits() {
return nb_parity_bits;
}
void
SWGChirpChatDemodReport::setNbParityBits(qint32 nb_parity_bits) {
this->nb_parity_bits = nb_parity_bits;
this->m_nb_parity_bits_isSet = true;
}
qint32
SWGChirpChatDemodReport::getPacketLength() {
return packet_length;
}
void
SWGChirpChatDemodReport::setPacketLength(qint32 packet_length) {
this->packet_length = packet_length;
this->m_packet_length_isSet = true;
}
qint32
SWGChirpChatDemodReport::getNbSymbols() {
return nb_symbols;
}
void
SWGChirpChatDemodReport::setNbSymbols(qint32 nb_symbols) {
this->nb_symbols = nb_symbols;
this->m_nb_symbols_isSet = true;
}
qint32
SWGChirpChatDemodReport::getNbCodewords() {
return nb_codewords;
}
void
SWGChirpChatDemodReport::setNbCodewords(qint32 nb_codewords) {
this->nb_codewords = nb_codewords;
this->m_nb_codewords_isSet = true;
}
qint32
SWGChirpChatDemodReport::getHeaderParityStatus() {
return header_parity_status;
}
void
SWGChirpChatDemodReport::setHeaderParityStatus(qint32 header_parity_status) {
this->header_parity_status = header_parity_status;
this->m_header_parity_status_isSet = true;
}
qint32
SWGChirpChatDemodReport::getHeaderCrcStatus() {
return header_crc_status;
}
void
SWGChirpChatDemodReport::setHeaderCrcStatus(qint32 header_crc_status) {
this->header_crc_status = header_crc_status;
this->m_header_crc_status_isSet = true;
}
qint32
SWGChirpChatDemodReport::getPayloadParityStatus() {
return payload_parity_status;
}
void
SWGChirpChatDemodReport::setPayloadParityStatus(qint32 payload_parity_status) {
this->payload_parity_status = payload_parity_status;
this->m_payload_parity_status_isSet = true;
}
qint32
SWGChirpChatDemodReport::getPayloadCrcStatus() {
return payload_crc_status;
}
void
SWGChirpChatDemodReport::setPayloadCrcStatus(qint32 payload_crc_status) {
this->payload_crc_status = payload_crc_status;
this->m_payload_crc_status_isSet = true;
}
QString*
SWGChirpChatDemodReport::getMessageTimestamp() {
return message_timestamp;
}
void
SWGChirpChatDemodReport::setMessageTimestamp(QString* message_timestamp) {
this->message_timestamp = message_timestamp;
this->m_message_timestamp_isSet = true;
}
QString*
SWGChirpChatDemodReport::getMessageString() {
return message_string;
}
void
SWGChirpChatDemodReport::setMessageString(QString* message_string) {
this->message_string = message_string;
this->m_message_string_isSet = true;
}
QList<QString*>*
SWGChirpChatDemodReport::getMessageBytes() {
return message_bytes;
}
void
SWGChirpChatDemodReport::setMessageBytes(QList<QString*>* message_bytes) {
this->message_bytes = message_bytes;
this->m_message_bytes_isSet = true;
}
bool
SWGChirpChatDemodReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_noise_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_signal_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_snr_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
if(m_sync_word_isSet){
isObjectUpdated = true; break;
}
if(m_has_crc_isSet){
isObjectUpdated = true; break;
}
if(m_nb_parity_bits_isSet){
isObjectUpdated = true; break;
}
if(m_packet_length_isSet){
isObjectUpdated = true; break;
}
if(m_nb_symbols_isSet){
isObjectUpdated = true; break;
}
if(m_nb_codewords_isSet){
isObjectUpdated = true; break;
}
if(m_header_parity_status_isSet){
isObjectUpdated = true; break;
}
if(m_header_crc_status_isSet){
isObjectUpdated = true; break;
}
if(m_payload_parity_status_isSet){
isObjectUpdated = true; break;
}
if(m_payload_crc_status_isSet){
isObjectUpdated = true; break;
}
if(message_timestamp && *message_timestamp != QString("")){
isObjectUpdated = true; break;
}
if(message_string && *message_string != QString("")){
isObjectUpdated = true; break;
}
if(message_bytes && (message_bytes->size() > 0)){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,162 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1, USRP and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.15.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGChirpChatDemodReport.h
*
* ChirpChatDemod
*/
#ifndef SWGChirpChatDemodReport_H_
#define SWGChirpChatDemodReport_H_
#include <QJsonObject>
#include <QList>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGChirpChatDemodReport: public SWGObject {
public:
SWGChirpChatDemodReport();
SWGChirpChatDemodReport(QString* json);
virtual ~SWGChirpChatDemodReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGChirpChatDemodReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
float getNoisePowerDb();
void setNoisePowerDb(float noise_power_db);
float getSignalPowerDb();
void setSignalPowerDb(float signal_power_db);
float getSnrPowerDb();
void setSnrPowerDb(float snr_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
qint32 getSyncWord();
void setSyncWord(qint32 sync_word);
qint32 getHasCrc();
void setHasCrc(qint32 has_crc);
qint32 getNbParityBits();
void setNbParityBits(qint32 nb_parity_bits);
qint32 getPacketLength();
void setPacketLength(qint32 packet_length);
qint32 getNbSymbols();
void setNbSymbols(qint32 nb_symbols);
qint32 getNbCodewords();
void setNbCodewords(qint32 nb_codewords);
qint32 getHeaderParityStatus();
void setHeaderParityStatus(qint32 header_parity_status);
qint32 getHeaderCrcStatus();
void setHeaderCrcStatus(qint32 header_crc_status);
qint32 getPayloadParityStatus();
void setPayloadParityStatus(qint32 payload_parity_status);
qint32 getPayloadCrcStatus();
void setPayloadCrcStatus(qint32 payload_crc_status);
QString* getMessageTimestamp();
void setMessageTimestamp(QString* message_timestamp);
QString* getMessageString();
void setMessageString(QString* message_string);
QList<QString*>* getMessageBytes();
void setMessageBytes(QList<QString*>* message_bytes);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
float noise_power_db;
bool m_noise_power_db_isSet;
float signal_power_db;
bool m_signal_power_db_isSet;
float snr_power_db;
bool m_snr_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
qint32 sync_word;
bool m_sync_word_isSet;
qint32 has_crc;
bool m_has_crc_isSet;
qint32 nb_parity_bits;
bool m_nb_parity_bits_isSet;
qint32 packet_length;
bool m_packet_length_isSet;
qint32 nb_symbols;
bool m_nb_symbols_isSet;
qint32 nb_codewords;
bool m_nb_codewords_isSet;
qint32 header_parity_status;
bool m_header_parity_status_isSet;
qint32 header_crc_status;
bool m_header_crc_status_isSet;
qint32 payload_parity_status;
bool m_payload_parity_status_isSet;
qint32 payload_crc_status;
bool m_payload_crc_status_isSet;
QString* message_timestamp;
bool m_message_timestamp_isSet;
QString* message_string;
bool m_message_string_isSet;
QList<QString*>* message_bytes;
bool m_message_bytes_isSet;
};
}
#endif /* SWGChirpChatDemodReport_H_ */

View File

@ -0,0 +1,689 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1, USRP and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.15.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGChirpChatDemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGChirpChatDemodSettings::SWGChirpChatDemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGChirpChatDemodSettings::SWGChirpChatDemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
bandwidth_index = 0;
m_bandwidth_index_isSet = false;
spread_factor = 0;
m_spread_factor_isSet = false;
de_bits = 0;
m_de_bits_isSet = false;
fft_window = 0;
m_fft_window_isSet = false;
coding_scheme = 0;
m_coding_scheme_isSet = false;
decode_active = 0;
m_decode_active_isSet = false;
eom_squelch_tenths = 0;
m_eom_squelch_tenths_isSet = false;
nb_symbols_max = 0;
m_nb_symbols_max_isSet = false;
auto_nb_symbols_max = 0;
m_auto_nb_symbols_max_isSet = false;
preamble_chirps = 0;
m_preamble_chirps_isSet = false;
nb_parity_bits = 0;
m_nb_parity_bits_isSet = false;
packet_length = 0;
m_packet_length_isSet = false;
has_crc = 0;
m_has_crc_isSet = false;
has_header = 0;
m_has_header_isSet = false;
send_via_udp = 0;
m_send_via_udp_isSet = false;
udp_address = nullptr;
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
SWGChirpChatDemodSettings::~SWGChirpChatDemodSettings() {
this->cleanup();
}
void
SWGChirpChatDemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
bandwidth_index = 0;
m_bandwidth_index_isSet = false;
spread_factor = 0;
m_spread_factor_isSet = false;
de_bits = 0;
m_de_bits_isSet = false;
fft_window = 0;
m_fft_window_isSet = false;
coding_scheme = 0;
m_coding_scheme_isSet = false;
decode_active = 0;
m_decode_active_isSet = false;
eom_squelch_tenths = 0;
m_eom_squelch_tenths_isSet = false;
nb_symbols_max = 0;
m_nb_symbols_max_isSet = false;
auto_nb_symbols_max = 0;
m_auto_nb_symbols_max_isSet = false;
preamble_chirps = 0;
m_preamble_chirps_isSet = false;
nb_parity_bits = 0;
m_nb_parity_bits_isSet = false;
packet_length = 0;
m_packet_length_isSet = false;
has_crc = 0;
m_has_crc_isSet = false;
has_header = 0;
m_has_header_isSet = false;
send_via_udp = 0;
m_send_via_udp_isSet = false;
udp_address = new QString("");
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
void
SWGChirpChatDemodSettings::cleanup() {
if(udp_address != nullptr) {
delete udp_address;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGChirpChatDemodSettings*
SWGChirpChatDemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGChirpChatDemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&bandwidth_index, pJson["bandwidthIndex"], "qint32", "");
::SWGSDRangel::setValue(&spread_factor, pJson["spreadFactor"], "qint32", "");
::SWGSDRangel::setValue(&de_bits, pJson["deBits"], "qint32", "");
::SWGSDRangel::setValue(&fft_window, pJson["fftWindow"], "qint32", "");
::SWGSDRangel::setValue(&coding_scheme, pJson["codingScheme"], "qint32", "");
::SWGSDRangel::setValue(&decode_active, pJson["decodeActive"], "qint32", "");
::SWGSDRangel::setValue(&eom_squelch_tenths, pJson["eomSquelchTenths"], "qint32", "");
::SWGSDRangel::setValue(&nb_symbols_max, pJson["nbSymbolsMax"], "qint32", "");
::SWGSDRangel::setValue(&auto_nb_symbols_max, pJson["autoNbSymbolsMax"], "qint32", "");
::SWGSDRangel::setValue(&preamble_chirps, pJson["preambleChirps"], "qint32", "");
::SWGSDRangel::setValue(&nb_parity_bits, pJson["nbParityBits"], "qint32", "");
::SWGSDRangel::setValue(&packet_length, pJson["packetLength"], "qint32", "");
::SWGSDRangel::setValue(&has_crc, pJson["hasCRC"], "qint32", "");
::SWGSDRangel::setValue(&has_header, pJson["hasHeader"], "qint32", "");
::SWGSDRangel::setValue(&send_via_udp, pJson["sendViaUDP"], "qint32", "");
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
}
QString
SWGChirpChatDemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGChirpChatDemodSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_bandwidth_index_isSet){
obj->insert("bandwidthIndex", QJsonValue(bandwidth_index));
}
if(m_spread_factor_isSet){
obj->insert("spreadFactor", QJsonValue(spread_factor));
}
if(m_de_bits_isSet){
obj->insert("deBits", QJsonValue(de_bits));
}
if(m_fft_window_isSet){
obj->insert("fftWindow", QJsonValue(fft_window));
}
if(m_coding_scheme_isSet){
obj->insert("codingScheme", QJsonValue(coding_scheme));
}
if(m_decode_active_isSet){
obj->insert("decodeActive", QJsonValue(decode_active));
}
if(m_eom_squelch_tenths_isSet){
obj->insert("eomSquelchTenths", QJsonValue(eom_squelch_tenths));
}
if(m_nb_symbols_max_isSet){
obj->insert("nbSymbolsMax", QJsonValue(nb_symbols_max));
}
if(m_auto_nb_symbols_max_isSet){
obj->insert("autoNbSymbolsMax", QJsonValue(auto_nb_symbols_max));
}
if(m_preamble_chirps_isSet){
obj->insert("preambleChirps", QJsonValue(preamble_chirps));
}
if(m_nb_parity_bits_isSet){
obj->insert("nbParityBits", QJsonValue(nb_parity_bits));
}
if(m_packet_length_isSet){
obj->insert("packetLength", QJsonValue(packet_length));
}
if(m_has_crc_isSet){
obj->insert("hasCRC", QJsonValue(has_crc));
}
if(m_has_header_isSet){
obj->insert("hasHeader", QJsonValue(has_header));
}
if(m_send_via_udp_isSet){
obj->insert("sendViaUDP", QJsonValue(send_via_udp));
}
if(udp_address != nullptr && *udp_address != QString("")){
toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString"));
}
if(m_udp_port_isSet){
obj->insert("udpPort", QJsonValue(udp_port));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
return obj;
}
qint64
SWGChirpChatDemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGChirpChatDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getBandwidthIndex() {
return bandwidth_index;
}
void
SWGChirpChatDemodSettings::setBandwidthIndex(qint32 bandwidth_index) {
this->bandwidth_index = bandwidth_index;
this->m_bandwidth_index_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getSpreadFactor() {
return spread_factor;
}
void
SWGChirpChatDemodSettings::setSpreadFactor(qint32 spread_factor) {
this->spread_factor = spread_factor;
this->m_spread_factor_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getDeBits() {
return de_bits;
}
void
SWGChirpChatDemodSettings::setDeBits(qint32 de_bits) {
this->de_bits = de_bits;
this->m_de_bits_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getFftWindow() {
return fft_window;
}
void
SWGChirpChatDemodSettings::setFftWindow(qint32 fft_window) {
this->fft_window = fft_window;
this->m_fft_window_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getCodingScheme() {
return coding_scheme;
}
void
SWGChirpChatDemodSettings::setCodingScheme(qint32 coding_scheme) {
this->coding_scheme = coding_scheme;
this->m_coding_scheme_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getDecodeActive() {
return decode_active;
}
void
SWGChirpChatDemodSettings::setDecodeActive(qint32 decode_active) {
this->decode_active = decode_active;
this->m_decode_active_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getEomSquelchTenths() {
return eom_squelch_tenths;
}
void
SWGChirpChatDemodSettings::setEomSquelchTenths(qint32 eom_squelch_tenths) {
this->eom_squelch_tenths = eom_squelch_tenths;
this->m_eom_squelch_tenths_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getNbSymbolsMax() {
return nb_symbols_max;
}
void
SWGChirpChatDemodSettings::setNbSymbolsMax(qint32 nb_symbols_max) {
this->nb_symbols_max = nb_symbols_max;
this->m_nb_symbols_max_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getAutoNbSymbolsMax() {
return auto_nb_symbols_max;
}
void
SWGChirpChatDemodSettings::setAutoNbSymbolsMax(qint32 auto_nb_symbols_max) {
this->auto_nb_symbols_max = auto_nb_symbols_max;
this->m_auto_nb_symbols_max_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getPreambleChirps() {
return preamble_chirps;
}
void
SWGChirpChatDemodSettings::setPreambleChirps(qint32 preamble_chirps) {
this->preamble_chirps = preamble_chirps;
this->m_preamble_chirps_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getNbParityBits() {
return nb_parity_bits;
}
void
SWGChirpChatDemodSettings::setNbParityBits(qint32 nb_parity_bits) {
this->nb_parity_bits = nb_parity_bits;
this->m_nb_parity_bits_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getPacketLength() {
return packet_length;
}
void
SWGChirpChatDemodSettings::setPacketLength(qint32 packet_length) {
this->packet_length = packet_length;
this->m_packet_length_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getHasCrc() {
return has_crc;
}
void
SWGChirpChatDemodSettings::setHasCrc(qint32 has_crc) {
this->has_crc = has_crc;
this->m_has_crc_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getHasHeader() {
return has_header;
}
void
SWGChirpChatDemodSettings::setHasHeader(qint32 has_header) {
this->has_header = has_header;
this->m_has_header_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getSendViaUdp() {
return send_via_udp;
}
void
SWGChirpChatDemodSettings::setSendViaUdp(qint32 send_via_udp) {
this->send_via_udp = send_via_udp;
this->m_send_via_udp_isSet = true;
}
QString*
SWGChirpChatDemodSettings::getUdpAddress() {
return udp_address;
}
void
SWGChirpChatDemodSettings::setUdpAddress(QString* udp_address) {
this->udp_address = udp_address;
this->m_udp_address_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getUdpPort() {
return udp_port;
}
void
SWGChirpChatDemodSettings::setUdpPort(qint32 udp_port) {
this->udp_port = udp_port;
this->m_udp_port_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGChirpChatDemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGChirpChatDemodSettings::getTitle() {
return title;
}
void
SWGChirpChatDemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGChirpChatDemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGChirpChatDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGChirpChatDemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGChirpChatDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGChirpChatDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGChirpChatDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGChirpChatDemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGChirpChatDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
bool
SWGChirpChatDemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_bandwidth_index_isSet){
isObjectUpdated = true; break;
}
if(m_spread_factor_isSet){
isObjectUpdated = true; break;
}
if(m_de_bits_isSet){
isObjectUpdated = true; break;
}
if(m_fft_window_isSet){
isObjectUpdated = true; break;
}
if(m_coding_scheme_isSet){
isObjectUpdated = true; break;
}
if(m_decode_active_isSet){
isObjectUpdated = true; break;
}
if(m_eom_squelch_tenths_isSet){
isObjectUpdated = true; break;
}
if(m_nb_symbols_max_isSet){
isObjectUpdated = true; break;
}
if(m_auto_nb_symbols_max_isSet){
isObjectUpdated = true; break;
}
if(m_preamble_chirps_isSet){
isObjectUpdated = true; break;
}
if(m_nb_parity_bits_isSet){
isObjectUpdated = true; break;
}
if(m_packet_length_isSet){
isObjectUpdated = true; break;
}
if(m_has_crc_isSet){
isObjectUpdated = true; break;
}
if(m_has_header_isSet){
isObjectUpdated = true; break;
}
if(m_send_via_udp_isSet){
isObjectUpdated = true; break;
}
if(udp_address && *udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_port_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,209 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1, USRP and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.15.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGChirpChatDemodSettings.h
*
* ChirpChatDemod
*/
#ifndef SWGChirpChatDemodSettings_H_
#define SWGChirpChatDemodSettings_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGChirpChatDemodSettings: public SWGObject {
public:
SWGChirpChatDemodSettings();
SWGChirpChatDemodSettings(QString* json);
virtual ~SWGChirpChatDemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGChirpChatDemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
qint32 getBandwidthIndex();
void setBandwidthIndex(qint32 bandwidth_index);
qint32 getSpreadFactor();
void setSpreadFactor(qint32 spread_factor);
qint32 getDeBits();
void setDeBits(qint32 de_bits);
qint32 getFftWindow();
void setFftWindow(qint32 fft_window);
qint32 getCodingScheme();
void setCodingScheme(qint32 coding_scheme);
qint32 getDecodeActive();
void setDecodeActive(qint32 decode_active);
qint32 getEomSquelchTenths();
void setEomSquelchTenths(qint32 eom_squelch_tenths);
qint32 getNbSymbolsMax();
void setNbSymbolsMax(qint32 nb_symbols_max);
qint32 getAutoNbSymbolsMax();
void setAutoNbSymbolsMax(qint32 auto_nb_symbols_max);
qint32 getPreambleChirps();
void setPreambleChirps(qint32 preamble_chirps);
qint32 getNbParityBits();
void setNbParityBits(qint32 nb_parity_bits);
qint32 getPacketLength();
void setPacketLength(qint32 packet_length);
qint32 getHasCrc();
void setHasCrc(qint32 has_crc);
qint32 getHasHeader();
void setHasHeader(qint32 has_header);
qint32 getSendViaUdp();
void setSendViaUdp(qint32 send_via_udp);
QString* getUdpAddress();
void setUdpAddress(QString* udp_address);
qint32 getUdpPort();
void setUdpPort(qint32 udp_port);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
virtual bool isSet() override;
private:
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
qint32 bandwidth_index;
bool m_bandwidth_index_isSet;
qint32 spread_factor;
bool m_spread_factor_isSet;
qint32 de_bits;
bool m_de_bits_isSet;
qint32 fft_window;
bool m_fft_window_isSet;
qint32 coding_scheme;
bool m_coding_scheme_isSet;
qint32 decode_active;
bool m_decode_active_isSet;
qint32 eom_squelch_tenths;
bool m_eom_squelch_tenths_isSet;
qint32 nb_symbols_max;
bool m_nb_symbols_max_isSet;
qint32 auto_nb_symbols_max;
bool m_auto_nb_symbols_max_isSet;
qint32 preamble_chirps;
bool m_preamble_chirps_isSet;
qint32 nb_parity_bits;
bool m_nb_parity_bits_isSet;
qint32 packet_length;
bool m_packet_length_isSet;
qint32 has_crc;
bool m_has_crc_isSet;
qint32 has_header;
bool m_has_header_isSet;
qint32 send_via_udp;
bool m_send_via_udp_isSet;
QString* udp_address;
bool m_udp_address_isSet;
qint32 udp_port;
bool m_udp_port_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
qint32 stream_index;
bool m_stream_index_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
};
}
#endif /* SWGChirpChatDemodSettings_H_ */

View File

@ -56,6 +56,8 @@
#include "SWGChannelReport.h"
#include "SWGChannelSettings.h"
#include "SWGChannelsDetail.h"
#include "SWGChirpChatDemodReport.h"
#include "SWGChirpChatDemodSettings.h"
#include "SWGCommand.h"
#include "SWGComplex.h"
#include "SWGDATVDemodSettings.h"
@ -335,6 +337,12 @@ namespace SWGSDRangel {
if(QString("SWGChannelsDetail").compare(type) == 0) {
return new SWGChannelsDetail();
}
if(QString("SWGChirpChatDemodReport").compare(type) == 0) {
return new SWGChirpChatDemodReport();
}
if(QString("SWGChirpChatDemodSettings").compare(type) == 0) {
return new SWGChirpChatDemodSettings();
}
if(QString("SWGCommand").compare(type) == 0) {
return new SWGCommand();
}