mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-22 17:45:48 -05:00
Add RS41 Radiosonde (Weather Ballon) demodulator and feature
This commit is contained in:
parent
7ab11615f6
commit
874ca5e85e
BIN
doc/img/RadiosondeDemod_plugin.png
Normal file
BIN
doc/img/RadiosondeDemod_plugin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
BIN
doc/img/RadiosondeDemod_plugin_table.png
Normal file
BIN
doc/img/RadiosondeDemod_plugin_table.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
doc/img/Radiosonde_plugin.png
Normal file
BIN
doc/img/Radiosonde_plugin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
BIN
doc/img/Radiosonde_plugin_map.png
Normal file
BIN
doc/img/Radiosonde_plugin_map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 391 KiB |
@ -21,6 +21,7 @@ add_subdirectory(demodais)
|
||||
add_subdirectory(demodpager)
|
||||
add_subdirectory(radioclock)
|
||||
add_subdirectory(radioastronomy)
|
||||
add_subdirectory(demodradiosonde)
|
||||
|
||||
if(DAB_FOUND AND ZLIB_FOUND AND FAAD_FOUND)
|
||||
add_subdirectory(demoddab)
|
||||
|
68
plugins/channelrx/demodradiosonde/CMakeLists.txt
Normal file
68
plugins/channelrx/demodradiosonde/CMakeLists.txt
Normal file
@ -0,0 +1,68 @@
|
||||
project(demodradiosonde)
|
||||
|
||||
set(demodradiosonde_SOURCES
|
||||
radiosondedemod.cpp
|
||||
radiosondedemodsettings.cpp
|
||||
radiosondedemodbaseband.cpp
|
||||
radiosondedemodsink.cpp
|
||||
radiosondedemodplugin.cpp
|
||||
radiosondedemodwebapiadapter.cpp
|
||||
)
|
||||
|
||||
set(demodradiosonde_HEADERS
|
||||
radiosondedemod.h
|
||||
radiosondedemodsettings.h
|
||||
radiosondedemodbaseband.h
|
||||
radiosondedemodsink.h
|
||||
radiosondedemodplugin.h
|
||||
radiosondedemodwebapiadapter.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(demodradiosonde_SOURCES
|
||||
${demodradiosonde_SOURCES}
|
||||
radiosondedemodgui.cpp
|
||||
radiosondedemodgui.ui
|
||||
)
|
||||
set(demodradiosonde_HEADERS
|
||||
${demodradiosonde_HEADERS}
|
||||
radiosondedemodgui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME demodradiosonde)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME demodradiosondesrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${demodradiosonde_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
715
plugins/channelrx/demodradiosonde/radiosondedemod.cpp
Normal file
715
plugins/channelrx/demodradiosonde/radiosondedemod.cpp
Normal file
@ -0,0 +1,715 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "radiosondedemod.h"
|
||||
|
||||
#include <QTime>
|
||||
#include <QDebug>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QBuffer>
|
||||
#include <QThread>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <complex.h>
|
||||
|
||||
#include "SWGChannelSettings.h"
|
||||
#include "SWGChannelReport.h"
|
||||
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "feature/feature.h"
|
||||
#include "util/db.h"
|
||||
#include "maincore.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RadiosondeDemod::MsgConfigureRadiosondeDemod, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RadiosondeDemod::MsgMessage, Message)
|
||||
|
||||
const char * const RadiosondeDemod::m_channelIdURI = "sdrangel.channel.radiosondedemod";
|
||||
const char * const RadiosondeDemod::m_channelId = "RadiosondeDemod";
|
||||
|
||||
RadiosondeDemod::RadiosondeDemod(DeviceAPI *deviceAPI) :
|
||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_basebandSampleRate(0)
|
||||
{
|
||||
setObjectName(m_channelId);
|
||||
|
||||
m_basebandSink = new RadiosondeDemodBaseband(this);
|
||||
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
|
||||
m_basebandSink->setChannel(this);
|
||||
m_basebandSink->moveToThread(&m_thread);
|
||||
|
||||
applySettings(m_settings, true);
|
||||
|
||||
m_deviceAPI->addChannelSink(this);
|
||||
m_deviceAPI->addChannelSinkAPI(this);
|
||||
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
connect(&m_channelMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleChannelMessages()));
|
||||
}
|
||||
|
||||
RadiosondeDemod::~RadiosondeDemod()
|
||||
{
|
||||
qDebug("RadiosondeDemod::~RadiosondeDemod");
|
||||
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
delete m_networkManager;
|
||||
m_deviceAPI->removeChannelSinkAPI(this);
|
||||
m_deviceAPI->removeChannelSink(this);
|
||||
|
||||
if (m_basebandSink->isRunning()) {
|
||||
stop();
|
||||
}
|
||||
|
||||
delete m_basebandSink;
|
||||
}
|
||||
|
||||
uint32_t RadiosondeDemod::getNumberOfDeviceStreams() const
|
||||
{
|
||||
return m_deviceAPI->getNbSourceStreams();
|
||||
}
|
||||
|
||||
void RadiosondeDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
|
||||
{
|
||||
(void) firstOfBurst;
|
||||
m_basebandSink->feed(begin, end);
|
||||
}
|
||||
|
||||
void RadiosondeDemod::start()
|
||||
{
|
||||
qDebug("RadiosondeDemod::start");
|
||||
|
||||
m_basebandSink->reset();
|
||||
m_basebandSink->startWork();
|
||||
m_thread.start();
|
||||
|
||||
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
|
||||
m_basebandSink->getInputMessageQueue()->push(dspMsg);
|
||||
|
||||
RadiosondeDemodBaseband::MsgConfigureRadiosondeDemodBaseband *msg = RadiosondeDemodBaseband::MsgConfigureRadiosondeDemodBaseband::create(m_settings, true);
|
||||
m_basebandSink->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
|
||||
void RadiosondeDemod::stop()
|
||||
{
|
||||
qDebug("RadiosondeDemod::stop");
|
||||
m_basebandSink->stopWork();
|
||||
m_thread.quit();
|
||||
m_thread.wait();
|
||||
}
|
||||
|
||||
bool RadiosondeDemod::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureRadiosondeDemod::match(cmd))
|
||||
{
|
||||
MsgConfigureRadiosondeDemod& cfg = (MsgConfigureRadiosondeDemod&) cmd;
|
||||
qDebug() << "RadiosondeDemod::handleMessage: MsgConfigureRadiosondeDemod";
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
m_basebandSampleRate = notif.getSampleRate();
|
||||
m_centerFrequency = notif.getCenterFrequency();
|
||||
// Forward to the sink
|
||||
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
|
||||
qDebug() << "RadiosondeDemod::handleMessage: DSPSignalNotification";
|
||||
m_basebandSink->getInputMessageQueue()->push(rep);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgMessage::match(cmd))
|
||||
{
|
||||
MsgMessage& report = (MsgMessage&)cmd;
|
||||
|
||||
// Decode the message
|
||||
RS41Frame *frame = RS41Frame::decode(report.getMessage());
|
||||
RS41Subframe *subframe = nullptr;
|
||||
|
||||
if (m_subframes.contains(frame->m_serial))
|
||||
{
|
||||
subframe = m_subframes.value(frame->m_serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
subframe = new RS41Subframe();
|
||||
m_subframes.insert(frame->m_serial, subframe);
|
||||
}
|
||||
subframe->update(frame);
|
||||
|
||||
// Forward to GUI
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgMessage *msg = new MsgMessage(report);
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
|
||||
MessagePipesLegacy& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
|
||||
// Forward to Radiosonde feature
|
||||
QList<MessageQueue*> *radiosondeMessageQueues = messagePipes.getMessageQueues(this, "radiosonde");
|
||||
if (radiosondeMessageQueues)
|
||||
{
|
||||
QList<MessageQueue*>::iterator it = radiosondeMessageQueues->begin();
|
||||
for (; it != radiosondeMessageQueues->end(); ++it)
|
||||
{
|
||||
MainCore::MsgPacket *msg = MainCore::MsgPacket::create(this, report.getMessage(), report.getDateTime());
|
||||
(*it)->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Forward via UDP
|
||||
if (m_settings.m_udpEnabled)
|
||||
{
|
||||
m_udpSocket.writeDatagram(report.getMessage().data(), report.getMessage().size(),
|
||||
QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort);
|
||||
}
|
||||
|
||||
// Write to log file
|
||||
if (m_logFile.isOpen())
|
||||
{
|
||||
|
||||
m_logStream << report.getDateTime().date().toString() << ","
|
||||
<< report.getDateTime().time().toString() << ","
|
||||
<< report.getMessage().toHex() << ",";
|
||||
|
||||
if (frame->m_statusValid)
|
||||
{
|
||||
m_logStream << frame->m_serial << ","
|
||||
<< frame->m_frameNumber << ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_logStream << ",,";
|
||||
}
|
||||
if (frame->m_posValid)
|
||||
{
|
||||
m_logStream << frame->m_latitude << ","
|
||||
<< frame->m_longitude << ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_logStream << ",,";
|
||||
}
|
||||
if (frame->m_measValid)
|
||||
{
|
||||
m_logStream << frame->getPressureString(subframe) << ","
|
||||
<< frame->getTemperatureString(subframe) << ","
|
||||
<< frame->getHumidityString(subframe) << ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_logStream << ",,,";
|
||||
}
|
||||
m_logStream << "\n";
|
||||
}
|
||||
|
||||
delete frame;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MainCore::MsgChannelDemodQuery::match(cmd))
|
||||
{
|
||||
qDebug() << "RadiosondeDemod::handleMessage: MsgChannelDemodQuery";
|
||||
sendSampleRateToDemodAnalyzer();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ScopeVis *RadiosondeDemod::getScopeSink()
|
||||
{
|
||||
return m_basebandSink->getScopeSink();
|
||||
}
|
||||
|
||||
void RadiosondeDemod::setCenterFrequency(qint64 frequency)
|
||||
{
|
||||
RadiosondeDemodSettings settings = m_settings;
|
||||
settings.m_inputFrequencyOffset = frequency;
|
||||
applySettings(settings, false);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureRadiosondeDemod *msgToGUI = MsgConfigureRadiosondeDemod::create(settings, false);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemod::applySettings(const RadiosondeDemodSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RadiosondeDemod::applySettings:"
|
||||
<< " m_logEnabled: " << settings.m_logEnabled
|
||||
<< " m_logFilename: " << settings.m_logFilename
|
||||
<< " m_streamIndex: " << settings.m_streamIndex
|
||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
||||
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
|
||||
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
|
||||
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
|
||||
<< " force: " << force;
|
||||
|
||||
QList<QString> reverseAPIKeys;
|
||||
|
||||
if ((settings.m_baud != m_settings.m_baud) || force) {
|
||||
reverseAPIKeys.append("baud");
|
||||
}
|
||||
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
|
||||
reverseAPIKeys.append("inputFrequencyOffset");
|
||||
}
|
||||
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
|
||||
reverseAPIKeys.append("rfBandwidth");
|
||||
}
|
||||
if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) {
|
||||
reverseAPIKeys.append("fmDeviation");
|
||||
}
|
||||
if ((settings.m_correlationThreshold != m_settings.m_correlationThreshold) || force) {
|
||||
reverseAPIKeys.append("correlationThreshold");
|
||||
}
|
||||
if ((settings.m_udpEnabled != m_settings.m_udpEnabled) || force) {
|
||||
reverseAPIKeys.append("udpEnabled");
|
||||
}
|
||||
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) {
|
||||
reverseAPIKeys.append("udpAddress");
|
||||
}
|
||||
if ((settings.m_udpPort != m_settings.m_udpPort) || force) {
|
||||
reverseAPIKeys.append("udpPort");
|
||||
}
|
||||
if ((settings.m_logFilename != m_settings.m_logFilename) || force) {
|
||||
reverseAPIKeys.append("logFilename");
|
||||
}
|
||||
if ((settings.m_logEnabled != m_settings.m_logEnabled) || force) {
|
||||
reverseAPIKeys.append("logEnabled");
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
RadiosondeDemodBaseband::MsgConfigureRadiosondeDemodBaseband *msg = RadiosondeDemodBaseband::MsgConfigureRadiosondeDemodBaseband::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 ((settings.m_logEnabled != m_settings.m_logEnabled)
|
||||
|| (settings.m_logFilename != m_settings.m_logFilename)
|
||||
|| force)
|
||||
{
|
||||
if (m_logFile.isOpen())
|
||||
{
|
||||
m_logStream.flush();
|
||||
m_logFile.close();
|
||||
}
|
||||
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
|
||||
{
|
||||
m_logFile.setFileName(settings.m_logFilename);
|
||||
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
|
||||
{
|
||||
qDebug() << "RadiosondeDemod::applySettings - Logging to: " << settings.m_logFilename;
|
||||
bool newFile = m_logFile.size() == 0;
|
||||
m_logStream.setDevice(&m_logFile);
|
||||
if (newFile)
|
||||
{
|
||||
// Write header
|
||||
m_logStream << "Date,Time,Data,Serial,Frame,Lat,Lon,P (hPa),T (C), U (%)\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "RadiosondeDemod::applySettings - Unable to open log file: " << settings.m_logFilename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
QByteArray RadiosondeDemod::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RadiosondeDemod::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
MsgConfigureRadiosondeDemod *msg = MsgConfigureRadiosondeDemod::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
MsgConfigureRadiosondeDemod *msg = MsgConfigureRadiosondeDemod::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemod::sendSampleRateToDemodAnalyzer()
|
||||
{
|
||||
QList<MessageQueue*> *messageQueues = MainCore::instance()->getMessagePipes().getMessageQueues(this, "reportdemod");
|
||||
|
||||
if (messageQueues)
|
||||
{
|
||||
QList<MessageQueue*>::iterator it = messageQueues->begin();
|
||||
|
||||
for (; it != messageQueues->end(); ++it)
|
||||
{
|
||||
MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(
|
||||
this,
|
||||
RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE
|
||||
);
|
||||
(*it)->push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RadiosondeDemod::webapiSettingsGet(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadiosondeDemodSettings(new SWGSDRangel::SWGRadiosondeDemodSettings());
|
||||
response.getRadiosondeDemodSettings()->init();
|
||||
webapiFormatChannelSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RadiosondeDemod::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
RadiosondeDemodSettings settings = m_settings;
|
||||
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
|
||||
|
||||
MsgConfigureRadiosondeDemod *msg = MsgConfigureRadiosondeDemod::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
qDebug("RadiosondeDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureRadiosondeDemod *msgToGUI = MsgConfigureRadiosondeDemod::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatChannelSettings(response, settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
void RadiosondeDemod::webapiUpdateChannelSettings(
|
||||
RadiosondeDemodSettings& settings,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response)
|
||||
{
|
||||
if (channelSettingsKeys.contains("baud")) {
|
||||
settings.m_baud = response.getRadiosondeDemodSettings()->getBaud();
|
||||
}
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
|
||||
settings.m_inputFrequencyOffset = response.getRadiosondeDemodSettings()->getInputFrequencyOffset();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rfBandwidth")) {
|
||||
settings.m_rfBandwidth = response.getRadiosondeDemodSettings()->getRfBandwidth();
|
||||
}
|
||||
if (channelSettingsKeys.contains("fmDeviation")) {
|
||||
settings.m_fmDeviation = response.getRadiosondeDemodSettings()->getFmDeviation();
|
||||
}
|
||||
if (channelSettingsKeys.contains("correlationThreshold")) {
|
||||
settings.m_correlationThreshold = response.getRadiosondeDemodSettings()->getCorrelationThreshold();
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpEnabled")) {
|
||||
settings.m_udpEnabled = response.getRadiosondeDemodSettings()->getUdpEnabled();
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpAddress")) {
|
||||
settings.m_udpAddress = *response.getRadiosondeDemodSettings()->getUdpAddress();
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpPort")) {
|
||||
settings.m_udpPort = response.getRadiosondeDemodSettings()->getUdpPort();
|
||||
}
|
||||
if (channelSettingsKeys.contains("logFilename")) {
|
||||
settings.m_logFilename = *response.getRadiosondeDemodSettings()->getLogFilename();
|
||||
}
|
||||
if (channelSettingsKeys.contains("logEnabled")) {
|
||||
settings.m_logEnabled = response.getRadiosondeDemodSettings()->getLogEnabled();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getRadiosondeDemodSettings()->getRgbColor();
|
||||
}
|
||||
if (channelSettingsKeys.contains("title")) {
|
||||
settings.m_title = *response.getRadiosondeDemodSettings()->getTitle();
|
||||
}
|
||||
if (channelSettingsKeys.contains("streamIndex")) {
|
||||
settings.m_streamIndex = response.getRadiosondeDemodSettings()->getStreamIndex();
|
||||
}
|
||||
if (channelSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getRadiosondeDemodSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getRadiosondeDemodSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getRadiosondeDemodSettings()->getReverseApiPort();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
|
||||
settings.m_reverseAPIDeviceIndex = response.getRadiosondeDemodSettings()->getReverseApiDeviceIndex();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
|
||||
settings.m_reverseAPIChannelIndex = response.getRadiosondeDemodSettings()->getReverseApiChannelIndex();
|
||||
}
|
||||
if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) {
|
||||
settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.getRadiosondeDemodSettings()->getScopeConfig());
|
||||
}
|
||||
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
|
||||
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getRadiosondeDemodSettings()->getChannelMarker());
|
||||
}
|
||||
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
|
||||
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getRadiosondeDemodSettings()->getRollupState());
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const RadiosondeDemodSettings& settings)
|
||||
{
|
||||
response.getRadiosondeDemodSettings()->setBaud(settings.m_baud);
|
||||
response.getRadiosondeDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
response.getRadiosondeDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
|
||||
response.getRadiosondeDemodSettings()->setFmDeviation(settings.m_fmDeviation);
|
||||
response.getRadiosondeDemodSettings()->setCorrelationThreshold(settings.m_correlationThreshold);
|
||||
response.getRadiosondeDemodSettings()->setUdpEnabled(settings.m_udpEnabled);
|
||||
response.getRadiosondeDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress));
|
||||
response.getRadiosondeDemodSettings()->setUdpPort(settings.m_udpPort);
|
||||
response.getRadiosondeDemodSettings()->setLogFilename(new QString(settings.m_logFilename));
|
||||
response.getRadiosondeDemodSettings()->setLogEnabled(settings.m_logEnabled);
|
||||
|
||||
response.getRadiosondeDemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||
if (response.getRadiosondeDemodSettings()->getTitle()) {
|
||||
*response.getRadiosondeDemodSettings()->getTitle() = settings.m_title;
|
||||
} else {
|
||||
response.getRadiosondeDemodSettings()->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
|
||||
response.getRadiosondeDemodSettings()->setStreamIndex(settings.m_streamIndex);
|
||||
response.getRadiosondeDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getRadiosondeDemodSettings()->getReverseApiAddress()) {
|
||||
*response.getRadiosondeDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getRadiosondeDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getRadiosondeDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getRadiosondeDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getRadiosondeDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
|
||||
if (settings.m_scopeGUI)
|
||||
{
|
||||
if (response.getRadiosondeDemodSettings()->getScopeConfig())
|
||||
{
|
||||
settings.m_scopeGUI->formatTo(response.getRadiosondeDemodSettings()->getScopeConfig());
|
||||
}
|
||||
else
|
||||
{
|
||||
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
|
||||
settings.m_scopeGUI->formatTo(swgGLScope);
|
||||
response.getRadiosondeDemodSettings()->setScopeConfig(swgGLScope);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.m_channelMarker)
|
||||
{
|
||||
if (response.getRadiosondeDemodSettings()->getChannelMarker())
|
||||
{
|
||||
settings.m_channelMarker->formatTo(response.getRadiosondeDemodSettings()->getChannelMarker());
|
||||
}
|
||||
else
|
||||
{
|
||||
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
|
||||
settings.m_channelMarker->formatTo(swgChannelMarker);
|
||||
response.getRadiosondeDemodSettings()->setChannelMarker(swgChannelMarker);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.m_rollupState)
|
||||
{
|
||||
if (response.getRadiosondeDemodSettings()->getRollupState())
|
||||
{
|
||||
settings.m_rollupState->formatTo(response.getRadiosondeDemodSettings()->getRollupState());
|
||||
}
|
||||
else
|
||||
{
|
||||
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
|
||||
settings.m_rollupState->formatTo(swgRollupState);
|
||||
response.getRadiosondeDemodSettings()->setRollupState(swgRollupState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RadiosondeDemodSettings& 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 RadiosondeDemod::webapiFormatChannelSettings(
|
||||
QList<QString>& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
|
||||
const RadiosondeDemodSettings& settings,
|
||||
bool force
|
||||
)
|
||||
{
|
||||
swgChannelSettings->setDirection(0); // Single sink (Rx)
|
||||
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
|
||||
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
|
||||
swgChannelSettings->setChannelType(new QString("RadiosondeDemod"));
|
||||
swgChannelSettings->setRadiosondeDemodSettings(new SWGSDRangel::SWGRadiosondeDemodSettings());
|
||||
SWGSDRangel::SWGRadiosondeDemodSettings *swgRadiosondeDemodSettings = swgChannelSettings->getRadiosondeDemodSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (channelSettingsKeys.contains("baud") || force) {
|
||||
swgRadiosondeDemodSettings->setBaud(settings.m_baud);
|
||||
}
|
||||
if (channelSettingsKeys.contains("fmDeviation") || force) {
|
||||
swgRadiosondeDemodSettings->setFmDeviation(settings.m_fmDeviation);
|
||||
}
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
|
||||
swgRadiosondeDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rfBandwidth") || force) {
|
||||
swgRadiosondeDemodSettings->setRfBandwidth(settings.m_rfBandwidth);
|
||||
}
|
||||
if (channelSettingsKeys.contains("correlationThreshold") || force) {
|
||||
swgRadiosondeDemodSettings->setCorrelationThreshold(settings.m_correlationThreshold);
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpEnabled") || force) {
|
||||
swgRadiosondeDemodSettings->setUdpEnabled(settings.m_udpEnabled);
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpAddress") || force) {
|
||||
swgRadiosondeDemodSettings->setUdpAddress(new QString(settings.m_udpAddress));
|
||||
}
|
||||
if (channelSettingsKeys.contains("udpPort") || force) {
|
||||
swgRadiosondeDemodSettings->setUdpPort(settings.m_udpPort);
|
||||
}
|
||||
if (channelSettingsKeys.contains("logFilename") || force) {
|
||||
swgRadiosondeDemodSettings->setLogFilename(new QString(settings.m_logFilename));
|
||||
}
|
||||
if (channelSettingsKeys.contains("logEnabled") || force) {
|
||||
swgRadiosondeDemodSettings->setLogEnabled(settings.m_logEnabled);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||
swgRadiosondeDemodSettings->setRgbColor(settings.m_rgbColor);
|
||||
}
|
||||
if (channelSettingsKeys.contains("title") || force) {
|
||||
swgRadiosondeDemodSettings->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
if (channelSettingsKeys.contains("streamIndex") || force) {
|
||||
swgRadiosondeDemodSettings->setStreamIndex(settings.m_streamIndex);
|
||||
}
|
||||
|
||||
if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force))
|
||||
{
|
||||
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
|
||||
settings.m_scopeGUI->formatTo(swgGLScope);
|
||||
swgRadiosondeDemodSettings->setScopeConfig(swgGLScope);
|
||||
}
|
||||
|
||||
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
|
||||
{
|
||||
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
|
||||
settings.m_channelMarker->formatTo(swgChannelMarker);
|
||||
swgRadiosondeDemodSettings->setChannelMarker(swgChannelMarker);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemod::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "RadiosondeDemod::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("RadiosondeDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void RadiosondeDemod::handleChannelMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_channelMessageQueue.pop()) != nullptr)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
195
plugins/channelrx/demodradiosonde/radiosondedemod.h
Normal file
195
plugins/channelrx/demodradiosonde/radiosondedemod.h
Normal file
@ -0,0 +1,195 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMOD_H
|
||||
#define INCLUDE_RADIOSONDEDEMOD_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QUdpSocket>
|
||||
#include <QThread>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QVector3D>
|
||||
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "util/message.h"
|
||||
#include "util/radiosonde.h"
|
||||
|
||||
#include "radiosondedemodbaseband.h"
|
||||
#include "radiosondedemodsettings.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QThread;
|
||||
class DeviceAPI;
|
||||
class ScopeVis;
|
||||
|
||||
class RadiosondeDemod : public BasebandSampleSink, public ChannelAPI {
|
||||
|
||||
public:
|
||||
class MsgConfigureRadiosondeDemod : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RadiosondeDemodSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRadiosondeDemod* create(const RadiosondeDemodSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureRadiosondeDemod(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RadiosondeDemodSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRadiosondeDemod(const RadiosondeDemodSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgMessage : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QByteArray getMessage() const { return m_message; }
|
||||
QDateTime getDateTime() const { return m_dateTime; }
|
||||
int getErrorsCorrected() const { return m_errorsCorrected; }
|
||||
int getThreshold() const { return m_threshold; }
|
||||
|
||||
static MsgMessage* create(QByteArray message, int errorsCorrected, int threshold)
|
||||
{
|
||||
return new MsgMessage(message, QDateTime::currentDateTime(), errorsCorrected, threshold);
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_message;
|
||||
QDateTime m_dateTime;
|
||||
int m_errorsCorrected;
|
||||
int m_threshold;
|
||||
|
||||
MsgMessage(QByteArray message, QDateTime dateTime, int errorsCorrected, int threshold) :
|
||||
Message(),
|
||||
m_message(message),
|
||||
m_dateTime(dateTime),
|
||||
m_errorsCorrected(errorsCorrected),
|
||||
m_threshold(threshold)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
RadiosondeDemod(DeviceAPI *deviceAPI);
|
||||
virtual ~RadiosondeDemod();
|
||||
virtual void destroy() { delete this; }
|
||||
|
||||
using BasebandSampleSink::feed;
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); }
|
||||
virtual QString getSinkName() { return objectName(); }
|
||||
|
||||
virtual void getIdentifier(QString& id) { id = objectName(); }
|
||||
virtual const QString& getURI() const { return getName(); }
|
||||
virtual void getTitle(QString& title) { title = m_settings.m_title; }
|
||||
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
|
||||
virtual void setCenterFrequency(qint64 frequency);
|
||||
|
||||
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);
|
||||
|
||||
static void webapiFormatChannelSettings(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
const RadiosondeDemodSettings& settings);
|
||||
|
||||
static void webapiUpdateChannelSettings(
|
||||
RadiosondeDemodSettings& settings,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response);
|
||||
|
||||
ScopeVis *getScopeSink();
|
||||
double getMagSq() const { return m_basebandSink->getMagSq(); }
|
||||
|
||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
|
||||
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
|
||||
}
|
||||
|
||||
uint32_t getNumberOfDeviceStreams() const;
|
||||
|
||||
static const char * const m_channelIdURI;
|
||||
static const char * const m_channelId;
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QThread m_thread;
|
||||
RadiosondeDemodBaseband* m_basebandSink;
|
||||
RadiosondeDemodSettings m_settings;
|
||||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||
qint64 m_centerFrequency;
|
||||
QUdpSocket m_udpSocket;
|
||||
QFile m_logFile;
|
||||
QTextStream m_logStream;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
QHash<QString, RS41Subframe *> m_subframes; // Hash of serial to subframes
|
||||
|
||||
virtual bool handleMessage(const Message& cmd);
|
||||
void applySettings(const RadiosondeDemodSettings& settings, bool force = false);
|
||||
void sendSampleRateToDemodAnalyzer();
|
||||
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RadiosondeDemodSettings& settings, bool force);
|
||||
void webapiFormatChannelSettings(
|
||||
QList<QString>& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
|
||||
const RadiosondeDemodSettings& settings,
|
||||
bool force
|
||||
);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
void handleChannelMessages();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMOD_H
|
176
plugins/channelrx/demodradiosonde/radiosondedemodbaseband.cpp
Normal file
176
plugins/channelrx/demodradiosonde/radiosondedemodbaseband.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/downchannelizer.h"
|
||||
|
||||
#include "radiosondedemodbaseband.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RadiosondeDemodBaseband::MsgConfigureRadiosondeDemodBaseband, Message)
|
||||
|
||||
RadiosondeDemodBaseband::RadiosondeDemodBaseband(RadiosondeDemod *radiosondeDemod) :
|
||||
m_sink(radiosondeDemod),
|
||||
m_running(false),
|
||||
m_mutex(QMutex::Recursive)
|
||||
{
|
||||
qDebug("RadiosondeDemodBaseband::RadiosondeDemodBaseband");
|
||||
|
||||
m_sink.setScopeSink(&m_scopeSink);
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
|
||||
m_channelizer = new DownChannelizer(&m_sink);
|
||||
}
|
||||
|
||||
RadiosondeDemodBaseband::~RadiosondeDemodBaseband()
|
||||
{
|
||||
m_inputMessageQueue.clear();
|
||||
|
||||
delete m_channelizer;
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::reset()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_inputMessageQueue.clear();
|
||||
m_sampleFifo.reset();
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::startWork()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
QObject::connect(
|
||||
&m_sampleFifo,
|
||||
&SampleSinkFifo::dataReady,
|
||||
this,
|
||||
&RadiosondeDemodBaseband::handleData,
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::stopWork()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
QObject::disconnect(
|
||||
&m_sampleFifo,
|
||||
&SampleSinkFifo::dataReady,
|
||||
this,
|
||||
&RadiosondeDemodBaseband::handleData
|
||||
);
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::setChannel(ChannelAPI *channel)
|
||||
{
|
||||
m_sink.setChannel(channel);
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
m_sampleFifo.write(begin, end);
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::handleData()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
|
||||
{
|
||||
SampleVector::iterator part1begin;
|
||||
SampleVector::iterator part1end;
|
||||
SampleVector::iterator part2begin;
|
||||
SampleVector::iterator part2end;
|
||||
|
||||
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
|
||||
|
||||
// first part of FIFO data
|
||||
if (part1begin != part1end) {
|
||||
m_channelizer->feed(part1begin, part1end);
|
||||
}
|
||||
|
||||
// second part of FIFO data (used when block wraps around)
|
||||
if(part2begin != part2end) {
|
||||
m_channelizer->feed(part2begin, part2end);
|
||||
}
|
||||
|
||||
m_sampleFifo.readCommit((unsigned int) count);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != nullptr)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RadiosondeDemodBaseband::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureRadiosondeDemodBaseband::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
MsgConfigureRadiosondeDemodBaseband& cfg = (MsgConfigureRadiosondeDemodBaseband&) cmd;
|
||||
qDebug() << "RadiosondeDemodBaseband::handleMessage: MsgConfigureRadiosondeDemodBaseband";
|
||||
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
qDebug() << "RadiosondeDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
|
||||
setBasebandSampleRate(notif.getSampleRate());
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::applySettings(const RadiosondeDemodSettings& settings, bool force)
|
||||
{
|
||||
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
|
||||
{
|
||||
m_channelizer->setChannelization(RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
|
||||
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
|
||||
}
|
||||
|
||||
m_sink.applySettings(settings, force);
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void RadiosondeDemodBaseband::setBasebandSampleRate(int sampleRate)
|
||||
{
|
||||
m_channelizer->setBasebandSampleRate(sampleRate);
|
||||
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
|
||||
}
|
100
plugins/channelrx/demodradiosonde/radiosondedemodbaseband.h
Normal file
100
plugins/channelrx/demodradiosonde/radiosondedemodbaseband.h
Normal file
@ -0,0 +1,100 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMODBASEBAND_H
|
||||
#define INCLUDE_RADIOSONDEDEMODBASEBAND_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
#include "dsp/samplesinkfifo.h"
|
||||
#include "dsp/scopevis.h"
|
||||
#include "util/message.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "radiosondedemodsink.h"
|
||||
|
||||
class DownChannelizer;
|
||||
class ChannelAPI;
|
||||
class RadiosondeDemod;
|
||||
class ScopeVis;
|
||||
|
||||
class RadiosondeDemodBaseband : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureRadiosondeDemodBaseband : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RadiosondeDemodSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRadiosondeDemodBaseband* create(const RadiosondeDemodSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureRadiosondeDemodBaseband(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RadiosondeDemodSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRadiosondeDemodBaseband(const RadiosondeDemodSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
RadiosondeDemodBaseband(RadiosondeDemod *radiosondeDemod);
|
||||
~RadiosondeDemodBaseband();
|
||||
void reset();
|
||||
void startWork();
|
||||
void stopWork();
|
||||
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
|
||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
|
||||
m_sink.getMagSqLevels(avg, peak, nbSamples);
|
||||
}
|
||||
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
|
||||
void setBasebandSampleRate(int sampleRate);
|
||||
ScopeVis *getScopeSink() { return &m_scopeSink; }
|
||||
void setChannel(ChannelAPI *channel);
|
||||
double getMagSq() const { return m_sink.getMagSq(); }
|
||||
bool isRunning() const { return m_running; }
|
||||
|
||||
private:
|
||||
SampleSinkFifo m_sampleFifo;
|
||||
DownChannelizer *m_channelizer;
|
||||
RadiosondeDemodSink m_sink;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
RadiosondeDemodSettings m_settings;
|
||||
ScopeVis m_scopeSink;
|
||||
bool m_running;
|
||||
QMutex m_mutex;
|
||||
|
||||
bool handleMessage(const Message& cmd);
|
||||
void calculateOffset(RadiosondeDemodSink *sink);
|
||||
void applySettings(const RadiosondeDemodSettings& settings, bool force = false);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void handleData(); //!< Handle data when samples have to be processed
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMODBASEBAND_H
|
898
plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp
Normal file
898
plugins/channelrx/demodradiosonde/radiosondedemodgui.cpp
Normal file
@ -0,0 +1,898 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
#include <QRegExp>
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "radiosondedemodgui.h"
|
||||
|
||||
#include "device/deviceuiset.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "ui_radiosondedemodgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "util/csv.h"
|
||||
#include "util/db.h"
|
||||
#include "util/units.h"
|
||||
#include "gui/basicchannelsettingsdialog.h"
|
||||
#include "gui/devicestreamselectiondialog.h"
|
||||
#include "gui/datetimedelegate.h"
|
||||
#include "gui/decimaldelegate.h"
|
||||
#include "gui/timedelegate.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/glscopesettings.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "channel/channelwebapiutils.h"
|
||||
#include "maincore.h"
|
||||
#include "feature/featurewebapiutils.h"
|
||||
|
||||
#include "radiosondedemod.h"
|
||||
#include "radiosondedemodsink.h"
|
||||
|
||||
void RadiosondeDemodGUI::resizeTable()
|
||||
{
|
||||
// Fill table with a row of dummy data that will size the columns nicely
|
||||
// Trailing chars are for sort arrow
|
||||
int row = ui->frames->rowCount();
|
||||
ui->frames->setRowCount(row + 1);
|
||||
ui->frames->setItem(row, FRAME_COL_DATE, new QTableWidgetItem("2015/04/15-"));
|
||||
ui->frames->setItem(row, FRAME_COL_TIME, new QTableWidgetItem("10:17:00"));
|
||||
ui->frames->setItem(row, FRAME_COL_SERIAL, new QTableWidgetItem("S1234567"));
|
||||
ui->frames->setItem(row, FRAME_COL_FRAME_NUMBER, new QTableWidgetItem("10000"));
|
||||
ui->frames->setItem(row, FRAME_COL_FLIGHT_PHASE, new QTableWidgetItem("Descent"));
|
||||
ui->frames->setItem(row, FRAME_COL_LATITUDE, new QTableWidgetItem("-90.00000"));
|
||||
ui->frames->setItem(row, FRAME_COL_LONGITUDE, new QTableWidgetItem("-180.00000"));
|
||||
ui->frames->setItem(row, FRAME_COL_ALTITUDE, new QTableWidgetItem("20000.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_SPEED, new QTableWidgetItem("50.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_VERTICAL_RATE, new QTableWidgetItem("50.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_HEADING, new QTableWidgetItem("359.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_PRESSURE, new QTableWidgetItem("100.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_TEMP, new QTableWidgetItem("-50.1U"));
|
||||
ui->frames->setItem(row, FRAME_COL_HUMIDITY, new QTableWidgetItem("100.0"));
|
||||
ui->frames->setItem(row, FRAME_COL_BATTERY_VOLTAGE, new QTableWidgetItem("2.7"));
|
||||
ui->frames->setItem(row, FRAME_COL_BATTERY_STATUS, new QTableWidgetItem("Low"));
|
||||
ui->frames->setItem(row, FRAME_COL_PCB_TEMP, new QTableWidgetItem("21"));
|
||||
ui->frames->setItem(row, FRAME_COL_HUMIDITY_PWM, new QTableWidgetItem("1000"));
|
||||
ui->frames->setItem(row, FRAME_COL_TX_POWER, new QTableWidgetItem("7"));
|
||||
ui->frames->setItem(row, FRAME_COL_MAX_SUBFRAME_NO, new QTableWidgetItem("50"));
|
||||
ui->frames->setItem(row, FRAME_COL_SUBFRAME_NO, new QTableWidgetItem("50"));
|
||||
ui->frames->setItem(row, FRAME_COL_SUBFRAME, new QTableWidgetItem("00112233445566778899aabbccddeeff----"));
|
||||
ui->frames->setItem(row, FRAME_COL_GPS_TIME, new QTableWidgetItem("2015/04/15 10:17:00"));
|
||||
ui->frames->setItem(row, FRAME_COL_GPS_SATS, new QTableWidgetItem("12"));
|
||||
ui->frames->setItem(row, FRAME_COL_ECC, new QTableWidgetItem("12"));
|
||||
ui->frames->setItem(row, FRAME_COL_CORR, new QTableWidgetItem("-500"));
|
||||
ui->frames->resizeColumnsToContents();
|
||||
ui->frames->removeRow(row);
|
||||
}
|
||||
|
||||
// Columns in table reordered
|
||||
void RadiosondeDemodGUI::frames_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
||||
{
|
||||
(void) oldVisualIndex;
|
||||
|
||||
m_settings.m_frameColumnIndexes[logicalIndex] = newVisualIndex;
|
||||
}
|
||||
|
||||
// Column in table resized (when hidden size is 0)
|
||||
void RadiosondeDemodGUI::frames_sectionResized(int logicalIndex, int oldSize, int newSize)
|
||||
{
|
||||
(void) oldSize;
|
||||
|
||||
m_settings.m_frameColumnSizes[logicalIndex] = newSize;
|
||||
}
|
||||
|
||||
// Right click in table header - show column select menu
|
||||
void RadiosondeDemodGUI::framesColumnSelectMenu(QPoint pos)
|
||||
{
|
||||
framesMenu->popup(ui->frames->horizontalHeader()->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
// Hide/show column when menu selected
|
||||
void RadiosondeDemodGUI::framesColumnSelectMenuChecked(bool checked)
|
||||
{
|
||||
(void) checked;
|
||||
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
if (action != nullptr)
|
||||
{
|
||||
int idx = action->data().toInt(nullptr);
|
||||
ui->frames->setColumnHidden(idx, !action->isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
// Create column select menu item
|
||||
QAction *RadiosondeDemodGUI::createCheckableItem(QString &text, int idx, bool checked, const char *slot)
|
||||
{
|
||||
QAction *action = new QAction(text, this);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(checked);
|
||||
action->setData(QVariant(idx));
|
||||
connect(action, SIGNAL(triggered()), this, slot);
|
||||
return action;
|
||||
}
|
||||
|
||||
RadiosondeDemodGUI* RadiosondeDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
|
||||
{
|
||||
RadiosondeDemodGUI* gui = new RadiosondeDemodGUI(pluginAPI, deviceUISet, rxChannel);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
QByteArray RadiosondeDemodGUI::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RadiosondeDemodGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add row to table
|
||||
void RadiosondeDemodGUI::frameReceived(const QByteArray& frame, const QDateTime& dateTime, int errorsCorrected, int threshold)
|
||||
{
|
||||
RS41Frame *radiosonde;
|
||||
|
||||
// Decode the frame
|
||||
radiosonde = RS41Frame::decode(frame);
|
||||
|
||||
// Is scroll bar at bottom
|
||||
QScrollBar *sb = ui->frames->verticalScrollBar();
|
||||
bool scrollToBottom = sb->value() == sb->maximum();
|
||||
|
||||
// Add to frames table
|
||||
ui->frames->setSortingEnabled(false);
|
||||
int row = ui->frames->rowCount();
|
||||
ui->frames->setRowCount(row + 1);
|
||||
|
||||
QTableWidgetItem *dateItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *timeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *serialItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *frameNumberItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *flightPhaseItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *latitudeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *longitudeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *altitudeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *speedItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *verticalRateItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *headingItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *pressureItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *tempItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *humidityItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *batteryVoltageItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *batteryStatusItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *pcbTempItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *humidityPWMItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *txPowerItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *maxSubframeNoItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *subframeNoItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *subframeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *gpsTimeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *gpsSatsItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *eccItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *thItem = new QTableWidgetItem();
|
||||
|
||||
ui->frames->setItem(row, FRAME_COL_DATE, dateItem);
|
||||
ui->frames->setItem(row, FRAME_COL_TIME, timeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_SERIAL, serialItem);
|
||||
ui->frames->setItem(row, FRAME_COL_FRAME_NUMBER, frameNumberItem);
|
||||
ui->frames->setItem(row, FRAME_COL_FLIGHT_PHASE, flightPhaseItem);
|
||||
ui->frames->setItem(row, FRAME_COL_LATITUDE, latitudeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_LONGITUDE, longitudeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_ALTITUDE, altitudeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_SPEED, speedItem);
|
||||
ui->frames->setItem(row, FRAME_COL_VERTICAL_RATE, verticalRateItem);
|
||||
ui->frames->setItem(row, FRAME_COL_HEADING, headingItem);
|
||||
ui->frames->setItem(row, FRAME_COL_PRESSURE, pressureItem);
|
||||
ui->frames->setItem(row, FRAME_COL_TEMP, tempItem);
|
||||
ui->frames->setItem(row, FRAME_COL_HUMIDITY, humidityItem);
|
||||
ui->frames->setItem(row, FRAME_COL_BATTERY_VOLTAGE, batteryVoltageItem);
|
||||
ui->frames->setItem(row, FRAME_COL_BATTERY_STATUS, batteryStatusItem);
|
||||
ui->frames->setItem(row, FRAME_COL_PCB_TEMP, pcbTempItem);
|
||||
ui->frames->setItem(row, FRAME_COL_HUMIDITY_PWM, humidityPWMItem);
|
||||
ui->frames->setItem(row, FRAME_COL_TX_POWER, txPowerItem);
|
||||
ui->frames->setItem(row, FRAME_COL_MAX_SUBFRAME_NO, maxSubframeNoItem);
|
||||
ui->frames->setItem(row, FRAME_COL_SUBFRAME_NO, subframeNoItem);
|
||||
ui->frames->setItem(row, FRAME_COL_SUBFRAME, subframeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_GPS_TIME, gpsTimeItem);
|
||||
ui->frames->setItem(row, FRAME_COL_GPS_SATS, gpsSatsItem);
|
||||
ui->frames->setItem(row, FRAME_COL_ECC, eccItem);
|
||||
ui->frames->setItem(row, FRAME_COL_CORR, thItem);
|
||||
|
||||
dateItem->setData(Qt::DisplayRole, dateTime.date());
|
||||
timeItem->setData(Qt::DisplayRole, dateTime.time());
|
||||
|
||||
RS41Subframe *subframe = nullptr;
|
||||
|
||||
frameNumberItem->setData(Qt::DisplayRole, radiosonde->m_frameNumber);
|
||||
if (radiosonde->m_statusValid)
|
||||
{
|
||||
serialItem->setText(radiosonde->m_serial);
|
||||
flightPhaseItem->setText(radiosonde->m_flightPhase);
|
||||
batteryVoltageItem->setData(Qt::DisplayRole, radiosonde->m_batteryVoltage);
|
||||
batteryStatusItem->setText(radiosonde->m_batteryStatus);
|
||||
pcbTempItem->setData(Qt::DisplayRole, radiosonde->m_pcbTemperature);
|
||||
humidityPWMItem->setData(Qt::DisplayRole, (int)round(radiosonde->m_humiditySensorHeating / 1000.0 * 100.0));
|
||||
txPowerItem->setData(Qt::DisplayRole, (int)round(radiosonde->m_transmitPower / 7.0 * 100.0));
|
||||
maxSubframeNoItem->setData(Qt::DisplayRole, radiosonde->m_maxSubframeNumber);
|
||||
subframeNoItem->setData(Qt::DisplayRole, radiosonde->m_subframeNumber);
|
||||
subframeItem->setText(radiosonde->m_subframe.toHex());
|
||||
if (m_subframes.contains(radiosonde->m_serial))
|
||||
{
|
||||
subframe = m_subframes.value(radiosonde->m_serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
subframe = new RS41Subframe();
|
||||
m_subframes.insert(radiosonde->m_serial, subframe);
|
||||
}
|
||||
subframe->update(radiosonde);
|
||||
}
|
||||
|
||||
if (radiosonde->m_posValid)
|
||||
{
|
||||
latitudeItem->setData(Qt::DisplayRole, radiosonde->m_latitude);
|
||||
longitudeItem->setData(Qt::DisplayRole, radiosonde->m_longitude);
|
||||
altitudeItem->setData(Qt::DisplayRole, radiosonde->m_height);
|
||||
speedItem->setData(Qt::DisplayRole, Units::kmpsToKPH(radiosonde->m_speed/1000.0));
|
||||
verticalRateItem->setData(Qt::DisplayRole, radiosonde->m_verticalRate);
|
||||
headingItem->setData(Qt::DisplayRole, radiosonde->m_heading);
|
||||
gpsSatsItem->setData(Qt::DisplayRole, radiosonde->m_satellitesUsed);
|
||||
}
|
||||
|
||||
if (radiosonde->m_gpsInfoValid)
|
||||
{
|
||||
gpsTimeItem->setData(Qt::DisplayRole, radiosonde->m_gpsDateTime);
|
||||
}
|
||||
|
||||
if (radiosonde->m_measValid && subframe)
|
||||
{
|
||||
pressureItem->setData(Qt::DisplayRole, radiosonde->getPressureString(subframe));
|
||||
tempItem->setData(Qt::DisplayRole, radiosonde->getTemperatureString(subframe));
|
||||
humidityItem->setData(Qt::DisplayRole, radiosonde->getHumidityString(subframe));
|
||||
}
|
||||
|
||||
eccItem->setData(Qt::DisplayRole, errorsCorrected);
|
||||
thItem->setData(Qt::DisplayRole, threshold);
|
||||
|
||||
ui->frames->setSortingEnabled(true);
|
||||
if (scrollToBottom) {
|
||||
ui->frames->scrollToBottom();
|
||||
}
|
||||
filterRow(row);
|
||||
|
||||
delete radiosonde;
|
||||
}
|
||||
|
||||
bool RadiosondeDemodGUI::handleMessage(const Message& frame)
|
||||
{
|
||||
if (RadiosondeDemod::MsgConfigureRadiosondeDemod::match(frame))
|
||||
{
|
||||
qDebug("RadiosondeDemodGUI::handleMessage: RadiosondeDemod::MsgConfigureRadiosondeDemod");
|
||||
const RadiosondeDemod::MsgConfigureRadiosondeDemod& cfg = (RadiosondeDemod::MsgConfigureRadiosondeDemod&) frame;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
ui->scopeGUI->updateSettings();
|
||||
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else if (RadiosondeDemod::MsgMessage::match(frame))
|
||||
{
|
||||
RadiosondeDemod::MsgMessage& report = (RadiosondeDemod::MsgMessage&) frame;
|
||||
frameReceived(report.getMessage(), report.getDateTime(), report.getErrorsCorrected(), report.getThreshold());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::handleInputMessages()
|
||||
{
|
||||
Message* frame;
|
||||
|
||||
while ((frame = getInputMessageQueue()->pop()) != 0)
|
||||
{
|
||||
if (handleMessage(*frame))
|
||||
{
|
||||
delete frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::channelMarkerChangedByCursor()
|
||||
{
|
||||
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
|
||||
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::channelMarkerHighlightedByCursor()
|
||||
{
|
||||
setHighlighted(m_channelMarker.getHighlighted());
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_deltaFrequency_changed(qint64 value)
|
||||
{
|
||||
m_channelMarker.setCenterFrequency(value);
|
||||
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_rfBW_valueChanged(int value)
|
||||
{
|
||||
float bw = value * 100.0f;
|
||||
ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
|
||||
m_channelMarker.setBandwidth(bw);
|
||||
m_settings.m_rfBandwidth = bw;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_fmDev_valueChanged(int value)
|
||||
{
|
||||
ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
|
||||
m_settings.m_fmDeviation = value * 100.0;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_threshold_valueChanged(int value)
|
||||
{
|
||||
ui->thresholdText->setText(QString("%1").arg(value));
|
||||
m_settings.m_correlationThreshold = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_filterSerial_editingFinished()
|
||||
{
|
||||
m_settings.m_filterSerial = ui->filterSerial->text();
|
||||
filter();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_clearTable_clicked()
|
||||
{
|
||||
ui->frames->setRowCount(0);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_udpEnabled_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_udpEnabled = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_udpAddress_editingFinished()
|
||||
{
|
||||
m_settings.m_udpAddress = ui->udpAddress->text();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_udpPort_editingFinished()
|
||||
{
|
||||
m_settings.m_udpPort = ui->udpPort->text().toInt();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_channel1_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_scopeCh1 = index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_channel2_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_scopeCh2 = index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_frames_cellDoubleClicked(int row, int column)
|
||||
{
|
||||
// Get serial in row double clicked
|
||||
QString serial = ui->frames->item(row, FRAME_COL_SERIAL)->text();
|
||||
if (column == FRAME_COL_SERIAL)
|
||||
{
|
||||
// Search for Serial on sondehub
|
||||
QDesktopServices::openUrl(QUrl(QString("https://sondehub.org/?f=%1#!mt=Mapnik&f=%1&q=%1").arg(serial)));
|
||||
}
|
||||
else if ((column == FRAME_COL_LATITUDE) || (column == FRAME_COL_LONGITUDE))
|
||||
{
|
||||
// Find serial on Map
|
||||
FeatureWebAPIUtils::mapFind(serial);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::filterRow(int row)
|
||||
{
|
||||
bool hidden = false;
|
||||
if (m_settings.m_filterSerial != "")
|
||||
{
|
||||
QRegExp re(m_settings.m_filterSerial);
|
||||
QTableWidgetItem *fromItem = ui->frames->item(row, FRAME_COL_SERIAL);
|
||||
if (!re.exactMatch(fromItem->text()))
|
||||
hidden = true;
|
||||
}
|
||||
ui->frames->setRowHidden(row, hidden);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::filter()
|
||||
{
|
||||
for (int i = 0; i < ui->frames->rowCount(); i++)
|
||||
{
|
||||
filterRow(i);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
if (widget == ui->scopeContainer)
|
||||
{
|
||||
if (rollDown)
|
||||
{
|
||||
// Make wide enough for scope controls
|
||||
setMinimumWidth(716);
|
||||
}
|
||||
else
|
||||
{
|
||||
setMinimumWidth(352);
|
||||
}
|
||||
}
|
||||
|
||||
saveState(m_rollupState);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::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_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_radiosondeDemod->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();
|
||||
}
|
||||
|
||||
RadiosondeDemodGUI::RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
|
||||
ChannelGUI(parent),
|
||||
ui(new Ui::RadiosondeDemodGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_channelMarker(this),
|
||||
m_doApplySettings(true),
|
||||
m_tickCount(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_helpURL = "plugins/channelrx/demodradiosonde/readme.md";
|
||||
|
||||
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_radiosondeDemod = reinterpret_cast<RadiosondeDemod*>(rxChannel);
|
||||
m_radiosondeDemod->setMessageQueueToGUI(getInputMessageQueue());
|
||||
|
||||
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
|
||||
|
||||
m_scopeVis = m_radiosondeDemod->getScopeSink();
|
||||
m_scopeVis->setGLScope(ui->glScope);
|
||||
ui->glScope->connectTimer(MainCore::instance()->getMasterTimer());
|
||||
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
|
||||
|
||||
// Scope settings to display the IQ waveforms
|
||||
ui->scopeGUI->setPreTrigger(1);
|
||||
GLScopeSettings::TraceData traceDataI, traceDataQ;
|
||||
traceDataI.m_projectionType = Projector::ProjectionReal;
|
||||
traceDataI.m_amp = 1.0; // for -1 to +1
|
||||
traceDataI.m_ofs = 0.0; // vertical offset
|
||||
traceDataQ.m_projectionType = Projector::ProjectionImag;
|
||||
traceDataQ.m_amp = 1.0;
|
||||
traceDataQ.m_ofs = 0.0;
|
||||
ui->scopeGUI->changeTrace(0, traceDataI);
|
||||
ui->scopeGUI->addTrace(traceDataQ);
|
||||
ui->scopeGUI->setDisplayMode(GLScopeSettings::DisplayXYV);
|
||||
ui->scopeGUI->focusOnTrace(0); // re-focus to take changes into account in the GUI
|
||||
|
||||
GLScopeSettings::TriggerData triggerData;
|
||||
triggerData.m_triggerLevel = 0.1;
|
||||
triggerData.m_triggerLevelCoarse = 10;
|
||||
triggerData.m_triggerPositiveEdge = true;
|
||||
ui->scopeGUI->changeTrigger(0, triggerData);
|
||||
ui->scopeGUI->focusOnTrigger(0); // re-focus to take changes into account in the GUI
|
||||
|
||||
m_scopeVis->setLiveRate(9600*6);
|
||||
//m_scopeVis->setFreeRun(false); // FIXME: add method rather than call m_scopeVis->configure()
|
||||
|
||||
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
|
||||
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
|
||||
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
|
||||
|
||||
m_channelMarker.blockSignals(true);
|
||||
m_channelMarker.setColor(Qt::yellow);
|
||||
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
|
||||
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
|
||||
m_channelMarker.setTitle("Radiosonde Demodulator");
|
||||
m_channelMarker.blockSignals(false);
|
||||
m_channelMarker.setVisible(true); // activate signal on the last setting only
|
||||
|
||||
setTitleColor(m_channelMarker.getColor());
|
||||
m_settings.setChannelMarker(&m_channelMarker);
|
||||
m_settings.setScopeGUI(ui->scopeGUI);
|
||||
m_settings.setRollupState(&m_rollupState);
|
||||
|
||||
m_deviceUISet->addChannelMarker(&m_channelMarker);
|
||||
m_deviceUISet->addRollupWidget(this);
|
||||
|
||||
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
|
||||
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
// Resize the table using dummy data
|
||||
resizeTable();
|
||||
// Allow user to reorder columns
|
||||
ui->frames->horizontalHeader()->setSectionsMovable(true);
|
||||
// Allow user to sort table by clicking on headers
|
||||
ui->frames->setSortingEnabled(true);
|
||||
// Add context menu to allow hiding/showing of columns
|
||||
framesMenu = new QMenu(ui->frames);
|
||||
for (int i = 0; i < ui->frames->horizontalHeader()->count(); i++)
|
||||
{
|
||||
QString text = ui->frames->horizontalHeaderItem(i)->text();
|
||||
framesMenu->addAction(createCheckableItem(text, i, true, SLOT(framesColumnSelectMenuChecked())));
|
||||
}
|
||||
ui->frames->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->frames->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(framesColumnSelectMenu(QPoint)));
|
||||
// Get signals when columns change
|
||||
connect(ui->frames->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(frames_sectionMoved(int, int, int)));
|
||||
connect(ui->frames->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(frames_sectionResized(int, int, int)));
|
||||
ui->frames->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->frames, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customContextMenuRequested(QPoint)));
|
||||
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_DATE, new DateTimeDelegate("yyyy/MM/dd"));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_TIME, new TimeDelegate());
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_LATITUDE, new DecimalDelegate(5));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_LONGITUDE, new DecimalDelegate(5));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_ALTITUDE, new DecimalDelegate(1));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_SPEED, new DecimalDelegate(1));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_VERTICAL_RATE, new DecimalDelegate(1));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_HEADING, new DecimalDelegate(1));
|
||||
ui->frames->setItemDelegateForColumn(FRAME_COL_GPS_TIME, new DateTimeDelegate("yyyy/MM/dd hh:mm:ss"));
|
||||
|
||||
ui->scopeContainer->setVisible(false);
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::customContextMenuRequested(QPoint pos)
|
||||
{
|
||||
QTableWidgetItem *item = ui->frames->itemAt(pos);
|
||||
if (item)
|
||||
{
|
||||
int row = item->row();
|
||||
QString serial = ui->frames->item(row, FRAME_COL_SERIAL)->text();
|
||||
|
||||
QMenu* tableContextMenu = new QMenu(ui->frames);
|
||||
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
|
||||
|
||||
// Copy current cell
|
||||
QAction* copyAction = new QAction("Copy", tableContextMenu);
|
||||
const QString text = item->text();
|
||||
connect(copyAction, &QAction::triggered, this, [text]()->void {
|
||||
QClipboard *clipboard = QGuiApplication::clipboard();
|
||||
clipboard->setText(text);
|
||||
});
|
||||
tableContextMenu->addAction(copyAction);
|
||||
|
||||
// View radiosonde on various websites
|
||||
QAction* mmsiRadiosondeHubAction = new QAction(QString("View %1 on sondehub.net...").arg(serial), tableContextMenu);
|
||||
connect(mmsiRadiosondeHubAction, &QAction::triggered, this, [serial]()->void {
|
||||
QDesktopServices::openUrl(QUrl(QString("https://sondehub.org/?f=%1#!mt=Mapnik&f=%1&q=%1").arg(serial)));
|
||||
});
|
||||
tableContextMenu->addAction(mmsiRadiosondeHubAction);
|
||||
|
||||
// Find on Map
|
||||
tableContextMenu->addSeparator();
|
||||
QAction* findMapFeatureAction = new QAction(QString("Find %1 on map").arg(serial), tableContextMenu);
|
||||
connect(findMapFeatureAction, &QAction::triggered, this, [serial]()->void {
|
||||
FeatureWebAPIUtils::mapFind(serial);
|
||||
});
|
||||
tableContextMenu->addAction(findMapFeatureAction);
|
||||
|
||||
tableContextMenu->popup(ui->frames->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
RadiosondeDemodGUI::~RadiosondeDemodGUI()
|
||||
{
|
||||
delete ui;
|
||||
qDeleteAll(m_subframes);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::blockApplySettings(bool block)
|
||||
{
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::applySettings(bool force)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
RadiosondeDemod::MsgConfigureRadiosondeDemod* frame = RadiosondeDemod::MsgConfigureRadiosondeDemod::create( m_settings, force);
|
||||
m_radiosondeDemod->getInputMessageQueue()->push(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::displaySettings()
|
||||
{
|
||||
m_channelMarker.blockSignals(true);
|
||||
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
|
||||
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
|
||||
m_channelMarker.setTitle(m_settings.m_title);
|
||||
m_channelMarker.blockSignals(false);
|
||||
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
|
||||
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
setWindowTitle(m_channelMarker.getTitle());
|
||||
|
||||
blockApplySettings(true);
|
||||
|
||||
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
|
||||
|
||||
ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
|
||||
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
|
||||
|
||||
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1));
|
||||
ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0);
|
||||
|
||||
ui->thresholdText->setText(QString("%1").arg(m_settings.m_correlationThreshold));
|
||||
ui->threshold->setValue(m_settings.m_correlationThreshold);
|
||||
|
||||
displayStreamIndex();
|
||||
|
||||
ui->filterSerial->setText(m_settings.m_filterSerial);
|
||||
|
||||
ui->udpEnabled->setChecked(m_settings.m_udpEnabled);
|
||||
ui->udpAddress->setText(m_settings.m_udpAddress);
|
||||
ui->udpPort->setText(QString::number(m_settings.m_udpPort));
|
||||
|
||||
ui->channel1->setCurrentIndex(m_settings.m_scopeCh1);
|
||||
ui->channel2->setCurrentIndex(m_settings.m_scopeCh2);
|
||||
|
||||
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
||||
ui->logEnable->setChecked(m_settings.m_logEnabled);
|
||||
|
||||
// Order and size columns
|
||||
QHeaderView *header = ui->frames->horizontalHeader();
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++)
|
||||
{
|
||||
bool hidden = m_settings.m_frameColumnSizes[i] == 0;
|
||||
header->setSectionHidden(i, hidden);
|
||||
framesMenu->actions().at(i)->setChecked(!hidden);
|
||||
if (m_settings.m_frameColumnSizes[i] > 0)
|
||||
ui->frames->setColumnWidth(i, m_settings.m_frameColumnSizes[i]);
|
||||
header->moveSection(header->visualIndex(i), m_settings.m_frameColumnIndexes[i]);
|
||||
}
|
||||
|
||||
filter();
|
||||
|
||||
restoreState(m_rollupState);
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::displayStreamIndex()
|
||||
{
|
||||
if (m_deviceUISet->m_deviceMIMOEngine) {
|
||||
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
|
||||
} else {
|
||||
setStreamIndicator("S"); // single channel indicator
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::leaveEvent(QEvent*)
|
||||
{
|
||||
m_channelMarker.setHighlighted(false);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::enterEvent(QEvent*)
|
||||
{
|
||||
m_channelMarker.setHighlighted(true);
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::tick()
|
||||
{
|
||||
double magsqAvg, magsqPeak;
|
||||
int nbMagsqSamples;
|
||||
m_radiosondeDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
|
||||
double powDbAvg = CalcDb::dbPower(magsqAvg);
|
||||
double powDbPeak = CalcDb::dbPower(magsqPeak);
|
||||
|
||||
ui->channelPowerMeter->levelChanged(
|
||||
(100.0f + powDbAvg) / 100.0f,
|
||||
(100.0f + powDbPeak) / 100.0f,
|
||||
nbMagsqSamples);
|
||||
|
||||
if (m_tickCount % 4 == 0) {
|
||||
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
|
||||
}
|
||||
|
||||
m_tickCount++;
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_logEnable_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_logEnabled = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadiosondeDemodGUI::on_logFilename_clicked()
|
||||
{
|
||||
// Get filename to save to
|
||||
QFileDialog fileDialog(nullptr, "Select file to log received frames to", "", "*.csv");
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
if (fileDialog.exec())
|
||||
{
|
||||
QStringList fileNames = fileDialog.selectedFiles();
|
||||
if (fileNames.size() > 0)
|
||||
{
|
||||
m_settings.m_logFilename = fileNames[0];
|
||||
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read .csv log and process as received frames
|
||||
void RadiosondeDemodGUI::on_logOpen_clicked()
|
||||
{
|
||||
QFileDialog fileDialog(nullptr, "Select .csv log file to read", "", "*.csv");
|
||||
if (fileDialog.exec())
|
||||
{
|
||||
QStringList fileNames = fileDialog.selectedFiles();
|
||||
if (fileNames.size() > 0)
|
||||
{
|
||||
QFile file(fileNames[0]);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
QTextStream in(&file);
|
||||
QString error;
|
||||
QHash<QString, int> colIndexes = CSV::readHeader(in, {"Date", "Time", "Data"}, error);
|
||||
if (error.isEmpty())
|
||||
{
|
||||
int dateCol = colIndexes.value("Date");
|
||||
int timeCol = colIndexes.value("Time");
|
||||
int dataCol = colIndexes.value("Data");
|
||||
int maxCol = std::max({dateCol, timeCol, dataCol});
|
||||
|
||||
QMessageBox dialog(this);
|
||||
dialog.setText("Reading frames");
|
||||
dialog.addButton(QMessageBox::Cancel);
|
||||
dialog.show();
|
||||
QApplication::processEvents();
|
||||
int count = 0;
|
||||
bool cancelled = false;
|
||||
QStringList cols;
|
||||
|
||||
MessagePipesLegacy& framePipes = MainCore::instance()->getMessagePipes();
|
||||
QList<MessageQueue*> *radiosondeMessageQueues = framePipes.getMessageQueues(m_radiosondeDemod, "radiosonde");
|
||||
|
||||
while (!cancelled && CSV::readRow(in, &cols))
|
||||
{
|
||||
if (cols.size() > maxCol)
|
||||
{
|
||||
QDate date = QDate::fromString(cols[dateCol]);
|
||||
QTime time = QTime::fromString(cols[timeCol]);
|
||||
QDateTime dateTime(date, time);
|
||||
QByteArray bytes = QByteArray::fromHex(cols[dataCol].toLatin1());
|
||||
|
||||
// Add to table
|
||||
frameReceived(bytes, dateTime, 0, 0);
|
||||
|
||||
// Forward to Radiosonde feature
|
||||
if (radiosondeMessageQueues)
|
||||
{
|
||||
QList<MessageQueue*>::iterator it = radiosondeMessageQueues->begin();
|
||||
for (; it != radiosondeMessageQueues->end(); ++it)
|
||||
{
|
||||
MainCore::MsgPacket *msg = MainCore::MsgPacket::create(m_radiosondeDemod, bytes, dateTime);
|
||||
(*it)->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (count % 100 == 0)
|
||||
{
|
||||
QApplication::processEvents();
|
||||
if (dialog.clickedButton()) {
|
||||
cancelled = true;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
dialog.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, "Radiosonde Demod", error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, "Radiosonde Demod", QString("Failed to open file %1").arg(fileNames[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
155
plugins/channelrx/demodradiosonde/radiosondedemodgui.h
Normal file
155
plugins/channelrx/demodradiosonde/radiosondedemodgui.h
Normal file
@ -0,0 +1,155 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMODGUI_H
|
||||
#define INCLUDE_RADIOSONDEDEMODGUI_H
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QMenu>
|
||||
|
||||
#include "channel/channelgui.h"
|
||||
#include "dsp/channelmarker.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "settings/rollupstate.h"
|
||||
|
||||
#include "radiosondedemodsettings.h"
|
||||
#include "radiosondedemod.h"
|
||||
|
||||
class PluginAPI;
|
||||
class DeviceUISet;
|
||||
class BasebandSampleSink;
|
||||
class ScopeVis;
|
||||
class ScopeVisXY;
|
||||
class RadiosondeDemod;
|
||||
class RadiosondeDemodGUI;
|
||||
class RS41Frame;
|
||||
|
||||
namespace Ui {
|
||||
class RadiosondeDemodGUI;
|
||||
}
|
||||
|
||||
class RadiosondeDemodGUI : public ChannelGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static RadiosondeDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
|
||||
virtual void destroy();
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
|
||||
public slots:
|
||||
void channelMarkerChangedByCursor();
|
||||
void channelMarkerHighlightedByCursor();
|
||||
|
||||
private:
|
||||
Ui::RadiosondeDemodGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
DeviceUISet* m_deviceUISet;
|
||||
ChannelMarker m_channelMarker;
|
||||
RollupState m_rollupState;
|
||||
RadiosondeDemodSettings m_settings;
|
||||
bool m_doApplySettings;
|
||||
ScopeVis* m_scopeVis;
|
||||
|
||||
RadiosondeDemod* m_radiosondeDemod;
|
||||
uint32_t m_tickCount;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
QMenu *framesMenu; // Column select context menu
|
||||
QMenu *copyMenu;
|
||||
|
||||
QHash<QString, RS41Subframe *> m_subframes; // Hash of serial to subframes
|
||||
|
||||
explicit RadiosondeDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~RadiosondeDemodGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displayStreamIndex();
|
||||
void frameReceived(const QByteArray& frame, const QDateTime& dateTime, int errorsCorrected, int threshold);
|
||||
bool handleMessage(const Message& message);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
||||
void resizeTable();
|
||||
QAction *createCheckableItem(QString& text, int idx, bool checked, const char *slot);
|
||||
|
||||
enum MessageCol {
|
||||
FRAME_COL_DATE,
|
||||
FRAME_COL_TIME,
|
||||
FRAME_COL_SERIAL,
|
||||
FRAME_COL_FRAME_NUMBER,
|
||||
FRAME_COL_FLIGHT_PHASE,
|
||||
FRAME_COL_LATITUDE,
|
||||
FRAME_COL_LONGITUDE,
|
||||
FRAME_COL_ALTITUDE,
|
||||
FRAME_COL_SPEED,
|
||||
FRAME_COL_VERTICAL_RATE,
|
||||
FRAME_COL_HEADING,
|
||||
FRAME_COL_PRESSURE,
|
||||
FRAME_COL_TEMP,
|
||||
FRAME_COL_HUMIDITY,
|
||||
FRAME_COL_BATTERY_VOLTAGE,
|
||||
FRAME_COL_BATTERY_STATUS,
|
||||
FRAME_COL_PCB_TEMP,
|
||||
FRAME_COL_HUMIDITY_PWM,
|
||||
FRAME_COL_TX_POWER,
|
||||
FRAME_COL_MAX_SUBFRAME_NO,
|
||||
FRAME_COL_SUBFRAME_NO,
|
||||
FRAME_COL_SUBFRAME,
|
||||
FRAME_COL_GPS_TIME,
|
||||
FRAME_COL_GPS_SATS,
|
||||
FRAME_COL_ECC,
|
||||
FRAME_COL_CORR
|
||||
};
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_rfBW_valueChanged(int index);
|
||||
void on_fmDev_valueChanged(int value);
|
||||
void on_threshold_valueChanged(int value);
|
||||
void on_filterSerial_editingFinished();
|
||||
void on_clearTable_clicked();
|
||||
void on_udpEnabled_clicked(bool checked);
|
||||
void on_udpAddress_editingFinished();
|
||||
void on_udpPort_editingFinished();
|
||||
void on_channel1_currentIndexChanged(int index);
|
||||
void on_channel2_currentIndexChanged(int index);
|
||||
void on_frames_cellDoubleClicked(int row, int column);
|
||||
void on_logEnable_clicked(bool checked=false);
|
||||
void on_logFilename_clicked();
|
||||
void on_logOpen_clicked();
|
||||
void filterRow(int row);
|
||||
void filter();
|
||||
void frames_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
|
||||
void frames_sectionResized(int logicalIndex, int oldSize, int newSize);
|
||||
void framesColumnSelectMenu(QPoint pos);
|
||||
void framesColumnSelectMenuChecked(bool checked = false);
|
||||
void customContextMenuRequested(QPoint point);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void handleInputMessages();
|
||||
void tick();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMODGUI_H
|
1116
plugins/channelrx/demodradiosonde/radiosondedemodgui.ui
Normal file
1116
plugins/channelrx/demodradiosonde/radiosondedemodgui.ui
Normal file
File diff suppressed because it is too large
Load Diff
92
plugins/channelrx/demodradiosonde/radiosondedemodplugin.cpp
Normal file
92
plugins/channelrx/demodradiosonde/radiosondedemodplugin.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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"
|
||||
|
||||
#ifndef SERVER_MODE
|
||||
#include "radiosondedemodgui.h"
|
||||
#endif
|
||||
#include "radiosondedemod.h"
|
||||
#include "radiosondedemodwebapiadapter.h"
|
||||
#include "radiosondedemodplugin.h"
|
||||
|
||||
const PluginDescriptor RadiosondeDemodPlugin::m_pluginDescriptor = {
|
||||
RadiosondeDemod::m_channelId,
|
||||
QStringLiteral("Radiosonde Demodulator"),
|
||||
QStringLiteral("6.20.0"),
|
||||
QStringLiteral("(c) Jon Beniston, M7RCE"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
RadiosondeDemodPlugin::RadiosondeDemodPlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(0)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& RadiosondeDemodPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void RadiosondeDemodPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerRxChannel(RadiosondeDemod::m_channelIdURI, RadiosondeDemod::m_channelId, this);
|
||||
}
|
||||
|
||||
void RadiosondeDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
|
||||
{
|
||||
if (bs || cs)
|
||||
{
|
||||
RadiosondeDemod *instance = new RadiosondeDemod(deviceAPI);
|
||||
|
||||
if (bs) {
|
||||
*bs = instance;
|
||||
}
|
||||
|
||||
if (cs) {
|
||||
*cs = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
ChannelGUI* RadiosondeDemodPlugin::createRxChannelGUI(
|
||||
DeviceUISet *deviceUISet,
|
||||
BasebandSampleSink *rxChannel) const
|
||||
{
|
||||
(void) deviceUISet;
|
||||
(void) rxChannel;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
ChannelGUI* RadiosondeDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
|
||||
{
|
||||
return RadiosondeDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
|
||||
}
|
||||
#endif
|
||||
|
||||
ChannelWebAPIAdapter* RadiosondeDemodPlugin::createChannelWebAPIAdapter() const
|
||||
{
|
||||
return new RadiosondeDemodWebAPIAdapter();
|
||||
}
|
49
plugins/channelrx/demodradiosonde/radiosondedemodplugin.h
Normal file
49
plugins/channelrx/demodradiosonde/radiosondedemodplugin.h
Normal file
@ -0,0 +1,49 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMODPLUGIN_H
|
||||
#define INCLUDE_RADIOSONDEDEMODPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class DeviceUISet;
|
||||
class BasebandSampleSink;
|
||||
|
||||
class RadiosondeDemodPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.channel.radiosondedemod")
|
||||
|
||||
public:
|
||||
explicit RadiosondeDemodPlugin(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;
|
||||
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMODPLUGIN_H
|
200
plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp
Normal file
200
plugins/channelrx/demodradiosonde/radiosondedemodsettings.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "radiosondedemodsettings.h"
|
||||
|
||||
RadiosondeDemodSettings::RadiosondeDemodSettings() :
|
||||
m_channelMarker(nullptr),
|
||||
m_scopeGUI(nullptr),
|
||||
m_rollupState(nullptr)
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void RadiosondeDemodSettings::resetToDefaults()
|
||||
{
|
||||
m_baud = 4800; // Fixed for RS41 - may change for others
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_rfBandwidth = 9600.0f;
|
||||
m_fmDeviation = 2400.0f;
|
||||
m_correlationThreshold = 450;
|
||||
m_filterSerial = "";
|
||||
m_udpEnabled = false;
|
||||
m_udpAddress = "127.0.0.1";
|
||||
m_udpPort = 9999;
|
||||
m_scopeCh1 = 5;
|
||||
m_scopeCh2 = 6;
|
||||
m_logFilename = "radiosonde_log.csv";
|
||||
m_logEnabled = false;
|
||||
m_rgbColor = QColor(102, 0, 102).rgb();
|
||||
m_title = "Radiosonde Demodulator";
|
||||
m_streamIndex = 0;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
m_reverseAPIChannelIndex = 0;
|
||||
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++)
|
||||
{
|
||||
m_frameColumnIndexes[i] = i;
|
||||
m_frameColumnSizes[i] = -1; // Autosize
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray RadiosondeDemodSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_inputFrequencyOffset);
|
||||
s.writeFloat(2, m_rfBandwidth);
|
||||
s.writeFloat(3, m_fmDeviation);
|
||||
s.writeFloat(4, m_correlationThreshold);
|
||||
s.writeString(5, m_filterSerial);
|
||||
s.writeBool(6, m_udpEnabled);
|
||||
s.writeString(7, m_udpAddress);
|
||||
s.writeU32(8, m_udpPort);
|
||||
s.writeS32(10, m_scopeCh1);
|
||||
s.writeS32(11, m_scopeCh2);
|
||||
s.writeU32(12, m_rgbColor);
|
||||
s.writeString(13, m_title);
|
||||
|
||||
if (m_channelMarker) {
|
||||
s.writeBlob(14, m_channelMarker->serialize());
|
||||
}
|
||||
|
||||
s.writeS32(15, m_streamIndex);
|
||||
s.writeBool(16, m_useReverseAPI);
|
||||
s.writeString(17, m_reverseAPIAddress);
|
||||
s.writeU32(18, m_reverseAPIPort);
|
||||
s.writeU32(19, m_reverseAPIDeviceIndex);
|
||||
s.writeU32(20, m_reverseAPIChannelIndex);
|
||||
s.writeBlob(21, m_scopeGUI->serialize());
|
||||
s.writeString(22, m_logFilename);
|
||||
s.writeBool(23, m_logEnabled);
|
||||
s.writeS32(24, m_baud);
|
||||
|
||||
if (m_rollupState) {
|
||||
s.writeBlob(25, m_rollupState->serialize());
|
||||
}
|
||||
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++)
|
||||
s.writeS32(100 + i, m_frameColumnIndexes[i]);
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++)
|
||||
s.writeS32(200 + i, m_frameColumnSizes[i]);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool RadiosondeDemodSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if(!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
uint32_t utmp;
|
||||
QString strtmp;
|
||||
|
||||
d.readS32(1, &m_inputFrequencyOffset, 0);
|
||||
d.readFloat(2, &m_rfBandwidth, 16000.0f);
|
||||
d.readFloat(3, &m_fmDeviation, 4800.0f);
|
||||
d.readFloat(4, &m_correlationThreshold, 450);
|
||||
d.readString(5, &m_filterSerial, "");
|
||||
d.readBool(6, &m_udpEnabled);
|
||||
d.readString(7, &m_udpAddress);
|
||||
d.readU32(8, &utmp);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_udpPort = utmp;
|
||||
} else {
|
||||
m_udpPort = 9999;
|
||||
}
|
||||
|
||||
d.readS32(10, &m_scopeCh1, 0);
|
||||
d.readS32(11, &m_scopeCh2, 0);
|
||||
d.readU32(12, &m_rgbColor, QColor(102, 0, 102).rgb());
|
||||
d.readString(13, &m_title, "Radiosonde Demodulator");
|
||||
|
||||
if (m_channelMarker)
|
||||
{
|
||||
d.readBlob(14, &bytetmp);
|
||||
m_channelMarker->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readS32(15, &m_streamIndex, 0);
|
||||
d.readBool(16, &m_useReverseAPI, false);
|
||||
d.readString(17, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(18, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(19, &utmp, 0);
|
||||
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(20, &utmp, 0);
|
||||
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
if (m_scopeGUI)
|
||||
{
|
||||
d.readBlob(21, &bytetmp);
|
||||
m_scopeGUI->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readString(22, &m_logFilename, "radiosonde_log.csv");
|
||||
d.readBool(23, &m_logEnabled, false);
|
||||
d.readS32(24, &m_baud, 9600);
|
||||
|
||||
if (m_rollupState)
|
||||
{
|
||||
d.readBlob(25, &bytetmp);
|
||||
m_rollupState->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) {
|
||||
d.readS32(100 + i, &m_frameColumnIndexes[i], i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RADIOSONDEDEMOD_FRAME_COLUMNS; i++) {
|
||||
d.readS32(200 + i, &m_frameColumnSizes[i], -1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
75
plugins/channelrx/demodradiosonde/radiosondedemodsettings.h
Normal file
75
plugins/channelrx/demodradiosonde/radiosondedemodsettings.h
Normal file
@ -0,0 +1,75 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMODSETTINGS_H
|
||||
#define INCLUDE_RADIOSONDEDEMODSETTINGS_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
|
||||
class Serializable;
|
||||
|
||||
// Number of columns in the tables
|
||||
#define RADIOSONDEDEMOD_FRAME_COLUMNS 26
|
||||
|
||||
struct RadiosondeDemodSettings
|
||||
{
|
||||
qint32 m_baud;
|
||||
qint32 m_inputFrequencyOffset;
|
||||
Real m_rfBandwidth;
|
||||
Real m_fmDeviation;
|
||||
Real m_correlationThreshold;
|
||||
QString m_filterSerial;
|
||||
bool m_udpEnabled;
|
||||
QString m_udpAddress;
|
||||
uint16_t m_udpPort;
|
||||
int m_scopeCh1;
|
||||
int m_scopeCh2;
|
||||
|
||||
QString m_logFilename;
|
||||
bool m_logEnabled;
|
||||
|
||||
quint32 m_rgbColor;
|
||||
QString m_title;
|
||||
Serializable *m_channelMarker;
|
||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
uint16_t m_reverseAPIChannelIndex;
|
||||
Serializable *m_scopeGUI;
|
||||
Serializable *m_rollupState;
|
||||
|
||||
int m_frameColumnIndexes[RADIOSONDEDEMOD_FRAME_COLUMNS];//!< How the columns are ordered in the table
|
||||
int m_frameColumnSizes[RADIOSONDEDEMOD_FRAME_COLUMNS]; //!< Size of the columns in the table
|
||||
|
||||
static const int RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE = 57600; //!< 12x 4800 baud rate (use even multiple so Gausian filter has odd number of taps)
|
||||
|
||||
RadiosondeDemodSettings();
|
||||
void resetToDefaults();
|
||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_RADIOSONDEDEMODSETTINGS_H */
|
576
plugins/channelrx/demodradiosonde/radiosondedemodsink.cpp
Normal file
576
plugins/channelrx/demodradiosonde/radiosondedemodsink.cpp
Normal file
@ -0,0 +1,576 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
|
||||
#include <complex.h>
|
||||
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/datafifo.h"
|
||||
#include "dsp/scopevis.h"
|
||||
#include "util/db.h"
|
||||
#include "util/stepfunctions.h"
|
||||
#include "util/reedsolomon.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "radiosondedemod.h"
|
||||
#include "radiosondedemodsink.h"
|
||||
|
||||
const uint8_t RadiosondeDemodSink::m_descramble[64] = {
|
||||
0x96, 0x83, 0x3E, 0x51, 0xB1, 0x49, 0x08, 0x98,
|
||||
0x32, 0x05, 0x59, 0x0E, 0xF9, 0x44, 0xC6, 0x26,
|
||||
0x21, 0x60, 0xC2, 0xEA, 0x79, 0x5D, 0x6D, 0xA1,
|
||||
0x54, 0x69, 0x47, 0x0C, 0xDC, 0xE8, 0x5C, 0xF1,
|
||||
0xF7, 0x76, 0x82, 0x7F, 0x07, 0x99, 0xA2, 0x2C,
|
||||
0x93, 0x7C, 0x30, 0x63, 0xF5, 0x10, 0x2E, 0x61,
|
||||
0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23,
|
||||
0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1
|
||||
};
|
||||
|
||||
RadiosondeDemodSink::RadiosondeDemodSink(RadiosondeDemod *radiosondeDemod) :
|
||||
m_scopeSink(nullptr),
|
||||
m_radiosondeDemod(radiosondeDemod),
|
||||
m_channelSampleRate(RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_magsqSum(0.0f),
|
||||
m_magsqPeak(0.0f),
|
||||
m_magsqCount(0),
|
||||
m_messageQueueToChannel(nullptr),
|
||||
m_rxBuf(nullptr),
|
||||
m_train(nullptr),
|
||||
m_sampleBufferIndex(0)
|
||||
{
|
||||
m_magsq = 0.0;
|
||||
|
||||
m_demodBuffer.resize(1<<12);
|
||||
m_demodBufferFill = 0;
|
||||
m_sampleBuffer.resize(m_sampleBufferSize);
|
||||
|
||||
applySettings(m_settings, true);
|
||||
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
||||
}
|
||||
|
||||
RadiosondeDemodSink::~RadiosondeDemodSink()
|
||||
{
|
||||
delete[] m_rxBuf;
|
||||
delete[] m_train;
|
||||
}
|
||||
|
||||
void RadiosondeDemodSink::sampleToScope(Complex sample)
|
||||
{
|
||||
if (m_scopeSink)
|
||||
{
|
||||
Real r = std::real(sample) * SDR_RX_SCALEF;
|
||||
Real i = std::imag(sample) * SDR_RX_SCALEF;
|
||||
m_sampleBuffer[m_sampleBufferIndex++] = Sample(r, i);
|
||||
|
||||
if (m_sampleBufferIndex == m_sampleBufferSize)
|
||||
{
|
||||
std::vector<SampleVector::const_iterator> vbegin;
|
||||
vbegin.push_back(m_sampleBuffer.begin());
|
||||
m_scopeSink->feed(vbegin, m_sampleBufferSize);
|
||||
m_sampleBufferIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
Complex ci;
|
||||
|
||||
for (SampleVector::const_iterator it = begin; it != end; ++it)
|
||||
{
|
||||
Complex c(it->real(), it->imag());
|
||||
c *= m_nco.nextIQ();
|
||||
|
||||
if (m_interpolatorDistance < 1.0f) // interpolate
|
||||
{
|
||||
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
{
|
||||
processOneSample(ci);
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
}
|
||||
}
|
||||
else // decimate
|
||||
{
|
||||
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
|
||||
{
|
||||
processOneSample(ci);
|
||||
m_interpolatorDistanceRemain += m_interpolatorDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeDemodSink::processOneSample(Complex &ci)
|
||||
{
|
||||
Complex ca;
|
||||
|
||||
// FM demodulation
|
||||
double magsqRaw;
|
||||
Real deviation;
|
||||
Real fmDemod = m_phaseDiscri.phaseDiscriminatorDelta(ci, magsqRaw, deviation);
|
||||
|
||||
// Calculate average and peak levels for level meter
|
||||
Real magsq = magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED);
|
||||
m_movingAverage(magsq);
|
||||
m_magsq = m_movingAverage.asDouble();
|
||||
m_magsqSum += magsq;
|
||||
if (magsq > m_magsqPeak)
|
||||
{
|
||||
m_magsqPeak = magsq;
|
||||
}
|
||||
m_magsqCount++;
|
||||
|
||||
// Gaussian filter
|
||||
Real filt = m_pulseShape.filter(fmDemod);
|
||||
|
||||
// An input frequency offset corresponds to a DC offset after FM demodulation
|
||||
// What frequency offset is RS41 specified too?
|
||||
// We need to remove this, otherwise it may effect the sampling
|
||||
// To calculate what it is, we sum the training sequence, which should be zero
|
||||
|
||||
// Clip, as large noise can result in high correlation
|
||||
// Don't clip to 1.0 - as there may be some DC offset (1k/4.8k max dev=0.2)
|
||||
Real filtClipped;
|
||||
filtClipped = std::fmax(-1.4, std::fmin(1.4, filt));
|
||||
|
||||
// Buffer filtered samples. We buffer enough samples for a max length message
|
||||
// before trying to demod, so false triggering can't make us miss anything
|
||||
m_rxBuf[m_rxBufIdx] = filtClipped;
|
||||
m_rxBufIdx = (m_rxBufIdx + 1) % m_rxBufLength;
|
||||
m_rxBufCnt = std::min(m_rxBufCnt + 1, m_rxBufLength);
|
||||
|
||||
Real corr = 0.0f;
|
||||
bool scopeCRCValid = false;
|
||||
bool scopeCRCInvalid = false;
|
||||
Real dcOffset = 0.0f;
|
||||
bool thresholdMet = false;
|
||||
bool gotSOP = false;
|
||||
|
||||
if ((m_rxBufCnt >= m_rxBufLength))
|
||||
{
|
||||
// Correlate with training sequence
|
||||
corr = correlate(m_rxBufIdx);
|
||||
|
||||
// If we meet threshold, try to demod
|
||||
// Take abs value, to account for both initial phases
|
||||
thresholdMet = fabs(corr) >= m_settings.m_correlationThreshold;
|
||||
if (thresholdMet)
|
||||
{
|
||||
// Try to see if starting at a later sample improves correlation
|
||||
int maxCorrOffset = 0;
|
||||
Real maxCorr;
|
||||
Real initCorr = fabs(corr);
|
||||
do
|
||||
{
|
||||
maxCorr = fabs(corr);
|
||||
maxCorrOffset++;
|
||||
corr = correlate(m_rxBufIdx + maxCorrOffset);
|
||||
}
|
||||
while (fabs(corr) > maxCorr);
|
||||
maxCorrOffset--;
|
||||
|
||||
// Calculate mean of preamble as DC offset (as it should be 0 on an ideal signal)
|
||||
Real trainingSum = 0.0f;
|
||||
for (int i = 0; i < m_correlationLength; i++)
|
||||
{
|
||||
int j = (m_rxBufIdx + maxCorrOffset + i) % m_rxBufLength;
|
||||
trainingSum += m_rxBuf[j];
|
||||
}
|
||||
dcOffset = trainingSum/m_correlationLength;
|
||||
|
||||
// Start demod after (most of) preamble
|
||||
int x = (m_rxBufIdx + maxCorrOffset + m_correlationLength*3/4 + 0) % m_rxBufLength;
|
||||
|
||||
// Attempt to demodulate
|
||||
uint64_t bits = 0;
|
||||
int bitCount = 0;
|
||||
int onesCount = 0;
|
||||
int byteCount = 0;
|
||||
int symbolPrev = 0;
|
||||
QList<int> sampleIdxs;
|
||||
for (int sampleIdx = 0; sampleIdx < m_rxBufLength; sampleIdx += m_samplesPerSymbol)
|
||||
{
|
||||
// Sum and slice
|
||||
// Summing 3 samples seems to give a very small improvement vs just using 1
|
||||
int sampleCnt = 3;
|
||||
int sampleOffset = -1;
|
||||
Real sampleSum = 0.0f;
|
||||
for (int i = 0; i < sampleCnt; i++) {
|
||||
sampleSum += m_rxBuf[(x + sampleOffset + i) % m_rxBufLength] - dcOffset;
|
||||
sampleIdxs.append((x + sampleOffset + i) % m_rxBufLength);
|
||||
}
|
||||
int symbol = sampleSum >= 0.0f ? 1 : 0;
|
||||
|
||||
// Move to next symbol
|
||||
x = (x + m_samplesPerSymbol) % m_rxBufLength;
|
||||
|
||||
// Symbols map directly to bits
|
||||
int bit = symbol;
|
||||
|
||||
// Store in shift reg (little endian)
|
||||
bits |= ((uint64_t)bit) << bitCount;
|
||||
bitCount++;
|
||||
|
||||
if (gotSOP)
|
||||
{
|
||||
if (bitCount == 8)
|
||||
{
|
||||
// Got a complete byte
|
||||
m_bytes[byteCount] = bits;
|
||||
byteCount++;
|
||||
bits = 0;
|
||||
bitCount = 0;
|
||||
|
||||
if (byteCount >= RADIOSONDE_LENGTH_STD)
|
||||
{
|
||||
// Get expected length of frame
|
||||
uint8_t frameType = m_bytes[RADIOSONDE_OFFSET_FRAME_TYPE] ^ m_descramble[RADIOSONDE_OFFSET_FRAME_TYPE];
|
||||
int length = RS41Frame::getFrameLength(frameType);
|
||||
|
||||
// Have we received a complete frame?
|
||||
if (byteCount == length)
|
||||
{
|
||||
int firstError;
|
||||
bool ok = processFrame(length, corr, sampleIdx, &firstError);
|
||||
scopeCRCValid = ok;
|
||||
scopeCRCInvalid = !ok;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bits == 0xf812962211cab610ULL) // Scrambled header
|
||||
{
|
||||
// Start of packet
|
||||
gotSOP = true;
|
||||
bits = 0;
|
||||
bitCount = 0;
|
||||
m_bytes[0] = 0x10;
|
||||
m_bytes[1] = 0xb6;
|
||||
m_bytes[2] = 0xca;
|
||||
m_bytes[3] = 0x11;
|
||||
m_bytes[4] = 0x22;
|
||||
m_bytes[5] = 0x96;
|
||||
m_bytes[6] = 0x12;
|
||||
m_bytes[7] = 0xf8;
|
||||
byteCount = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitCount == 64)
|
||||
{
|
||||
bits >>= 1;
|
||||
bitCount--;
|
||||
}
|
||||
if (sampleIdx >= 16 * 8 * m_samplesPerSymbol)
|
||||
{
|
||||
// Too many bits without receving header
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Select signals to feed to scope
|
||||
Complex scopeSample;
|
||||
switch (m_settings.m_scopeCh1)
|
||||
{
|
||||
case 0:
|
||||
scopeSample.real(ci.real() / SDR_RX_SCALEF);
|
||||
break;
|
||||
case 1:
|
||||
scopeSample.real(ci.imag() / SDR_RX_SCALEF);
|
||||
break;
|
||||
case 2:
|
||||
scopeSample.real(magsq);
|
||||
break;
|
||||
case 3:
|
||||
scopeSample.real(fmDemod);
|
||||
break;
|
||||
case 4:
|
||||
scopeSample.real(filt);
|
||||
break;
|
||||
case 5:
|
||||
scopeSample.real(m_rxBuf[m_rxBufIdx]);
|
||||
break;
|
||||
case 6:
|
||||
scopeSample.real(corr / 100.0);
|
||||
break;
|
||||
case 7:
|
||||
scopeSample.real(thresholdMet);
|
||||
break;
|
||||
case 8:
|
||||
scopeSample.real(gotSOP);
|
||||
break;
|
||||
case 9:
|
||||
scopeSample.real(dcOffset);
|
||||
break;
|
||||
case 10:
|
||||
scopeSample.real(scopeCRCValid ? 1.0 : (scopeCRCInvalid ? -1.0 : 0));
|
||||
break;
|
||||
}
|
||||
switch (m_settings.m_scopeCh2)
|
||||
{
|
||||
case 0:
|
||||
scopeSample.imag(ci.real() / SDR_RX_SCALEF);
|
||||
break;
|
||||
case 1:
|
||||
scopeSample.imag(ci.imag() / SDR_RX_SCALEF);
|
||||
break;
|
||||
case 2:
|
||||
scopeSample.imag(magsq);
|
||||
break;
|
||||
case 3:
|
||||
scopeSample.imag(fmDemod);
|
||||
break;
|
||||
case 4:
|
||||
scopeSample.imag(filt);
|
||||
break;
|
||||
case 5:
|
||||
scopeSample.imag(m_rxBuf[m_rxBufIdx]);
|
||||
break;
|
||||
case 6:
|
||||
scopeSample.imag(corr / 100.0);
|
||||
break;
|
||||
case 7:
|
||||
scopeSample.imag(thresholdMet);
|
||||
break;
|
||||
case 8:
|
||||
scopeSample.imag(gotSOP);
|
||||
break;
|
||||
case 9:
|
||||
scopeSample.imag(dcOffset);
|
||||
break;
|
||||
case 10:
|
||||
scopeSample.imag(scopeCRCValid ? 1.0 : (scopeCRCInvalid ? -1.0 : 0));
|
||||
break;
|
||||
}
|
||||
sampleToScope(scopeSample);
|
||||
|
||||
// Send demod signal to Demod Analzyer feature
|
||||
m_demodBuffer[m_demodBufferFill++] = fmDemod * std::numeric_limits<int16_t>::max();
|
||||
|
||||
if (m_demodBufferFill >= m_demodBuffer.size())
|
||||
{
|
||||
QList<ObjectPipe*> dataPipes;
|
||||
MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes);
|
||||
|
||||
if (dataPipes.size() > 0)
|
||||
{
|
||||
QList<ObjectPipe*>::iterator it = dataPipes.begin();
|
||||
|
||||
for (; it != dataPipes.end(); ++it)
|
||||
{
|
||||
DataFifo *fifo = qobject_cast<DataFifo*>((*it)->m_element);
|
||||
|
||||
if (fifo) {
|
||||
fifo->write((quint8*) &m_demodBuffer[0], m_demodBuffer.size() * sizeof(qint16), DataFifo::DataTypeI16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_demodBufferFill = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Correlate received signal with training sequence
|
||||
// Note that DC offset doesn't matter for this
|
||||
Real RadiosondeDemodSink::correlate(int idx) const
|
||||
{
|
||||
Real corr = 0.0f;
|
||||
for (int i = 0; i < m_correlationLength; i++)
|
||||
{
|
||||
int j = (idx + i) % m_rxBufLength;
|
||||
corr += m_train[i] * m_rxBuf[j];
|
||||
}
|
||||
return corr;
|
||||
}
|
||||
|
||||
bool RadiosondeDemodSink::processFrame(int length, float corr, int sampleIdx, int *firstError)
|
||||
{
|
||||
// Descramble
|
||||
for (int i = 0; i < length; i++) {
|
||||
m_bytes[i] = m_bytes[i] ^ m_descramble[i & 0x3f];
|
||||
}
|
||||
|
||||
// Reed-Solomon Error Correction
|
||||
int errorsCorrected = reedSolomonErrorCorrection();
|
||||
if (errorsCorrected >= 0)
|
||||
{
|
||||
// Check per-block CRCs are correct
|
||||
if (checkCRCs(length))
|
||||
{
|
||||
if (getMessageQueueToChannel())
|
||||
{
|
||||
QByteArray rxPacket((char *)m_bytes, length);
|
||||
RadiosondeDemod::MsgMessage *msg = RadiosondeDemod::MsgMessage::create(rxPacket, errorsCorrected, corr);
|
||||
getMessageQueueToChannel()->push(msg);
|
||||
}
|
||||
|
||||
// Skip over received packet, so we don't try to re-demodulate it
|
||||
m_rxBufCnt -= sampleIdx;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reed Solomon error correction
|
||||
// Returns number of errors corrected, or -1 if there are uncorrectable errors
|
||||
int RadiosondeDemodSink::reedSolomonErrorCorrection()
|
||||
{
|
||||
ReedSolomon::RS<RADIOSONDE_RS_N,RADIOSONDE_RS_K> rs;
|
||||
int errorsCorrected = 0;
|
||||
|
||||
for (int i = 0; (i < RADIOSONDE_RS_INTERLEAVE) && (errorsCorrected >= 0); i++)
|
||||
{
|
||||
// Deinterleave and reverse order
|
||||
uint8_t rsData[RADIOSONDE_RS_N];
|
||||
|
||||
memset(rsData, 0, RADIOSONDE_RS_PAD);
|
||||
for (int j = 0; j < RADIOSONDE_RS_DATA; j++) {
|
||||
rsData[RADIOSONDE_RS_K-1-j] = m_bytes[RADIOSONDE_OFFSET_FRAME_TYPE+j*RADIOSONDE_RS_INTERLEAVE+i];
|
||||
}
|
||||
for (int j = 0; j < RADIOSONDE_RS_2T; j++) {
|
||||
rsData[RADIOSONDE_RS_N-1-j] = m_bytes[RADIOSONDE_OFFSET_RS+i*RADIOSONDE_RS_2T+j];
|
||||
}
|
||||
|
||||
// Detect and correct errors
|
||||
int errors = rs.decode(&rsData[0], RADIOSONDE_RS_K); // FIXME: Indicate 0 padding?
|
||||
if (errors >= 0) {
|
||||
errorsCorrected += errors;
|
||||
} else {
|
||||
// Uncorrectable errors
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Restore corrected data
|
||||
for (int j = 0; j < RADIOSONDE_RS_DATA; j++) {
|
||||
m_bytes[RADIOSONDE_OFFSET_FRAME_TYPE+j*RADIOSONDE_RS_INTERLEAVE+i] = rsData[RADIOSONDE_RS_K-1-j];
|
||||
}
|
||||
|
||||
}
|
||||
return errorsCorrected;
|
||||
}
|
||||
|
||||
// Check per-block CRCs
|
||||
// We could pass partial frames that have some correct CRCs, but for now, whole frame has to be correct
|
||||
bool RadiosondeDemodSink::checkCRCs(int length)
|
||||
{
|
||||
for (int i = RADIOSONDE_OFFSET_BLOCK_0; i < length; )
|
||||
{
|
||||
uint8_t blockID = m_bytes[i+0];
|
||||
uint8_t blockLength = m_bytes[i+1];
|
||||
uint16_t rxCrc = m_bytes[i+2+blockLength] | (m_bytes[i+2+blockLength+1] << 8);
|
||||
// CRC doesn't include ID/len - so these can be wrong
|
||||
m_crc.init();
|
||||
m_crc.calculate(&m_bytes[i+2], blockLength);
|
||||
uint16_t calcCrc = m_crc.get();
|
||||
if (calcCrc != rxCrc) {
|
||||
return false;
|
||||
}
|
||||
i += blockLength+4;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RadiosondeDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
|
||||
{
|
||||
qDebug() << "RadiosondeDemodSink::applyChannelSettings:"
|
||||
<< " channelSampleRate: " << channelSampleRate
|
||||
<< " channelFrequencyOffset: " << channelFrequencyOffset;
|
||||
|
||||
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
|
||||
(m_channelSampleRate != channelSampleRate) || force)
|
||||
{
|
||||
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
|
||||
}
|
||||
|
||||
if ((m_channelSampleRate != channelSampleRate) || force)
|
||||
{
|
||||
m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
|
||||
m_interpolatorDistance = (Real) channelSampleRate / (Real) RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE;
|
||||
m_interpolatorDistanceRemain = m_interpolatorDistance;
|
||||
}
|
||||
|
||||
m_channelSampleRate = channelSampleRate;
|
||||
m_channelFrequencyOffset = channelFrequencyOffset;
|
||||
m_samplesPerSymbol = RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE / m_settings.m_baud;
|
||||
qDebug() << "RadiosondeDemodSink::applyChannelSettings: m_samplesPerSymbol: " << m_samplesPerSymbol;
|
||||
}
|
||||
|
||||
void RadiosondeDemodSink::applySettings(const RadiosondeDemodSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RadiosondeDemodSink::applySettings:"
|
||||
<< " force: " << force;
|
||||
|
||||
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
|
||||
{
|
||||
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
|
||||
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE;
|
||||
m_interpolatorDistanceRemain = m_interpolatorDistance;
|
||||
m_lowpass.create(301, RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE, settings.m_rfBandwidth / 2.0f);
|
||||
}
|
||||
if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force)
|
||||
{
|
||||
m_phaseDiscri.setFMScaling(RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE / (2.0f * settings.m_fmDeviation));
|
||||
}
|
||||
|
||||
if ((settings.m_baud != m_settings.m_baud) || force)
|
||||
{
|
||||
m_samplesPerSymbol = RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE / settings.m_baud;
|
||||
qDebug() << "RadiosondeDemodSink::applySettings: m_samplesPerSymbol: " << m_samplesPerSymbol << " baud " << settings.m_baud;
|
||||
|
||||
// What value to use for BT? RFIC is Si4032 - its datasheet only appears to support 0.5
|
||||
m_pulseShape.create(0.5, 3, m_samplesPerSymbol);
|
||||
|
||||
// Recieve buffer, long enough for one max length message
|
||||
delete[] m_rxBuf;
|
||||
m_rxBufLength = RADIOSONDEDEMOD_MAX_BYTES*8*m_samplesPerSymbol;
|
||||
m_rxBuf = new Real[m_rxBufLength];
|
||||
m_rxBufIdx = 0;
|
||||
m_rxBufCnt = 0;
|
||||
|
||||
// Create training sequence for correlation
|
||||
delete[] m_train;
|
||||
|
||||
const int correlateBits = 200; // Preamble is 320bits - leave some for AGC (and clock recovery eventually)
|
||||
m_correlationLength = correlateBits*m_samplesPerSymbol; // Don't want to use header, as we want to calculate DC offset
|
||||
m_train = new Real[m_correlationLength]();
|
||||
|
||||
// Pulse shape filter takes a few symbols before outputting expected shape
|
||||
for (int j = 0; j < m_samplesPerSymbol; j++) {
|
||||
m_pulseShape.filter(-1.0f);
|
||||
}
|
||||
for (int j = 0; j < m_samplesPerSymbol; j++) {
|
||||
m_pulseShape.filter(1.0f);
|
||||
}
|
||||
for (int i = 0; i < correlateBits; i++)
|
||||
{
|
||||
for (int j = 0; j < m_samplesPerSymbol; j++) {
|
||||
m_train[i*m_samplesPerSymbol+j] = -m_pulseShape.filter((i&1) * 2.0f - 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
143
plugins/channelrx/demodradiosonde/radiosondedemodsink.h
Normal file
143
plugins/channelrx/demodradiosonde/radiosondedemodsink.h
Normal file
@ -0,0 +1,143 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMODSINK_H
|
||||
#define INCLUDE_RADIOSONDEDEMODSINK_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "dsp/channelsamplesink.h"
|
||||
#include "dsp/phasediscri.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "dsp/firfilter.h"
|
||||
#include "dsp/gaussian.h"
|
||||
#include "util/movingaverage.h"
|
||||
#include "util/doublebufferfifo.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/crc.h"
|
||||
|
||||
#include "radiosondedemodsettings.h"
|
||||
|
||||
// Length of preamble (40 bytes) and frame (std 320 bytes - extended 518)
|
||||
#define RADIOSONDEDEMOD_MAX_BYTES (40+518)
|
||||
|
||||
class ChannelAPI;
|
||||
class RadiosondeDemod;
|
||||
class ScopeVis;
|
||||
|
||||
class RadiosondeDemodSink : public ChannelSampleSink {
|
||||
public:
|
||||
RadiosondeDemodSink(RadiosondeDemod *radiosondeDemod);
|
||||
~RadiosondeDemodSink();
|
||||
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
|
||||
void setScopeSink(ScopeVis* scopeSink) { m_scopeSink = scopeSink; }
|
||||
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
||||
void applySettings(const RadiosondeDemodSettings& settings, bool force = false);
|
||||
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
|
||||
void setChannel(ChannelAPI *channel) { m_channel = channel; }
|
||||
|
||||
double getMagSq() const { return m_magsq; }
|
||||
|
||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
|
||||
{
|
||||
if (m_magsqCount > 0)
|
||||
{
|
||||
m_magsq = m_magsqSum / m_magsqCount;
|
||||
m_magSqLevelStore.m_magsq = m_magsq;
|
||||
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
|
||||
}
|
||||
|
||||
avg = m_magSqLevelStore.m_magsq;
|
||||
peak = m_magSqLevelStore.m_magsqPeak;
|
||||
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
|
||||
|
||||
m_magsqSum = 0.0f;
|
||||
m_magsqPeak = 0.0f;
|
||||
m_magsqCount = 0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
struct MagSqLevelsStore
|
||||
{
|
||||
MagSqLevelsStore() :
|
||||
m_magsq(1e-12),
|
||||
m_magsqPeak(1e-12)
|
||||
{}
|
||||
double m_magsq;
|
||||
double m_magsqPeak;
|
||||
};
|
||||
|
||||
ScopeVis* m_scopeSink; // Scope GUI to display baseband waveform
|
||||
RadiosondeDemod *m_radiosondeDemod;
|
||||
RadiosondeDemodSettings m_settings;
|
||||
ChannelAPI *m_channel;
|
||||
int m_channelSampleRate;
|
||||
int m_channelFrequencyOffset;
|
||||
int m_samplesPerSymbol; // Number of samples per symbol
|
||||
|
||||
NCO m_nco;
|
||||
Interpolator m_interpolator;
|
||||
Real m_interpolatorDistance;
|
||||
Real m_interpolatorDistanceRemain;
|
||||
|
||||
double m_magsq;
|
||||
double m_magsqSum;
|
||||
double m_magsqPeak;
|
||||
int m_magsqCount;
|
||||
MagSqLevelsStore m_magSqLevelStore;
|
||||
|
||||
MessageQueue *m_messageQueueToChannel;
|
||||
|
||||
MovingAverageUtil<Real, double, 16> m_movingAverage;
|
||||
|
||||
Lowpass<Complex> m_lowpass; // RF input filter
|
||||
PhaseDiscriminators m_phaseDiscri; // FM demodulator
|
||||
Gaussian<Real> m_pulseShape; // Pulse shaping filter
|
||||
Real *m_rxBuf; // Receive sample buffer, large enough for one max length messsage
|
||||
int m_rxBufLength; // Size in elements in m_rxBuf
|
||||
int m_rxBufIdx; // Index in to circular buffer
|
||||
int m_rxBufCnt; // Number of valid samples in buffer
|
||||
Real *m_train; // Training sequence to look for
|
||||
int m_correlationLength;
|
||||
|
||||
unsigned char m_bytes[RADIOSONDEDEMOD_MAX_BYTES];
|
||||
crc16ccitt m_crc;
|
||||
|
||||
QVector<qint16> m_demodBuffer;
|
||||
int m_demodBufferFill;
|
||||
|
||||
SampleVector m_sampleBuffer;
|
||||
static const int m_sampleBufferSize = RadiosondeDemodSettings::RADIOSONDEDEMOD_CHANNEL_SAMPLE_RATE / 20;
|
||||
int m_sampleBufferIndex;
|
||||
|
||||
static const uint8_t m_descramble[64];
|
||||
|
||||
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
|
||||
void processOneSample(Complex &ci);
|
||||
Real correlate(int idx) const;
|
||||
bool processFrame(int length, float corr, int sampleIdx, int *firstError);
|
||||
int reedSolomonErrorCorrection();
|
||||
bool checkCRCs(int length);
|
||||
void sampleToScope(Complex sample);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMODSINK_H
|
@ -0,0 +1,52 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "radiosondedemod.h"
|
||||
#include "radiosondedemodwebapiadapter.h"
|
||||
|
||||
RadiosondeDemodWebAPIAdapter::RadiosondeDemodWebAPIAdapter()
|
||||
{}
|
||||
|
||||
RadiosondeDemodWebAPIAdapter::~RadiosondeDemodWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int RadiosondeDemodWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadiosondeDemodSettings(new SWGSDRangel::SWGRadiosondeDemodSettings());
|
||||
response.getRadiosondeDemodSettings()->init();
|
||||
RadiosondeDemod::webapiFormatChannelSettings(response, m_settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RadiosondeDemodWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force;
|
||||
(void) errorMessage;
|
||||
RadiosondeDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
|
||||
|
||||
return 200;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDEDEMOD_WEBAPIADAPTER_H
|
||||
#define INCLUDE_RADIOSONDEDEMOD_WEBAPIADAPTER_H
|
||||
|
||||
#include "channel/channelwebapiadapter.h"
|
||||
#include "radiosondedemodsettings.h"
|
||||
|
||||
/**
|
||||
* Standalone API adapter only for the settings
|
||||
*/
|
||||
class RadiosondeDemodWebAPIAdapter : public ChannelWebAPIAdapter {
|
||||
public:
|
||||
RadiosondeDemodWebAPIAdapter();
|
||||
virtual ~RadiosondeDemodWebAPIAdapter();
|
||||
|
||||
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:
|
||||
RadiosondeDemodSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDEDEMOD_WEBAPIADAPTER_H
|
106
plugins/channelrx/demodradiosonde/readme.md
Normal file
106
plugins/channelrx/demodradiosonde/readme.md
Normal file
@ -0,0 +1,106 @@
|
||||
<h1>Radiosonde demodulator plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This plugin can be used to demodulate RS41 radiosonde weather ballon signals. Radiosondes typically transmit on 400-406MHz and are in the sky around the world for around 1 hour around 00:00 UTC.
|
||||
|
||||
RS41 radiosondes transmit data frames every second, containing position, velocity and PTU (Pressure, Temperature and Humidity) readings. The radios use GFSK modulation, with ±2.4kHz deviation at 4,800 baud. Reed Solomon encoding is used for ECC (Error Checking and Correction).
|
||||
|
||||
The Radiosonde demodulator can forward received data to the [Radiosone feature](../../feature/radiosonde/readme.md), which can plot charts showing how altitude and PTU vary over time, and also plot the position of the radiosonde on the 2D and 3D maps.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Radiosonde Demodulator plugin GUI](../../../doc/img/RadiosondeDemod_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: Channel power</h3>
|
||||
|
||||
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
|
||||
|
||||
<h3>3: Level meter in dB</h3>
|
||||
|
||||
- top bar (green): average value
|
||||
- bottom bar (blue green): instantaneous peak value
|
||||
- tip vertical bar (bright green): peak hold value
|
||||
|
||||
<h3>4: BW - RF Bandwidth</h3>
|
||||
|
||||
This specifies the bandwidth of a LPF that is applied to the input signal to limit the RF bandwidth. For RS41 radiosondes, this can be 9.6kHz.
|
||||
|
||||
<h3>5: Dev - Frequency deviation</h3>
|
||||
|
||||
Adjusts the expected frequency deviation in 0.1 kHz steps from 1 to 5 kHz. Typical value to RS41 is 2.4kHz.
|
||||
|
||||
<h3>6: TH - Correlation Threshold</h3>
|
||||
|
||||
The correlation threshold between the received signal and the preamble (training sequence). A lower value may be able to demodulate weaker signals, but increases processor usage. The default value is 450.
|
||||
|
||||
<h3>7: UDP</h3>
|
||||
|
||||
When checked, received receives are forwarded to the specified UDP address (12) and port (13).
|
||||
|
||||
<h3>8: UDP address</h3>
|
||||
|
||||
IP address of the host to forward received frames to via UDP.
|
||||
|
||||
<h3>9: UDP port</h3>
|
||||
|
||||
UDP port number to forward received frames to.
|
||||
|
||||
<h3>10: Find</h3>
|
||||
|
||||
Entering a regular expression in the Find field displays only frames where the radiosonde serial number matches the given regular expression.
|
||||
|
||||
<h3>11: Start/stop Logging Frames to .csv File</h3>
|
||||
|
||||
When checked, writes all received frames to a .csv file.
|
||||
|
||||
<h3>14: .csv Log Filename</h3>
|
||||
|
||||
Click to specify the name of the .csv file which received frames are logged to.
|
||||
|
||||
<h3>15: Read Data from .csv File</h3>
|
||||
|
||||
Click to specify a previously written radiosonde .csv log file, which is read and used to update the table.
|
||||
|
||||
<h3>11: Clear Data from table</h3>
|
||||
|
||||
Pressing this button clears all data from the table.
|
||||
|
||||
<h3>Received Data Table</h3>
|
||||
|
||||
The received frames table displays information about each radiosonde frame received.
|
||||
|
||||
![Radiosonde Demodulator plugin table](../../../doc/img/RadiosondeDemod_plugin_table.png)
|
||||
|
||||
* Date - The date the frame was received.
|
||||
* Time - The time the frame was received.
|
||||
* Serial - The serial number of the radiosonde. Double clicking on this column will search for the radiosone on https://sondehub.org/
|
||||
* Frame - Frame number
|
||||
* Phase - Flight phase: On ground, Ascent and Descent.
|
||||
* Lat (°) - Latitude in degrees, North positive. Double clicking on this column will search for the radiosonde on the Map.
|
||||
* Lon (°) - Longitude in degrees, East positive. Double clicking on this column will search for the radiosonde on the Map.
|
||||
* Alt (m) - Altitude in metres.
|
||||
* Spd (km/h) - Speed over ground in kilometres per hour.
|
||||
* VR (m/s) - Vertical climb rate in metres per second.
|
||||
* Hdg (°) - Heading in degrees.
|
||||
* P (hPA) - Air pressure in hectopascals. Not all RS41s include a pressure sensor. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* T (°C) - Air temperature in degrees Celsius. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* U (%) - Relative humidity in percent. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* Bat (V) - Battery voltage in Volts.
|
||||
* Bat - Battery status: OK or low.
|
||||
* PCB (°C) - Temperature of PCB.
|
||||
* PWM (%) - Humidity sensor heater PWM (Pulse Width Modulation) setting, in percent.
|
||||
* TX (%) - Transmit power in percent.
|
||||
* Max SF - Maximum subframe number.
|
||||
* SF No. - Subframe number of subframe data in this frame.
|
||||
* Subframe - Subframe data.
|
||||
* GPS Time - GPS date and time on board radiosonde. GPS time is offset 18 seconds from UTC.
|
||||
* GPS Sats - Number of GPS satellites used in position estimate.
|
||||
* ECC - Number of symbol errors corrected by Reed Solomon ECC.
|
||||
* Corr - Premable correlation value calculated for the frame. This can be used to choose a value for TH (6).
|
||||
|
||||
Right clicking on the table header allows you to select which columns to show. The columns can be reorderd by left clicking and dragging the column header. Right clicking on an item in the table allows you to copy the value to the clipboard.
|
@ -31,4 +31,7 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(startracker)
|
||||
if (Qt5Charts_FOUND)
|
||||
add_subdirectory(radiosonde)
|
||||
add_subdirectory(startracker)
|
||||
endif()
|
||||
|
60
plugins/feature/radiosonde/CMakeLists.txt
Normal file
60
plugins/feature/radiosonde/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
||||
project(radiosonde)
|
||||
|
||||
set(radiosonde_SOURCES
|
||||
radiosonde.cpp
|
||||
radiosondesettings.cpp
|
||||
radiosondeplugin.cpp
|
||||
radiosondewebapiadapter.cpp
|
||||
)
|
||||
|
||||
set(radiosonde_HEADERS
|
||||
radiosonde.h
|
||||
radiosondesettings.h
|
||||
radiosondeplugin.h
|
||||
radiosondewebapiadapter.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(radiosonde_SOURCES
|
||||
${radiosonde_SOURCES}
|
||||
radiosondegui.cpp
|
||||
radiosondegui.ui
|
||||
radiosonde.qrc
|
||||
)
|
||||
set(radiosonde_HEADERS
|
||||
${radiosonde_HEADERS}
|
||||
radiosondegui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME featureradiosonde)
|
||||
set(TARGET_LIB Qt5::Widgets Qt5::Charts)
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME featureradiosondesrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${radiosonde_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
||||
# Install debug symbols
|
||||
if (WIN32)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
|
||||
endif()
|
BIN
plugins/feature/radiosonde/map/ballon.png
Normal file
BIN
plugins/feature/radiosonde/map/ballon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 990 B |
BIN
plugins/feature/radiosonde/map/parachute.png
Normal file
BIN
plugins/feature/radiosonde/map/parachute.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
327
plugins/feature/radiosonde/radiosonde.cpp
Normal file
327
plugins/feature/radiosonde/radiosonde.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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 <QDebug>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QBuffer>
|
||||
#include <QTimer>
|
||||
|
||||
#include "SWGFeatureSettings.h"
|
||||
#include "SWGFeatureReport.h"
|
||||
#include "SWGFeatureActions.h"
|
||||
#include "SWGDeviceState.h"
|
||||
|
||||
#include "dsp/dspengine.h"
|
||||
#include "device/deviceset.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "feature/featureset.h"
|
||||
#include "settings/serializable.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "radiosonde.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(Radiosonde::MsgConfigureRadiosonde, Message)
|
||||
|
||||
const char* const Radiosonde::m_featureIdURI = "sdrangel.feature.radiosonde";
|
||||
const char* const Radiosonde::m_featureId = "Radiosonde";
|
||||
|
||||
Radiosonde::Radiosonde(WebAPIAdapterInterface *webAPIAdapterInterface) :
|
||||
Feature(m_featureIdURI, webAPIAdapterInterface)
|
||||
{
|
||||
qDebug("Radiosonde::Radiosonde: webAPIAdapterInterface: %p", webAPIAdapterInterface);
|
||||
setObjectName(m_featureId);
|
||||
m_state = StIdle;
|
||||
m_errorMessage = "Radiosonde error";
|
||||
connect(&m_updatePipesTimer, SIGNAL(timeout()), this, SLOT(updatePipes()));
|
||||
m_updatePipesTimer.start(1000);
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
Radiosonde::~Radiosonde()
|
||||
{
|
||||
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
delete m_networkManager;
|
||||
}
|
||||
|
||||
void Radiosonde::start()
|
||||
{
|
||||
qDebug("Radiosonde::start");
|
||||
m_state = StRunning;
|
||||
}
|
||||
|
||||
void Radiosonde::stop()
|
||||
{
|
||||
qDebug("Radiosonde::stop");
|
||||
m_state = StIdle;
|
||||
}
|
||||
|
||||
bool Radiosonde::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureRadiosonde::match(cmd))
|
||||
{
|
||||
MsgConfigureRadiosonde& cfg = (MsgConfigureRadiosonde&) cmd;
|
||||
qDebug() << "Radiosonde::handleMessage: MsgConfigureRadiosonde";
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MainCore::MsgPacket::match(cmd))
|
||||
{
|
||||
MainCore::MsgPacket& report = (MainCore::MsgPacket&) cmd;
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MainCore::MsgPacket *copy = new MainCore::MsgPacket(report);
|
||||
getMessageQueueToGUI()->push(copy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::updatePipes()
|
||||
{
|
||||
QList<AvailablePipeSource> availablePipes = updateAvailablePipeSources("radiosonde", RadiosondeSettings::m_pipeTypes, RadiosondeSettings::m_pipeURIs, this);
|
||||
|
||||
if (availablePipes != m_availablePipes) {
|
||||
m_availablePipes = availablePipes;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Radiosonde::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool Radiosonde::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
MsgConfigureRadiosonde *msg = MsgConfigureRadiosonde::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
MsgConfigureRadiosonde *msg = MsgConfigureRadiosonde::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::applySettings(const RadiosondeSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "Radiosonde::applySettings:"
|
||||
<< " m_title: " << settings.m_title
|
||||
<< " m_rgbColor: " << settings.m_rgbColor
|
||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
||||
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
|
||||
<< " m_reverseAPIFeatureSetIndex: " << settings.m_reverseAPIFeatureSetIndex
|
||||
<< " m_reverseAPIFeatureIndex: " << settings.m_reverseAPIFeatureIndex
|
||||
<< " force: " << force;
|
||||
|
||||
QList<QString> reverseAPIKeys;
|
||||
|
||||
if ((m_settings.m_title != settings.m_title) || force) {
|
||||
reverseAPIKeys.append("title");
|
||||
}
|
||||
if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) {
|
||||
reverseAPIKeys.append("rgbColor");
|
||||
}
|
||||
|
||||
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_reverseAPIFeatureSetIndex != settings.m_reverseAPIFeatureSetIndex) ||
|
||||
(m_settings.m_reverseAPIFeatureIndex != settings.m_reverseAPIFeatureIndex);
|
||||
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
int Radiosonde::webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadiosondeSettings(new SWGSDRangel::SWGRadiosondeSettings());
|
||||
response.getRadiosondeSettings()->init();
|
||||
webapiFormatFeatureSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int Radiosonde::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
RadiosondeSettings settings = m_settings;
|
||||
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
|
||||
|
||||
MsgConfigureRadiosonde *msg = MsgConfigureRadiosonde::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureRadiosonde *msgToGUI = MsgConfigureRadiosonde::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatFeatureSettings(response, settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
void Radiosonde::webapiFormatFeatureSettings(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
const RadiosondeSettings& settings)
|
||||
{
|
||||
if (response.getRadiosondeSettings()->getTitle()) {
|
||||
*response.getRadiosondeSettings()->getTitle() = settings.m_title;
|
||||
} else {
|
||||
response.getRadiosondeSettings()->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
|
||||
response.getRadiosondeSettings()->setRgbColor(settings.m_rgbColor);
|
||||
response.getRadiosondeSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getRadiosondeSettings()->getReverseApiAddress()) {
|
||||
*response.getRadiosondeSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getRadiosondeSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getRadiosondeSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getRadiosondeSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
|
||||
response.getRadiosondeSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
|
||||
|
||||
if (settings.m_rollupState)
|
||||
{
|
||||
if (response.getRadiosondeSettings()->getRollupState())
|
||||
{
|
||||
settings.m_rollupState->formatTo(response.getRadiosondeSettings()->getRollupState());
|
||||
}
|
||||
else
|
||||
{
|
||||
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
|
||||
settings.m_rollupState->formatTo(swgRollupState);
|
||||
response.getRadiosondeSettings()->setRollupState(swgRollupState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::webapiUpdateFeatureSettings(
|
||||
RadiosondeSettings& settings,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response)
|
||||
{
|
||||
if (featureSettingsKeys.contains("title")) {
|
||||
settings.m_title = *response.getRadiosondeSettings()->getTitle();
|
||||
}
|
||||
if (featureSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getRadiosondeSettings()->getRgbColor();
|
||||
}
|
||||
if (featureSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getRadiosondeSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getRadiosondeSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getRadiosondeSettings()->getReverseApiPort();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
|
||||
settings.m_reverseAPIFeatureSetIndex = response.getRadiosondeSettings()->getReverseApiFeatureSetIndex();
|
||||
}
|
||||
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
|
||||
settings.m_reverseAPIFeatureIndex = response.getRadiosondeSettings()->getReverseApiFeatureIndex();
|
||||
}
|
||||
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
|
||||
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getRadiosondeSettings()->getRollupState());
|
||||
}
|
||||
}
|
||||
|
||||
void Radiosonde::webapiReverseSendSettings(QList<QString>& featureSettingsKeys, const RadiosondeSettings& settings, bool force)
|
||||
{
|
||||
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
|
||||
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
|
||||
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
|
||||
swgFeatureSettings->setFeatureType(new QString("Radiosonde"));
|
||||
swgFeatureSettings->setRadiosondeSettings(new SWGSDRangel::SWGRadiosondeSettings());
|
||||
SWGSDRangel::SWGRadiosondeSettings *swgRadiosondeSettings = swgFeatureSettings->getRadiosondeSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (featureSettingsKeys.contains("title") || force) {
|
||||
swgRadiosondeSettings->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
if (featureSettingsKeys.contains("rgbColor") || force) {
|
||||
swgRadiosondeSettings->setRgbColor(settings.m_rgbColor);
|
||||
}
|
||||
|
||||
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
|
||||
.arg(settings.m_reverseAPIAddress)
|
||||
.arg(settings.m_reverseAPIPort)
|
||||
.arg(settings.m_reverseAPIFeatureSetIndex)
|
||||
.arg(settings.m_reverseAPIFeatureIndex);
|
||||
m_networkRequest.setUrl(QUrl(channelSettingsURL));
|
||||
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QBuffer *buffer = new QBuffer();
|
||||
buffer->open((QBuffer::ReadWrite));
|
||||
buffer->write(swgFeatureSettings->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 swgFeatureSettings;
|
||||
}
|
||||
|
||||
void Radiosonde::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "Radiosonde::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("Radiosonde::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
116
plugins/feature/radiosonde/radiosonde.h
Normal file
116
plugins/feature/radiosonde/radiosonde.h
Normal file
@ -0,0 +1,116 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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_FEATURE_RADIOSONDE_H_
|
||||
#define INCLUDE_FEATURE_RADIOSONDE_H_
|
||||
|
||||
#include <QThread>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTimer>
|
||||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "radiosondesettings.h"
|
||||
|
||||
class WebAPIAdapterInterface;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace SWGSDRangel {
|
||||
class SWGDeviceState;
|
||||
}
|
||||
|
||||
class Radiosonde : public Feature
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureRadiosonde : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RadiosondeSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRadiosonde* create(const RadiosondeSettings& settings, bool force) {
|
||||
return new MsgConfigureRadiosonde(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RadiosondeSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRadiosonde(const RadiosondeSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
Radiosonde(WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
virtual ~Radiosonde();
|
||||
virtual void destroy() { delete this; }
|
||||
virtual bool handleMessage(const Message& cmd);
|
||||
|
||||
virtual void getIdentifier(QString& id) const { id = objectName(); }
|
||||
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
static void webapiFormatFeatureSettings(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
const RadiosondeSettings& settings);
|
||||
|
||||
static void webapiUpdateFeatureSettings(
|
||||
RadiosondeSettings& settings,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response);
|
||||
|
||||
static const char* const m_featureIdURI;
|
||||
static const char* const m_featureId;
|
||||
|
||||
private:
|
||||
RadiosondeSettings m_settings;
|
||||
QList<PipeEndPoint::AvailablePipeSource> m_availablePipes;
|
||||
QTimer m_updatePipesTimer;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void applySettings(const RadiosondeSettings& settings, bool force = false);
|
||||
void webapiReverseSendSettings(QList<QString>& featureSettingsKeys, const RadiosondeSettings& settings, bool force);
|
||||
|
||||
private slots:
|
||||
void updatePipes();
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_RADIOSONDE_H_
|
6
plugins/feature/radiosonde/radiosonde.qrc
Normal file
6
plugins/feature/radiosonde/radiosonde.qrc
Normal file
@ -0,0 +1,6 @@
|
||||
<RCC>
|
||||
<qresource prefix="/radiosonde/">
|
||||
<file>map/ballon.png</file>
|
||||
<file>map/parachute.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
857
plugins/feature/radiosonde/radiosondegui.cpp
Normal file
857
plugins/feature/radiosonde/radiosondegui.cpp
Normal file
@ -0,0 +1,857 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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 <cmath>
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QAction>
|
||||
#include <QClipboard>
|
||||
|
||||
#include "feature/featureuiset.h"
|
||||
#include "feature/featurewebapiutils.h"
|
||||
#include "gui/basicfeaturesettingsdialog.h"
|
||||
#include "gui/datetimedelegate.h"
|
||||
#include "gui/decimaldelegate.h"
|
||||
#include "mainwindow.h"
|
||||
#include "device/deviceuiset.h"
|
||||
|
||||
#include "ui_radiosondegui.h"
|
||||
#include "radiosonde.h"
|
||||
#include "radiosondegui.h"
|
||||
|
||||
#include "SWGMapItem.h"
|
||||
|
||||
RadiosondeGUI* RadiosondeGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
||||
{
|
||||
RadiosondeGUI* gui = new RadiosondeGUI(pluginAPI, featureUISet, feature);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
QByteArray RadiosondeGUI::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RadiosondeGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RadiosondeGUI::handleMessage(const Message& message)
|
||||
{
|
||||
if (Radiosonde::MsgConfigureRadiosonde::match(message))
|
||||
{
|
||||
qDebug("RadiosondeGUI::handleMessage: Radiosonde::MsgConfigureRadiosonde");
|
||||
const Radiosonde::MsgConfigureRadiosonde& cfg = (Radiosonde::MsgConfigureRadiosonde&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MainCore::MsgPacket::match(message))
|
||||
{
|
||||
MainCore::MsgPacket& report = (MainCore::MsgPacket&) message;
|
||||
|
||||
// Decode the message
|
||||
RS41Frame *frame = RS41Frame::decode(report.getPacket());
|
||||
// Update table
|
||||
updateRadiosondes(frame, report.getDateTime());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = getInputMessageQueue()->pop()))
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
(void) rollDown;
|
||||
|
||||
saveState(m_rollupState);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
RadiosondeGUI::RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
||||
FeatureGUI(parent),
|
||||
ui(new Ui::RadiosondeGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_featureUISet(featureUISet),
|
||||
m_doApplySettings(true),
|
||||
m_lastFeatureState(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_helpURL = "plugins/feature/radiosonde/readme.md";
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
setChannelWidget(false);
|
||||
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
m_radiosonde = reinterpret_cast<Radiosonde*>(feature);
|
||||
m_radiosonde->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
|
||||
m_featureUISet->addRollupWidget(this);
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
// Intialise chart
|
||||
ui->chart->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// Resize the table using dummy data
|
||||
resizeTable();
|
||||
// Allow user to reorder columns
|
||||
ui->radiosondes->horizontalHeader()->setSectionsMovable(true);
|
||||
// Allow user to sort table by clicking on headers
|
||||
ui->radiosondes->setSortingEnabled(true);
|
||||
// Add context menu to allow hiding/showing of columns
|
||||
radiosondesMenu = new QMenu(ui->radiosondes);
|
||||
for (int i = 0; i < ui->radiosondes->horizontalHeader()->count(); i++)
|
||||
{
|
||||
QString text = ui->radiosondes->horizontalHeaderItem(i)->text();
|
||||
radiosondesMenu->addAction(createCheckableItem(text, i, true, SLOT(radiosondesColumnSelectMenuChecked())));
|
||||
}
|
||||
ui->radiosondes->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->radiosondes->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(radiosondesColumnSelectMenu(QPoint)));
|
||||
// Get signals when columns change
|
||||
connect(ui->radiosondes->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(radiosondes_sectionMoved(int, int, int)));
|
||||
connect(ui->radiosondes->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(radiosondes_sectionResized(int, int, int)));
|
||||
// Context menu
|
||||
ui->radiosondes->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->radiosondes, SIGNAL(customContextMenuRequested(QPoint)), SLOT(radiosondes_customContextMenuRequested(QPoint)));
|
||||
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LATITUDE, new DecimalDelegate(5));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LONGITUDE, new DecimalDelegate(5));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALTITUDE, new DecimalDelegate(1));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_SPEED, new DecimalDelegate(1));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_VERTICAL_RATE, new DecimalDelegate(1));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_HEADING, new DecimalDelegate(1));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_ALT_MAX, new DecimalDelegate(1));
|
||||
ui->radiosondes->setItemDelegateForColumn(RADIOSONDE_COL_LAST_UPDATE, new DateTimeDelegate());
|
||||
|
||||
m_settings.setRollupState(&m_rollupState);
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
|
||||
plotChart();
|
||||
}
|
||||
|
||||
RadiosondeGUI::~RadiosondeGUI()
|
||||
{
|
||||
qDeleteAll(m_radiosondes);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::blockApplySettings(bool block)
|
||||
{
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::displaySettings()
|
||||
{
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
setWindowTitle(m_settings.m_title);
|
||||
blockApplySettings(true);
|
||||
|
||||
// Order and size columns
|
||||
QHeaderView *header = ui->radiosondes->horizontalHeader();
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++)
|
||||
{
|
||||
bool hidden = m_settings.m_radiosondesColumnSizes[i] == 0;
|
||||
header->setSectionHidden(i, hidden);
|
||||
radiosondesMenu->actions().at(i)->setChecked(!hidden);
|
||||
if (m_settings.m_radiosondesColumnSizes[i] > 0) {
|
||||
ui->radiosondes->setColumnWidth(i, m_settings.m_radiosondesColumnSizes[i]);
|
||||
}
|
||||
header->moveSection(header->visualIndex(i), m_settings.m_radiosondesColumnIndexes[i]);
|
||||
}
|
||||
|
||||
ui->y1->setCurrentIndex((int)m_settings.m_y1);
|
||||
ui->y2->setCurrentIndex((int)m_settings.m_y2);
|
||||
|
||||
restoreState(m_rollupState);
|
||||
blockApplySettings(false);
|
||||
arrangeRollups();
|
||||
}
|
||||
|
||||
void RadiosondeGUI::leaveEvent(QEvent*)
|
||||
{
|
||||
}
|
||||
|
||||
void RadiosondeGUI::enterEvent(QEvent*)
|
||||
{
|
||||
}
|
||||
|
||||
void RadiosondeGUI::onMenuDialogCalled(const QPoint &p)
|
||||
{
|
||||
if (m_contextMenuType == ContextMenuChannelSettings)
|
||||
{
|
||||
BasicFeatureSettingsDialog dialog(this);
|
||||
dialog.setTitle(m_settings.m_title);
|
||||
dialog.setColor(m_settings.m_rgbColor);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
|
||||
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
|
||||
|
||||
dialog.move(p);
|
||||
dialog.exec();
|
||||
|
||||
m_settings.m_rgbColor = dialog.getColor().rgb();
|
||||
m_settings.m_title = dialog.getTitle();
|
||||
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
|
||||
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
|
||||
|
||||
setWindowTitle(m_settings.m_title);
|
||||
setTitleColor(m_settings.m_rgbColor);
|
||||
|
||||
applySettings();
|
||||
}
|
||||
|
||||
resetContextMenuType();
|
||||
}
|
||||
|
||||
void RadiosondeGUI::applySettings(bool force)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
Radiosonde::MsgConfigureRadiosonde* message = Radiosonde::MsgConfigureRadiosonde::create(m_settings, force);
|
||||
m_radiosonde->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeGUI::resizeTable()
|
||||
{
|
||||
// Fill table with a row of dummy data that will size the columns nicely
|
||||
int row = ui->radiosondes->rowCount();
|
||||
ui->radiosondes->setRowCount(row + 1);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_SERIAL, new QTableWidgetItem("123456789"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_TYPE, new QTableWidgetItem("RS41-SGP"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LATITUDE, new QTableWidgetItem("90.000000-"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LONGITUDE, new QTableWidgetItem("180.00000-"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_ALTITUDE, new QTableWidgetItem("10000"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_SPEED, new QTableWidgetItem("120"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_VERTICAL_RATE, new QTableWidgetItem("120"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_HEADING, new QTableWidgetItem("360"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_STATUS, new QTableWidgetItem("Ascent"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_PRESSURE, new QTableWidgetItem("1234"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_TEMPERATURE, new QTableWidgetItem("-50.0"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_HUMIDITY, new QTableWidgetItem("100.0"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_ALT_MAX, new QTableWidgetItem("10000"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_FREQUENCY, new QTableWidgetItem("400.000"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_BURSTKILL_STATUS, new QTableWidgetItem("0"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_BURSTKILL_TIMER, new QTableWidgetItem("00:00"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LAST_UPDATE, new QTableWidgetItem("2022/12/12 12:00:00"));
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_MESSAGES, new QTableWidgetItem("1000"));
|
||||
ui->radiosondes->resizeColumnsToContents();
|
||||
ui->radiosondes->removeRow(row);
|
||||
}
|
||||
|
||||
// Columns in table reordered
|
||||
void RadiosondeGUI::radiosondes_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
||||
{
|
||||
(void) oldVisualIndex;
|
||||
|
||||
m_settings.m_radiosondesColumnIndexes[logicalIndex] = newVisualIndex;
|
||||
}
|
||||
|
||||
// Column in table resized (when hidden size is 0)
|
||||
void RadiosondeGUI::radiosondes_sectionResized(int logicalIndex, int oldSize, int newSize)
|
||||
{
|
||||
(void) oldSize;
|
||||
|
||||
m_settings.m_radiosondesColumnSizes[logicalIndex] = newSize;
|
||||
}
|
||||
|
||||
// Right click in table header - show column select menu
|
||||
void RadiosondeGUI::radiosondesColumnSelectMenu(QPoint pos)
|
||||
{
|
||||
radiosondesMenu->popup(ui->radiosondes->horizontalHeader()->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
// Hide/show column when menu selected
|
||||
void RadiosondeGUI::radiosondesColumnSelectMenuChecked(bool checked)
|
||||
{
|
||||
(void) checked;
|
||||
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
if (action != nullptr)
|
||||
{
|
||||
int idx = action->data().toInt(nullptr);
|
||||
ui->radiosondes->setColumnHidden(idx, !action->isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
// Create column select menu item
|
||||
QAction *RadiosondeGUI::createCheckableItem(QString &text, int idx, bool checked, const char *slot)
|
||||
{
|
||||
QAction *action = new QAction(text, this);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(checked);
|
||||
action->setData(QVariant(idx));
|
||||
connect(action, SIGNAL(triggered()), this, slot);
|
||||
return action;
|
||||
}
|
||||
|
||||
// Send to Map feature
|
||||
void RadiosondeGUI::sendToMap(const QString &name, const QString &label,
|
||||
const QString &image, const QString &text,
|
||||
const QString &model, float labelOffset,
|
||||
float latitude, float longitude, float altitude, QDateTime positionDateTime,
|
||||
float heading
|
||||
)
|
||||
{
|
||||
MessagePipesLegacy& messagePipes = MainCore::instance()->getMessagePipes();
|
||||
QList<MessageQueue*> *mapMessageQueues = messagePipes.getMessageQueues(m_radiosonde, "mapitems");
|
||||
if (mapMessageQueues)
|
||||
{
|
||||
QList<MessageQueue*>::iterator it = mapMessageQueues->begin();
|
||||
|
||||
for (; it != mapMessageQueues->end(); ++it)
|
||||
{
|
||||
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
||||
swgMapItem->setName(new QString(name));
|
||||
swgMapItem->setLatitude(latitude);
|
||||
swgMapItem->setLongitude(longitude);
|
||||
swgMapItem->setAltitude(altitude);
|
||||
swgMapItem->setAltitudeReference(0); // ABSOLUTE
|
||||
|
||||
if (positionDateTime.isValid()) {
|
||||
swgMapItem->setPositionDateTime(new QString(positionDateTime.toString(Qt::ISODateWithMs)));
|
||||
}
|
||||
|
||||
swgMapItem->setImageRotation(heading);
|
||||
swgMapItem->setText(new QString(text));
|
||||
|
||||
if (image.isEmpty()) {
|
||||
swgMapItem->setImage(new QString(""));
|
||||
} else {
|
||||
swgMapItem->setImage(new QString(QString("qrc:///radiosonde/map/%1").arg(image)));
|
||||
}
|
||||
swgMapItem->setModel(new QString(model));
|
||||
swgMapItem->setModelAltitudeOffset(0.0f);
|
||||
swgMapItem->setLabel(new QString(label));
|
||||
swgMapItem->setLabelAltitudeOffset(labelOffset);
|
||||
swgMapItem->setFixedPosition(false);
|
||||
swgMapItem->setOrientation(1);
|
||||
swgMapItem->setHeading(heading);
|
||||
swgMapItem->setPitch(0.0);
|
||||
swgMapItem->setRoll(0.0);
|
||||
|
||||
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_radiosonde, swgMapItem);
|
||||
(*it)->push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update table with received message
|
||||
void RadiosondeGUI::updateRadiosondes(RS41Frame *message, QDateTime dateTime)
|
||||
{
|
||||
QTableWidgetItem *serialItem;
|
||||
QTableWidgetItem *typeItem;
|
||||
QTableWidgetItem *latitudeItem;
|
||||
QTableWidgetItem *longitudeItem;
|
||||
QTableWidgetItem *alitutudeItem;
|
||||
QTableWidgetItem *speedItem;
|
||||
QTableWidgetItem *verticalRateItem;
|
||||
QTableWidgetItem *headingItem;
|
||||
QTableWidgetItem *statusItem;
|
||||
QTableWidgetItem *pressureItem;
|
||||
QTableWidgetItem *temperatureItem;
|
||||
QTableWidgetItem *humidityItem;
|
||||
QTableWidgetItem *altMaxItem;
|
||||
QTableWidgetItem *frequencyItem;
|
||||
QTableWidgetItem *burstKillStatusItem;
|
||||
QTableWidgetItem *burstKillTimerItem;
|
||||
QTableWidgetItem *lastUpdateItem;
|
||||
QTableWidgetItem *messagesItem;
|
||||
|
||||
if (!message->m_statusValid)
|
||||
{
|
||||
// Nothing to display if no serial number
|
||||
return;
|
||||
}
|
||||
|
||||
RadiosondeData *radiosonde;
|
||||
|
||||
// See if radiosonde is already in table
|
||||
QString messageSerial = message->m_serial;
|
||||
bool found = false;
|
||||
for (int row = 0; row < ui->radiosondes->rowCount(); row++)
|
||||
{
|
||||
QString itemSerial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
if (messageSerial == itemSerial)
|
||||
{
|
||||
// Update existing item
|
||||
serialItem = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL);
|
||||
typeItem = ui->radiosondes->item(row, RADIOSONDE_COL_TYPE);
|
||||
latitudeItem = ui->radiosondes->item(row, RADIOSONDE_COL_LATITUDE);
|
||||
longitudeItem = ui->radiosondes->item(row, RADIOSONDE_COL_LONGITUDE);
|
||||
alitutudeItem = ui->radiosondes->item(row, RADIOSONDE_COL_ALTITUDE);
|
||||
speedItem = ui->radiosondes->item(row, RADIOSONDE_COL_SPEED);
|
||||
verticalRateItem = ui->radiosondes->item(row, RADIOSONDE_COL_VERTICAL_RATE);
|
||||
headingItem = ui->radiosondes->item(row, RADIOSONDE_COL_HEADING);
|
||||
statusItem = ui->radiosondes->item(row, RADIOSONDE_COL_STATUS);
|
||||
pressureItem = ui->radiosondes->item(row, RADIOSONDE_COL_PRESSURE);
|
||||
temperatureItem = ui->radiosondes->item(row, RADIOSONDE_COL_TEMPERATURE);
|
||||
humidityItem = ui->radiosondes->item(row, RADIOSONDE_COL_HUMIDITY);
|
||||
altMaxItem = ui->radiosondes->item(row, RADIOSONDE_COL_ALT_MAX);
|
||||
frequencyItem = ui->radiosondes->item(row, RADIOSONDE_COL_FREQUENCY);
|
||||
burstKillStatusItem = ui->radiosondes->item(row, RADIOSONDE_COL_BURSTKILL_STATUS);
|
||||
burstKillTimerItem = ui->radiosondes->item(row, RADIOSONDE_COL_BURSTKILL_TIMER);
|
||||
lastUpdateItem = ui->radiosondes->item(row, RADIOSONDE_COL_LAST_UPDATE);
|
||||
messagesItem = ui->radiosondes->item(row, RADIOSONDE_COL_MESSAGES);
|
||||
radiosonde = m_radiosondes.value(messageSerial);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
// Add new radiosonde
|
||||
ui->radiosondes->setSortingEnabled(false);
|
||||
int row = ui->radiosondes->rowCount();
|
||||
ui->radiosondes->setRowCount(row + 1);
|
||||
|
||||
serialItem = new QTableWidgetItem();
|
||||
typeItem = new QTableWidgetItem();
|
||||
latitudeItem = new QTableWidgetItem();
|
||||
longitudeItem = new QTableWidgetItem();
|
||||
alitutudeItem = new QTableWidgetItem();
|
||||
speedItem = new QTableWidgetItem();
|
||||
verticalRateItem = new QTableWidgetItem();
|
||||
headingItem = new QTableWidgetItem();
|
||||
statusItem = new QTableWidgetItem();
|
||||
temperatureItem = new QTableWidgetItem();
|
||||
humidityItem = new QTableWidgetItem();
|
||||
pressureItem = new QTableWidgetItem();
|
||||
altMaxItem = new QTableWidgetItem();
|
||||
frequencyItem = new QTableWidgetItem();
|
||||
burstKillStatusItem = new QTableWidgetItem();
|
||||
burstKillTimerItem = new QTableWidgetItem();
|
||||
lastUpdateItem = new QTableWidgetItem();
|
||||
messagesItem = new QTableWidgetItem();
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_SERIAL, serialItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_TYPE, typeItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LATITUDE, latitudeItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LONGITUDE, longitudeItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_ALTITUDE, alitutudeItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_SPEED, speedItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_VERTICAL_RATE, verticalRateItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_HEADING, headingItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_STATUS, statusItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_PRESSURE, pressureItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_TEMPERATURE, temperatureItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_HUMIDITY, humidityItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_ALT_MAX, altMaxItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_FREQUENCY, frequencyItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_BURSTKILL_STATUS, burstKillStatusItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_BURSTKILL_TIMER, burstKillTimerItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_LAST_UPDATE, lastUpdateItem);
|
||||
ui->radiosondes->setItem(row, RADIOSONDE_COL_MESSAGES, messagesItem);
|
||||
messagesItem->setData(Qt::DisplayRole, 0);
|
||||
|
||||
radiosonde = new RadiosondeData();
|
||||
m_radiosondes.insert(messageSerial, radiosonde);
|
||||
}
|
||||
|
||||
serialItem->setText(message->m_serial);
|
||||
lastUpdateItem->setData(Qt::DisplayRole, dateTime);
|
||||
messagesItem->setData(Qt::DisplayRole, messagesItem->data(Qt::DisplayRole).toInt() + 1);
|
||||
|
||||
if (message->m_posValid)
|
||||
{
|
||||
latitudeItem->setData(Qt::DisplayRole, message->m_latitude);
|
||||
longitudeItem->setData(Qt::DisplayRole, message->m_longitude);
|
||||
alitutudeItem->setData(Qt::DisplayRole, message->m_height);
|
||||
float altMax = altMaxItem->data(Qt::DisplayRole).toFloat();
|
||||
if (message->m_height > altMax) {
|
||||
altMaxItem->setData(Qt::DisplayRole, message->m_height);
|
||||
}
|
||||
speedItem->setData(Qt::DisplayRole, Units::kmpsToKPH(message->m_speed/1000.0));
|
||||
verticalRateItem->setData(Qt::DisplayRole, message->m_verticalRate);
|
||||
headingItem->setData(Qt::DisplayRole, message->m_heading);
|
||||
}
|
||||
statusItem->setText(message->m_flightPhase);
|
||||
|
||||
radiosonde->m_subframe.update(message);
|
||||
|
||||
if (message->m_measValid)
|
||||
{
|
||||
pressureItem->setData(Qt::DisplayRole, message->getPressureString(&radiosonde->m_subframe));
|
||||
temperatureItem->setData(Qt::DisplayRole, message->getTemperatureString(&radiosonde->m_subframe));
|
||||
humidityItem->setData(Qt::DisplayRole, message->getHumidityString(&radiosonde->m_subframe));
|
||||
}
|
||||
|
||||
if (message->m_measValid && message->m_posValid) {
|
||||
radiosonde->addMessage(dateTime, message);
|
||||
}
|
||||
|
||||
typeItem->setText(radiosonde->m_subframe.getType());
|
||||
frequencyItem->setText(radiosonde->m_subframe.getFrequencyMHz());
|
||||
burstKillStatusItem->setText(radiosonde->m_subframe.getBurstKillStatus());
|
||||
burstKillTimerItem->setText(radiosonde->m_subframe.getBurstKillTimer());
|
||||
|
||||
ui->radiosondes->setSortingEnabled(true);
|
||||
|
||||
if (message->m_posValid)
|
||||
{
|
||||
// Text to display in info box
|
||||
QStringList text;
|
||||
QString type = typeItem->text();
|
||||
QVariant altitudeV = alitutudeItem->data(Qt::DisplayRole);
|
||||
QVariant speedV = speedItem->data(Qt::DisplayRole);
|
||||
QVariant verticalRateV = verticalRateItem->data(Qt::DisplayRole);
|
||||
QVariant headingV = headingItem->data(Qt::DisplayRole);
|
||||
QString pressure = pressureItem->text();
|
||||
QString temperature = temperatureItem->text();
|
||||
QString humidity = humidityItem->text();
|
||||
QString status = statusItem->text();
|
||||
QString frequency = frequencyItem->text();
|
||||
text.append(QString("Serial: %1").arg(serialItem->text()));
|
||||
if (!type.isEmpty()) {
|
||||
text.append(QString("Type: %1").arg(type));
|
||||
}
|
||||
if (!altitudeV.isNull()) {
|
||||
text.append(QString("Altitude: %1m").arg(altitudeV.toDouble(), 0, 'f', 1));
|
||||
}
|
||||
if (!speedV.isNull()) {
|
||||
text.append(QString("Speed: %1km/h").arg(speedV.toDouble(), 0, 'f', 1));
|
||||
}
|
||||
if (!verticalRateV.isNull()) {
|
||||
text.append(QString("Vertical rate: %1m/s").arg(verticalRateV.toDouble(), 0, 'f', 1));
|
||||
}
|
||||
if (!headingV.isNull()) {
|
||||
text.append(QString("Heading: %1%2").arg(headingV.toDouble(), 0, 'f', 1).arg(QChar(0xb0)));
|
||||
}
|
||||
if (!status.isEmpty()) {
|
||||
text.append(QString("Status: %1").arg(status));
|
||||
}
|
||||
if (!pressure.isEmpty()) {
|
||||
text.append(QString("Pressure: %1hPa").arg(pressure));
|
||||
}
|
||||
if (!temperature.isEmpty()) {
|
||||
text.append(QString("Temperature: %1C").arg(temperature));
|
||||
}
|
||||
if (!humidity.isEmpty()) {
|
||||
text.append(QString("Humidity: %1%").arg(humidity));
|
||||
}
|
||||
if (!frequency.isEmpty()) {
|
||||
text.append(QString("Frequency: %1MHz").arg(frequency));
|
||||
}
|
||||
|
||||
QString image = message->m_flightPhase == "Descent" ? "parachute.png" : "ballon.png";
|
||||
QString model = message->m_flightPhase == "Descent" ? "radiosondeparachute.glb" : "radiosondeballon.glb";
|
||||
// Send to map feature
|
||||
sendToMap(serialItem->text(), serialItem->text(),
|
||||
image, text.join("<br>"),
|
||||
model, 0.0,
|
||||
message->m_latitude, message->m_longitude, message->m_height, dateTime,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
// If this is the first row in the table, select it, so that the chart is plotted
|
||||
QList<QTableWidgetItem *> selectedItems = ui->radiosondes->selectedItems();
|
||||
if (selectedItems.size() == 0)
|
||||
{
|
||||
QTableWidgetSelectionRange r(0, 0, 0, RADIOSONDES_COLUMNS);
|
||||
ui->radiosondes->setRangeSelected(r, true);
|
||||
}
|
||||
|
||||
plotChart();
|
||||
}
|
||||
|
||||
void RadiosondeGUI::on_radiosondes_itemSelectionChanged()
|
||||
{
|
||||
plotChart();
|
||||
}
|
||||
|
||||
void RadiosondeGUI::on_radiosondes_cellDoubleClicked(int row, int column)
|
||||
{
|
||||
if (column == RADIOSONDE_COL_SERIAL)
|
||||
{
|
||||
// Get serial of Radiosonde in row double clicked
|
||||
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
// Search for MMSI on www.radiosondefinder.com
|
||||
QDesktopServices::openUrl(QUrl(QString("https://sondehub.org/?f=%1#!mt=Mapnik&f=%1&q=%1").arg(serial)));
|
||||
}
|
||||
else if ((column == RADIOSONDE_COL_LATITUDE) || (column == RADIOSONDE_COL_LONGITUDE))
|
||||
{
|
||||
// Get serial of Radiosonde in row double clicked
|
||||
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
// Find serial on Map
|
||||
FeatureWebAPIUtils::mapFind(serial);
|
||||
}
|
||||
}
|
||||
|
||||
// Table cells context menu
|
||||
void RadiosondeGUI::radiosondes_customContextMenuRequested(QPoint pos)
|
||||
{
|
||||
QTableWidgetItem *item = ui->radiosondes->itemAt(pos);
|
||||
if (item)
|
||||
{
|
||||
int row = item->row();
|
||||
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
QVariant latitudeV = ui->radiosondes->item(row, RADIOSONDE_COL_LATITUDE)->data(Qt::DisplayRole);
|
||||
QVariant longitudeV = ui->radiosondes->item(row, RADIOSONDE_COL_LONGITUDE)->data(Qt::DisplayRole);
|
||||
|
||||
QMenu* tableContextMenu = new QMenu(ui->radiosondes);
|
||||
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
|
||||
|
||||
// Copy current cell
|
||||
QAction* copyAction = new QAction("Copy", tableContextMenu);
|
||||
const QString text = item->text();
|
||||
connect(copyAction, &QAction::triggered, this, [text]()->void {
|
||||
QClipboard *clipboard = QGuiApplication::clipboard();
|
||||
clipboard->setText(text);
|
||||
});
|
||||
tableContextMenu->addAction(copyAction);
|
||||
tableContextMenu->addSeparator();
|
||||
|
||||
// View radiosonde on various websites
|
||||
QAction* mmsiRadiosondeHubAction = new QAction(QString("View %1 on sondehub.net...").arg(serial), tableContextMenu);
|
||||
connect(mmsiRadiosondeHubAction, &QAction::triggered, this, [serial]()->void {
|
||||
QDesktopServices::openUrl(QUrl(QString("https://sondehub.org/?f=%1#!mt=Mapnik&f=%1&q=%1").arg(serial)));
|
||||
});
|
||||
tableContextMenu->addAction(mmsiRadiosondeHubAction);
|
||||
|
||||
// Find on Map
|
||||
tableContextMenu->addSeparator();
|
||||
QAction* findMapFeatureAction = new QAction(QString("Find %1 on map").arg(serial), tableContextMenu);
|
||||
connect(findMapFeatureAction, &QAction::triggered, this, [serial]()->void {
|
||||
FeatureWebAPIUtils::mapFind(serial);
|
||||
});
|
||||
tableContextMenu->addAction(findMapFeatureAction);
|
||||
|
||||
tableContextMenu->popup(ui->radiosondes->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeGUI::on_y1_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_y1 = (RadiosondeSettings::ChartData)index;
|
||||
applySettings();
|
||||
plotChart();
|
||||
}
|
||||
|
||||
void RadiosondeGUI::on_y2_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_y2 = (RadiosondeSettings::ChartData)index;
|
||||
applySettings();
|
||||
plotChart();
|
||||
}
|
||||
|
||||
float RadiosondeGUI::getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message)
|
||||
{
|
||||
float data;
|
||||
switch (dataType)
|
||||
{
|
||||
case RadiosondeSettings::ALTITUDE:
|
||||
data = message->m_height;
|
||||
break;
|
||||
case RadiosondeSettings::TEMPERATURE:
|
||||
data = message->getTemperatureFloat(&radiosonde->m_subframe);
|
||||
break;
|
||||
case RadiosondeSettings::HUMIDITY:
|
||||
data = message->getHumidityFloat(&radiosonde->m_subframe);
|
||||
break;
|
||||
case RadiosondeSettings::PRESSURE:
|
||||
data = 0.0f;
|
||||
break;
|
||||
case RadiosondeSettings::SPEED:
|
||||
data = Units::kmpsToKPH(message->m_speed/1000.0);
|
||||
break;
|
||||
case RadiosondeSettings::VERTICAL_RATE:
|
||||
data = message->m_verticalRate;
|
||||
break;
|
||||
case RadiosondeSettings::HEADING:
|
||||
data = message->m_speed;
|
||||
break;
|
||||
case RadiosondeSettings::BATTERY_VOLTAGE:
|
||||
data = message->m_batteryVoltage;
|
||||
break;
|
||||
default:
|
||||
data = 0.0f;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static QString getAxisTitle(RadiosondeSettings::ChartData dataType)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case RadiosondeSettings::ALTITUDE:
|
||||
return "Altitude (m)";
|
||||
break;
|
||||
case RadiosondeSettings::TEMPERATURE:
|
||||
return QString("Temperature (%1C)").arg(QChar(0xb0));
|
||||
break;
|
||||
case RadiosondeSettings::HUMIDITY:
|
||||
return "Relative humidty (%)";
|
||||
break;
|
||||
case RadiosondeSettings::PRESSURE:
|
||||
return "Pressure (hPa)";
|
||||
break;
|
||||
case RadiosondeSettings::SPEED:
|
||||
return "Speed (km/h)";
|
||||
break;
|
||||
case RadiosondeSettings::VERTICAL_RATE:
|
||||
return "Vertical rate (m/s)";
|
||||
break;
|
||||
case RadiosondeSettings::HEADING:
|
||||
return QString("Heading (%1)").arg(QChar(0xb0));
|
||||
break;
|
||||
case RadiosondeSettings::BATTERY_VOLTAGE:
|
||||
return "Battery Voltage (V)";
|
||||
break;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void RadiosondeGUI::plotChart()
|
||||
{
|
||||
QChart *oldChart = ui->chart->chart();
|
||||
QChart *m_chart;
|
||||
|
||||
m_chart = new QChart();
|
||||
m_chart->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_chart->setMargins(QMargins(1, 1, 1, 1));
|
||||
m_chart->setTheme(QChart::ChartThemeDark);
|
||||
|
||||
// Get selected radiosonde
|
||||
QList<QTableWidgetItem *> selectedItems = ui->radiosondes->selectedItems();
|
||||
if (selectedItems.size() >= 1)
|
||||
{
|
||||
int row = selectedItems[0]->row();
|
||||
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
RadiosondeData *radiosonde = m_radiosondes.value(serial);
|
||||
if (radiosonde)
|
||||
{
|
||||
// Plot selected data
|
||||
QDateTimeAxis *m_chartXAxis;
|
||||
QValueAxis *m_chartY1Axis;
|
||||
QValueAxis *m_chartY2Axis;
|
||||
|
||||
m_chartXAxis = new QDateTimeAxis();
|
||||
if (m_settings.m_y1 != RadiosondeSettings::NONE) {
|
||||
m_chartY1Axis = new QValueAxis();
|
||||
}
|
||||
if (m_settings.m_y2 != RadiosondeSettings::NONE) {
|
||||
m_chartY2Axis = new QValueAxis();
|
||||
}
|
||||
|
||||
m_chart->legend()->hide();
|
||||
m_chart->addAxis(m_chartXAxis, Qt::AlignBottom);
|
||||
|
||||
QLineSeries *y1Series = new QLineSeries();
|
||||
QLineSeries *y2Series = new QLineSeries();
|
||||
|
||||
int idx = 0;
|
||||
for (auto message : radiosonde->m_messages)
|
||||
{
|
||||
float y1, y2;
|
||||
if (m_settings.m_y1 != RadiosondeSettings::NONE)
|
||||
{
|
||||
y1 = getData(m_settings.m_y1, radiosonde, message);
|
||||
y1Series->append(radiosonde->m_messagesDateTime[idx].toMSecsSinceEpoch(), y1);
|
||||
}
|
||||
if (m_settings.m_y2 != RadiosondeSettings::NONE)
|
||||
{
|
||||
y2 = getData(m_settings.m_y2, radiosonde, message);
|
||||
y2Series->append(radiosonde->m_messagesDateTime[idx].toMSecsSinceEpoch(), y2);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (m_settings.m_y1 != RadiosondeSettings::NONE)
|
||||
{
|
||||
m_chart->addSeries(y1Series);
|
||||
m_chart->addAxis(m_chartY1Axis, Qt::AlignLeft);
|
||||
y1Series->attachAxis(m_chartXAxis);
|
||||
y1Series->attachAxis(m_chartY1Axis);
|
||||
m_chartY1Axis->setTitleText(getAxisTitle(m_settings.m_y1));
|
||||
}
|
||||
if (m_settings.m_y2 != RadiosondeSettings::NONE)
|
||||
{
|
||||
m_chart->addSeries(y2Series);
|
||||
m_chart->addAxis(m_chartY2Axis, Qt::AlignRight);
|
||||
y2Series->attachAxis(m_chartXAxis);
|
||||
y2Series->attachAxis(m_chartY2Axis);
|
||||
m_chartY2Axis->setTitleText(getAxisTitle(m_settings.m_y2));
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->chart->setChart(m_chart);
|
||||
delete oldChart;
|
||||
}
|
||||
|
||||
void RadiosondeGUI::on_deleteAll_clicked()
|
||||
{
|
||||
for (int row = ui->radiosondes->rowCount() - 1; row >= 0; row--)
|
||||
{
|
||||
QString serial = ui->radiosondes->item(row, RADIOSONDE_COL_SERIAL)->text();
|
||||
// Remove from map
|
||||
sendToMap(serial, "",
|
||||
"", "",
|
||||
"", 0.0f,
|
||||
0.0f, 0.0f, 0.0f, QDateTime(),
|
||||
0.0f);
|
||||
// Remove from table
|
||||
ui->radiosondes->removeRow(row);
|
||||
// Remove from hash
|
||||
m_radiosondes.remove(serial);
|
||||
}
|
||||
}
|
155
plugins/feature/radiosonde/radiosondegui.h
Normal file
155
plugins/feature/radiosonde/radiosondegui.h
Normal file
@ -0,0 +1,155 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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_FEATURE_RADIOSONDEGUI_H_
|
||||
#define INCLUDE_FEATURE_RADIOSONDEGUI_H_
|
||||
|
||||
#include <QTimer>
|
||||
#include <QMenu>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QtCharts>
|
||||
|
||||
#include "feature/featuregui.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/radiosonde.h"
|
||||
#include "pipes/pipeendpoint.h"
|
||||
#include "settings/rollupstate.h"
|
||||
|
||||
#include "radiosondesettings.h"
|
||||
|
||||
class PluginAPI;
|
||||
class FeatureUISet;
|
||||
class Radiosonde;
|
||||
|
||||
namespace Ui {
|
||||
class RadiosondeGUI;
|
||||
}
|
||||
|
||||
using namespace QtCharts;
|
||||
|
||||
class RadiosondeGUI : public FeatureGUI {
|
||||
Q_OBJECT
|
||||
|
||||
// Holds information not in the table
|
||||
struct RadiosondeData {
|
||||
|
||||
QList<QDateTime> m_messagesDateTime;
|
||||
QList<RS41Frame *> m_messages;
|
||||
|
||||
RS41Subframe m_subframe;
|
||||
|
||||
~RadiosondeData()
|
||||
{
|
||||
qDeleteAll(m_messages);
|
||||
}
|
||||
|
||||
void addMessage(QDateTime dateTime, RS41Frame *message)
|
||||
{
|
||||
m_messagesDateTime.append(dateTime);
|
||||
m_messages.append(message);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
static RadiosondeGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
|
||||
virtual void destroy();
|
||||
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
|
||||
private:
|
||||
Ui::RadiosondeGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
FeatureUISet* m_featureUISet;
|
||||
RadiosondeSettings m_settings;
|
||||
RollupState m_rollupState;
|
||||
bool m_doApplySettings;
|
||||
|
||||
Radiosonde* m_radiosonde;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
int m_lastFeatureState;
|
||||
|
||||
QHash<QString, RadiosondeData *> m_radiosondes; // Hash of serial to radiosondes
|
||||
|
||||
QMenu *radiosondesMenu; // Column select context menu
|
||||
|
||||
explicit RadiosondeGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
||||
virtual ~RadiosondeGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
bool handleMessage(const Message& message);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
||||
void sendToMap(const QString &name, const QString &label,
|
||||
const QString &image, const QString &text,
|
||||
const QString &model, float labelOffset,
|
||||
float latitude, float longitude, float altitude, QDateTime positionDateTime,
|
||||
float heading
|
||||
);
|
||||
void updateRadiosondes(RS41Frame *radiosonde, QDateTime dateTime);
|
||||
void resizeTable();
|
||||
QAction *createCheckableItem(QString& text, int idx, bool checked, const char *slot);
|
||||
void plotChart();
|
||||
float getData(RadiosondeSettings::ChartData dataType, RadiosondeData *radiosonde, RS41Frame *message);
|
||||
|
||||
enum RadiosondeCol {
|
||||
RADIOSONDE_COL_SERIAL,
|
||||
RADIOSONDE_COL_TYPE,
|
||||
RADIOSONDE_COL_LATITUDE,
|
||||
RADIOSONDE_COL_LONGITUDE,
|
||||
RADIOSONDE_COL_ALTITUDE,
|
||||
RADIOSONDE_COL_SPEED,
|
||||
RADIOSONDE_COL_VERTICAL_RATE,
|
||||
RADIOSONDE_COL_HEADING,
|
||||
RADIOSONDE_COL_STATUS,
|
||||
RADIOSONDE_COL_PRESSURE,
|
||||
RADIOSONDE_COL_TEMPERATURE,
|
||||
RADIOSONDE_COL_HUMIDITY,
|
||||
RADIOSONDE_COL_ALT_MAX,
|
||||
RADIOSONDE_COL_FREQUENCY,
|
||||
RADIOSONDE_COL_BURSTKILL_STATUS,
|
||||
RADIOSONDE_COL_BURSTKILL_TIMER,
|
||||
RADIOSONDE_COL_LAST_UPDATE,
|
||||
RADIOSONDE_COL_MESSAGES
|
||||
};
|
||||
|
||||
private slots:
|
||||
void onMenuDialogCalled(const QPoint &p);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void handleInputMessages();
|
||||
void on_radiosondes_itemSelectionChanged();
|
||||
void on_radiosondes_cellDoubleClicked(int row, int column);
|
||||
void radiosondes_customContextMenuRequested(QPoint pos);
|
||||
void radiosondes_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
|
||||
void radiosondes_sectionResized(int logicalIndex, int oldSize, int newSize);
|
||||
void radiosondesColumnSelectMenu(QPoint pos);
|
||||
void radiosondesColumnSelectMenuChecked(bool checked = false);
|
||||
void on_y1_currentIndexChanged(int index);
|
||||
void on_y2_currentIndexChanged(int index);
|
||||
void on_deleteAll_clicked();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_RADIOSONDEGUI_H_
|
444
plugins/feature/radiosonde/radiosondegui.ui
Normal file
444
plugins/feature/radiosonde/radiosondegui.ui
Normal file
@ -0,0 +1,444 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RadiosondeGUI</class>
|
||||
<widget class="RollupWidget" name="RadiosondeGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>484</width>
|
||||
<height>732</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>320</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Radiosonde</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="tableContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>336</width>
|
||||
<height>508</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Radiosondes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<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>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QTableWidget" name="radiosondes">
|
||||
<property name="toolTip">
|
||||
<string>Radiosondes</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Serial</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Serial number of the Radiosonde</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Type of Radiosonde</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lat (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Latitude in degrees. East positive</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lon (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Longitude in degrees. North positive.</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Alt (m)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Altitude in metres</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Spd (km/h)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Speed in kilometers per second</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>VR (m/s)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Vertical climb rate in metres per second</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hd (°)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Heading in degrees.</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Status</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>P (hPa)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Pressure in hectpascals</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>T (C)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Temperature in degrees Celsius</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>U (%)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Relative humidity in percent</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Alt Max (m)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Maximum altitude seen in metres</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Freq (MHz)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Transmission frequency in MHz</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>BurstKill Status</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>BurstKill Timer</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Time last position was received</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Updated</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Time last message was received</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Messages</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Number of messages received</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QWidget" name="chartContainer" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="chartButtonsLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="y1Label">
|
||||
<property name="text">
|
||||
<string>Y1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="y1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select data to plot on left Y axis</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Altitude</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Temperature</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Humidity</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pressure</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Speed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vertical rate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Heading</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Battery Voltage</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="y2Label">
|
||||
<property name="text">
|
||||
<string>Y2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="y2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select data to plot on right Y axis</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Altitude</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Temperature</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Humidity</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pressure</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Speed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Vertical rate</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Heading</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Battery Voltage</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteAll">
|
||||
<property name="toolTip">
|
||||
<string>Delete all data</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QChartView" name="chart">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RollupWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QChartView</class>
|
||||
<extends>QGraphicsView</extends>
|
||||
<header>QtCharts</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
79
plugins/feature/radiosonde/radiosondeplugin.cpp
Normal file
79
plugins/feature/radiosonde/radiosondeplugin.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// 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 <QtPlugin>
|
||||
#include "plugin/pluginapi.h"
|
||||
|
||||
#ifndef SERVER_MODE
|
||||
#include "radiosondegui.h"
|
||||
#endif
|
||||
#include "radiosonde.h"
|
||||
#include "radiosondeplugin.h"
|
||||
#include "radiosondewebapiadapter.h"
|
||||
|
||||
const PluginDescriptor RadiosondePlugin::m_pluginDescriptor = {
|
||||
Radiosonde::m_featureId,
|
||||
QStringLiteral("Radiosonde"),
|
||||
QStringLiteral("6.20.0"),
|
||||
QStringLiteral("(c) Jon Beniston, M7RCE"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
RadiosondePlugin::RadiosondePlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& RadiosondePlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void RadiosondePlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerFeature(Radiosonde::m_featureIdURI, Radiosonde::m_featureId, this);
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
FeatureGUI* RadiosondePlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
(void) featureUISet;
|
||||
(void) feature;
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
FeatureGUI* RadiosondePlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
|
||||
{
|
||||
return RadiosondeGUI::create(m_pluginAPI, featureUISet, feature);
|
||||
}
|
||||
#endif
|
||||
|
||||
Feature* RadiosondePlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
|
||||
{
|
||||
return new Radiosonde(webAPIAdapterInterface);
|
||||
}
|
||||
|
||||
FeatureWebAPIAdapter* RadiosondePlugin::createFeatureWebAPIAdapter() const
|
||||
{
|
||||
return new RadiosondeWebAPIAdapter();
|
||||
}
|
49
plugins/feature/radiosonde/radiosondeplugin.h
Normal file
49
plugins/feature/radiosonde/radiosondeplugin.h
Normal file
@ -0,0 +1,49 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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_FEATURE_RADIOSONDEPLUGIN_H
|
||||
#define INCLUDE_FEATURE_RADIOSONDEPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class FeatureGUI;
|
||||
class WebAPIAdapterInterface;
|
||||
|
||||
class RadiosondePlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.feature.radiosonde")
|
||||
|
||||
public:
|
||||
explicit RadiosondePlugin(QObject* parent = nullptr);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
|
||||
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
|
||||
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
|
||||
PluginAPI* m_pluginAPI;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_RADIOSONDEPLUGIN_H
|
149
plugins/feature/radiosonde/radiosondesettings.cpp
Normal file
149
plugins/feature/radiosonde/radiosondesettings.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// 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 <QColor>
|
||||
#include <QDataStream>
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "settings/serializable.h"
|
||||
|
||||
#include "radiosondesettings.h"
|
||||
|
||||
const QStringList RadiosondeSettings::m_pipeTypes = {
|
||||
QStringLiteral("RadiosondeDemod")
|
||||
};
|
||||
|
||||
const QStringList RadiosondeSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.radiosondedemod")
|
||||
};
|
||||
|
||||
RadiosondeSettings::RadiosondeSettings() :
|
||||
m_rollupState(nullptr)
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void RadiosondeSettings::resetToDefaults()
|
||||
{
|
||||
m_title = "Radiosonde";
|
||||
m_rgbColor = QColor(102, 0, 102).rgb();
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIFeatureSetIndex = 0;
|
||||
m_reverseAPIFeatureIndex = 0;
|
||||
|
||||
m_y1 = ALTITUDE;
|
||||
m_y2 = TEMPERATURE;
|
||||
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++)
|
||||
{
|
||||
m_radiosondesColumnIndexes[i] = i;
|
||||
m_radiosondesColumnSizes[i] = -1; // Autosize
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray RadiosondeSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeString(1, m_title);
|
||||
s.writeU32(2, m_rgbColor);
|
||||
s.writeBool(3, m_useReverseAPI);
|
||||
s.writeString(4, m_reverseAPIAddress);
|
||||
s.writeU32(5, m_reverseAPIPort);
|
||||
s.writeU32(6, m_reverseAPIFeatureSetIndex);
|
||||
s.writeU32(7, m_reverseAPIFeatureIndex);
|
||||
|
||||
if (m_rollupState) {
|
||||
s.writeBlob(8, m_rollupState->serialize());
|
||||
}
|
||||
|
||||
s.writeS32(10, (int)m_y1);
|
||||
s.writeS32(11, (int)m_y2);
|
||||
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
|
||||
s.writeS32(300 + i, m_radiosondesColumnIndexes[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
|
||||
s.writeS32(400 + i, m_radiosondesColumnSizes[i]);
|
||||
}
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool RadiosondeSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
uint32_t utmp;
|
||||
QString strtmp;
|
||||
QByteArray blob;
|
||||
|
||||
d.readString(1, &m_title, "Radiosonde");
|
||||
d.readU32(2, &m_rgbColor, QColor(102, 0, 102).rgb());
|
||||
d.readBool(3, &m_useReverseAPI, false);
|
||||
d.readString(4, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(5, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(6, &utmp, 0);
|
||||
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readU32(7, &utmp, 0);
|
||||
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
if (m_rollupState)
|
||||
{
|
||||
d.readBlob(8, &bytetmp);
|
||||
m_rollupState->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readS32(10, (int *)&m_y1, (int)ALTITUDE);
|
||||
d.readS32(11, (int *)&m_y2, (int)TEMPERATURE);
|
||||
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
|
||||
d.readS32(300 + i, &m_radiosondesColumnIndexes[i], i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < RADIOSONDES_COLUMNS; i++) {
|
||||
d.readS32(400 + i, &m_radiosondesColumnSizes[i], -1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
70
plugins/feature/radiosonde/radiosondesettings.h
Normal file
70
plugins/feature/radiosonde/radiosondesettings.h
Normal file
@ -0,0 +1,70 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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_FEATURE_RADIOSONDESETTINGS_H_
|
||||
#define INCLUDE_FEATURE_RADIOSONDESETTINGS_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "util/message.h"
|
||||
|
||||
class Serializable;
|
||||
|
||||
// Number of columns in the table
|
||||
#define RADIOSONDES_COLUMNS 16
|
||||
|
||||
struct RadiosondeSettings
|
||||
{
|
||||
QString m_title;
|
||||
quint32 m_rgbColor;
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIFeatureSetIndex;
|
||||
uint16_t m_reverseAPIFeatureIndex;
|
||||
Serializable *m_rollupState;
|
||||
|
||||
enum ChartData {
|
||||
NONE,
|
||||
ALTITUDE,
|
||||
TEMPERATURE,
|
||||
HUMIDITY,
|
||||
PRESSURE,
|
||||
SPEED,
|
||||
VERTICAL_RATE,
|
||||
HEADING,
|
||||
BATTERY_VOLTAGE
|
||||
};
|
||||
ChartData m_y1;
|
||||
ChartData m_y2;
|
||||
|
||||
int m_radiosondesColumnIndexes[RADIOSONDES_COLUMNS];
|
||||
int m_radiosondesColumnSizes[RADIOSONDES_COLUMNS];
|
||||
|
||||
RadiosondeSettings();
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
|
||||
|
||||
static const QStringList m_pipeTypes;
|
||||
static const QStringList m_pipeURIs;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FEATURE_RADIOSONDESETTINGS_H_
|
52
plugins/feature/radiosonde/radiosondewebapiadapter.cpp
Normal file
52
plugins/feature/radiosonde/radiosondewebapiadapter.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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 "SWGFeatureSettings.h"
|
||||
#include "radiosonde.h"
|
||||
#include "radiosondewebapiadapter.h"
|
||||
|
||||
RadiosondeWebAPIAdapter::RadiosondeWebAPIAdapter()
|
||||
{}
|
||||
|
||||
RadiosondeWebAPIAdapter::~RadiosondeWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int RadiosondeWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadiosondeSettings(new SWGSDRangel::SWGRadiosondeSettings());
|
||||
response.getRadiosondeSettings()->init();
|
||||
Radiosonde::webapiFormatFeatureSettings(response, m_settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RadiosondeWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force; // no action
|
||||
(void) errorMessage;
|
||||
Radiosonde::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
|
||||
|
||||
return 200;
|
||||
}
|
50
plugins/feature/radiosonde/radiosondewebapiadapter.h
Normal file
50
plugins/feature/radiosonde/radiosondewebapiadapter.h
Normal file
@ -0,0 +1,50 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Jon Beniston, M7RCE //
|
||||
// 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_Radiosonde_WEBAPIADAPTER_H
|
||||
#define INCLUDE_Radiosonde_WEBAPIADAPTER_H
|
||||
|
||||
#include "feature/featurewebapiadapter.h"
|
||||
#include "radiosondesettings.h"
|
||||
|
||||
/**
|
||||
* Standalone API adapter only for the settings
|
||||
*/
|
||||
class RadiosondeWebAPIAdapter : public FeatureWebAPIAdapter {
|
||||
public:
|
||||
RadiosondeWebAPIAdapter();
|
||||
virtual ~RadiosondeWebAPIAdapter();
|
||||
|
||||
virtual QByteArray serialize() const { return m_settings.serialize(); }
|
||||
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& featureSettingsKeys,
|
||||
SWGSDRangel::SWGFeatureSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
private:
|
||||
RadiosondeSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_Radiosonde_WEBAPIADAPTER_H
|
55
plugins/feature/radiosonde/readme.md
Normal file
55
plugins/feature/radiosonde/readme.md
Normal file
@ -0,0 +1,55 @@
|
||||
<h1>Radiosonde Feature Plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
The Radiosonde feature displays a table containing the most recent information received from radiosondes
|
||||
based on data received via [Radiosonde Demodulators](../../channelrx/demodradiasonde/readme.md).
|
||||
|
||||
The chart can plot two data series vs time for the radiosonde selected in the table.
|
||||
|
||||
The Radiosonde feature can draw ballons objects on the [Map](../../feature/map/readme.md) feature in 2D and 3D.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Radiosonde feature plugin GUI](../../../doc/img/Radiosonde_plugin.png)
|
||||
|
||||
<h3>Radiosonde Table</h3>
|
||||
|
||||
The Radiosonde table displays the current status of each radiosonde, based on the latest received data from all Radiosonde Demodulators.
|
||||
|
||||
* Serial - The serial number that uniquely identifiers each radiosonde.
|
||||
* Type - The type of radiosonde.
|
||||
* Lat (°) - Latitude in degrees. East positive. Double clicking on this column will center the map on this object.
|
||||
* Lon (°) - Longitude in degrees. West positive. Double clicking on this column will center the map on this object.
|
||||
* Alt (m) - The altitude of the radiosonde in metres.
|
||||
* Spd (km/h) - Speed over ground in kilometres per hour.
|
||||
* VR (m/s) - Vertical climb rate in metres per second.
|
||||
* Hdg (°) - Heading in degrees.
|
||||
* Status - Flight status of the radiosonde (E.g. On ground, ascent or descent).
|
||||
* P (hPA) - Air pressure in hectopascals. Not all RS41s include a pressure sensor. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* T (°C) - Air temperature in degrees Celsius. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* U (%) - Relative humidity in percent. A value ending with 'U' indicates a uncalibrated estimate and may be inaccurate.
|
||||
* Alt Max (m) - The maximum altitude seen for the radiosonde in metres.
|
||||
* Freq (MHz) - The transmission frequency in megahertz as indicated by the radiosonde.
|
||||
* BurstKill status - Whether the BurstKill timer is active.
|
||||
* BurstKill timer - BurstKill timer.
|
||||
* Updated - Gives the date and time the last message was received.
|
||||
* Messages - Displays the number of messages received.
|
||||
|
||||
Right clicking on the table header allows you to select which columns to show. The columns can be reorderd by left clicking and dragging the column header.
|
||||
|
||||
Right clicking on a table cell allows you to copy the cell contents, or find the radiosonde on the map.
|
||||
|
||||
<h3>Map</h3>
|
||||
|
||||
The Radiosonde feature can plot ballons (during ascent) and parachutes (during descent) on the [Map](../../feature/map/readme.md).
|
||||
To use, simply open a Map feature and the Radiosonde plugin will display objects based upon the data it receives from that point.
|
||||
Selecting a radiosonde item on the map will display a text bubble containing information from the above table.
|
||||
To centre the map on an item in the table, double click in the Lat or Lon columns.
|
||||
|
||||
![Radiosonde on map](../../../doc/img/Radiosonde_plugin_map.png)
|
||||
|
||||
<h2>Attribution</h2>
|
||||
|
||||
Hot-air-balloon icons created by Freepik - https://www.flaticon.com/free-icons/hot-air-balloon
|
||||
Parachute icons created by Freepik - https://www.flaticon.com/free-icons/parachute
|
@ -191,6 +191,7 @@ set(sdrbase_SOURCES
|
||||
util/aprs.cpp
|
||||
util/astronomy.cpp
|
||||
util/azel.cpp
|
||||
util/coordinates.cpp
|
||||
util/crc.cpp
|
||||
util/CRC64.cpp
|
||||
util/csv.cpp
|
||||
@ -210,6 +211,7 @@ set(sdrbase_SOURCES
|
||||
util/planespotters.cpp
|
||||
util/png.cpp
|
||||
util/prettyprint.cpp
|
||||
util/radiosonde.cpp
|
||||
util/rtpsink.cpp
|
||||
util/syncmessenger.cpp
|
||||
util/samplesourceserializer.cpp
|
||||
@ -406,6 +408,7 @@ set(sdrbase_HEADERS
|
||||
util/aprs.h
|
||||
util/astronomy.h
|
||||
util/azel.h
|
||||
util/coordinates.h
|
||||
util/CRC64.h
|
||||
util/csv.h
|
||||
util/db.h
|
||||
@ -430,6 +433,7 @@ set(sdrbase_HEADERS
|
||||
util/planespotters.h
|
||||
util/png.h
|
||||
util/prettyprint.h
|
||||
util/radiosonde.h
|
||||
util/rtpsink.h
|
||||
util/syncmessenger.h
|
||||
util/samplesourceserializer.h
|
||||
|
318
sdrbase/util/coordinates.cpp
Normal file
318
sdrbase/util/coordinates.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2011-2020 Cesium Contributors //
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "coordinates.h"
|
||||
#include "units.h"
|
||||
|
||||
// Scale cartesian position on to surface of ellipsoid
|
||||
QVector3D Coordinates::scaleToGeodeticSurface(QVector3D cartesian, QVector3D oneOverRadii, QVector3D oneOverRadiiSquared)
|
||||
{
|
||||
float centerToleranceSquared = 0.1;
|
||||
|
||||
double x2 = cartesian.x() * cartesian.x() * oneOverRadii.x() * oneOverRadii.x();
|
||||
double y2 = cartesian.y() * cartesian.y() * oneOverRadii.y() * oneOverRadii.y();
|
||||
double z2 = cartesian.z() * cartesian.z() * oneOverRadii.z() * oneOverRadii.z();
|
||||
|
||||
double squaredNorm = x2 + y2 + z2;
|
||||
double ratio = sqrt(1.0 / squaredNorm);
|
||||
|
||||
QVector3D intersection = cartesian * ratio;
|
||||
|
||||
if (squaredNorm < centerToleranceSquared) {
|
||||
return intersection;
|
||||
}
|
||||
|
||||
QVector3D gradient(
|
||||
intersection.x() * oneOverRadiiSquared.x() * 2.0,
|
||||
intersection.y() * oneOverRadiiSquared.y() * 2.0,
|
||||
intersection.z() * oneOverRadiiSquared.z() * 2.0
|
||||
);
|
||||
|
||||
double lambda = ((1.0 - ratio) * cartesian.length()) / (0.5 * gradient.length());
|
||||
|
||||
double correction = 0.0;
|
||||
double func;
|
||||
double denominator;
|
||||
double xMultiplier;
|
||||
double yMultiplier;
|
||||
double zMultiplier;
|
||||
double xMultiplier2;
|
||||
double yMultiplier2;
|
||||
double zMultiplier2;
|
||||
double xMultiplier3;
|
||||
double yMultiplier3;
|
||||
double zMultiplier3;
|
||||
|
||||
do
|
||||
{
|
||||
lambda -= correction;
|
||||
|
||||
xMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquared.x());
|
||||
yMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquared.y());
|
||||
zMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquared.z());
|
||||
|
||||
xMultiplier2 = xMultiplier * xMultiplier;
|
||||
yMultiplier2 = yMultiplier * yMultiplier;
|
||||
zMultiplier2 = zMultiplier * zMultiplier;
|
||||
|
||||
xMultiplier3 = xMultiplier2 * xMultiplier;
|
||||
yMultiplier3 = yMultiplier2 * yMultiplier;
|
||||
zMultiplier3 = zMultiplier2 * zMultiplier;
|
||||
|
||||
func = x2 * xMultiplier2 + y2 * yMultiplier2 + z2 * zMultiplier2 - 1.0;
|
||||
|
||||
denominator =
|
||||
x2 * xMultiplier3 * oneOverRadiiSquared.x() +
|
||||
y2 * yMultiplier3 * oneOverRadiiSquared.y() +
|
||||
z2 * zMultiplier3 * oneOverRadiiSquared.z();
|
||||
|
||||
double derivative = -2.0 * denominator;
|
||||
|
||||
correction = func / derivative;
|
||||
}
|
||||
while (abs(func) > 0.000000000001);
|
||||
|
||||
QVector3D result(
|
||||
cartesian.x() * xMultiplier,
|
||||
cartesian.y() * yMultiplier,
|
||||
cartesian.z() * zMultiplier
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// QVector3D.normalized doesn't work with small numbers
|
||||
QVector3D Coordinates::normalized(QVector3D vec)
|
||||
{
|
||||
QVector3D result;
|
||||
float magnitude = vec.length();
|
||||
result.setX(vec.x() / magnitude);
|
||||
result.setY(vec.y() / magnitude);
|
||||
result.setZ(vec.z() / magnitude);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert ECEF position to geodetic coordinates
|
||||
void Coordinates::ecefToGeodetic(double x, double y, double z, double &latitude, double &longitude, double &height)
|
||||
{
|
||||
QVector3D wgs84OneOverRadix(1.0 / 6378137.0,
|
||||
1.0 / 6378137.0,
|
||||
1.0 / 6356752.3142451793);
|
||||
QVector3D wgs84OneOverRadiiSquared(1.0 / (6378137.0 * 6378137.0),
|
||||
1.0 / (6378137.0 * 6378137.0),
|
||||
1.0 / (6356752.3142451793 * 6356752.3142451793));
|
||||
|
||||
QVector3D cartesian(x, y, z);
|
||||
|
||||
QVector3D p = scaleToGeodeticSurface(cartesian, wgs84OneOverRadix, wgs84OneOverRadiiSquared);
|
||||
|
||||
QVector3D n = p * wgs84OneOverRadiiSquared;
|
||||
n = normalized(n);
|
||||
|
||||
QVector3D h = cartesian - p;
|
||||
|
||||
longitude = atan2(n.y(), n.x());
|
||||
latitude = asin(n.z());
|
||||
|
||||
longitude = Units::radiansToDegrees(longitude);
|
||||
latitude = Units::radiansToDegrees(latitude);
|
||||
|
||||
double t = QVector3D::dotProduct(h, cartesian);
|
||||
double sign = t >= 0.0 ? 1.0 : 0.0;
|
||||
height = sign * h.length();
|
||||
}
|
||||
|
||||
// Convert ECEF velocity to speed and heading
|
||||
void Coordinates::ecefVelToSpeedHeading(double latitude, double longitude,
|
||||
double velX, double velY, double velZ,
|
||||
double &speed, double &verticalRate, double &heading)
|
||||
{
|
||||
if ((velX == 0.0) && (velY == 0.0) && (velZ == 0.0))
|
||||
{
|
||||
speed = 0.0;
|
||||
heading = 0.0;
|
||||
verticalRate = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
double latRad = Units::degreesToRadians(latitude);
|
||||
double lonRad = Units::degreesToRadians(longitude);
|
||||
|
||||
double sinLat = sin(latRad);
|
||||
double cosLat = cos(latRad);
|
||||
double sinLon = sin(lonRad);
|
||||
double cosLon = cos(lonRad);
|
||||
|
||||
double velEast = -velX * sinLon + velY * cosLon;
|
||||
double velNorth = -velX * sinLat * cosLon - velY * sinLat * sinLon + velZ * cosLat;
|
||||
double velUp = velX * cosLat * cosLon + velY * cosLat * sinLon + velZ * sinLat;
|
||||
|
||||
speed = sqrt(velNorth * velNorth + velEast * velEast);
|
||||
verticalRate = velUp;
|
||||
|
||||
double headingRad = atan2(velEast, velNorth);
|
||||
heading = Units::radiansToDegrees(headingRad);
|
||||
if (heading < 0.0) {
|
||||
heading += 360.0;
|
||||
} else if (heading >= 360.0) {
|
||||
heading -= 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a position specified in longitude, latitude in degrees and height in metres above WGS84 ellipsoid in to
|
||||
// Earth Centered Earth Fixed frame cartesian coordinates
|
||||
// See Cesium.Cartesian3.fromDegrees
|
||||
QVector3D Coordinates::geodeticToECEF(double longitude, double latitude, double height)
|
||||
{
|
||||
return geodeticRadiansToECEF(Units::degreesToRadians(longitude), Units::degreesToRadians(latitude), height);
|
||||
}
|
||||
|
||||
// FIXME: QVector3D is only float!
|
||||
// See Cesium.Cartesian3.fromRadians
|
||||
QVector3D Coordinates::geodeticRadiansToECEF(double longitude, double latitude, double height)
|
||||
{
|
||||
QVector3D wgs84RadiiSquared(6378137.0 * 6378137.0, 6378137.0 * 6378137.0, 6356752.3142451793 * 6356752.3142451793);
|
||||
|
||||
double cosLatitude = cos(latitude);
|
||||
QVector3D n;
|
||||
n.setX(cosLatitude * cos(longitude));
|
||||
n.setY(cosLatitude * sin(longitude));
|
||||
n.setZ(sin(latitude));
|
||||
n.normalize();
|
||||
QVector3D k;
|
||||
k = wgs84RadiiSquared * n;
|
||||
double gamma = sqrt(QVector3D::dotProduct(n, k));
|
||||
k = k / gamma;
|
||||
n = n * height;
|
||||
return k + n;
|
||||
}
|
||||
|
||||
// Convert heading, pitch and roll in degrees to a quaternoin
|
||||
// See: Cesium.Quaternion.fromHeadingPitchRoll
|
||||
QQuaternion Coordinates::fromHeadingPitchRoll(double heading, double pitch, double roll)
|
||||
{
|
||||
QVector3D xAxis(1, 0, 0);
|
||||
QVector3D yAxis(0, 1, 0);
|
||||
QVector3D zAxis(0, 0, 1);
|
||||
|
||||
QQuaternion rollQ = QQuaternion::fromAxisAndAngle(xAxis, roll);
|
||||
|
||||
QQuaternion pitchQ = QQuaternion::fromAxisAndAngle(yAxis, -pitch);
|
||||
|
||||
QQuaternion headingQ = QQuaternion::fromAxisAndAngle(zAxis, -heading);
|
||||
|
||||
QQuaternion temp = rollQ * pitchQ;
|
||||
|
||||
return headingQ * temp;
|
||||
}
|
||||
|
||||
// Calculate a transformation matrix from a East, North, Up frame at the given position to Earth Centered Earth Fixed frame
|
||||
// See: Cesium.Transforms.eastNorthUpToFixedFrame
|
||||
QMatrix4x4 Coordinates::eastNorthUpToECEF(QVector3D origin)
|
||||
{
|
||||
// TODO: Handle special case at centre of earth and poles
|
||||
QVector3D up = origin.normalized();
|
||||
QVector3D east(-origin.y(), origin.x(), 0.0);
|
||||
east.normalize();
|
||||
QVector3D north = QVector3D::crossProduct(up, east);
|
||||
QMatrix4x4 result(
|
||||
east.x(), north.x(), up.x(), origin.x(),
|
||||
east.y(), north.y(), up.y(), origin.y(),
|
||||
east.z(), north.z(), up.z(), origin.z(),
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert 3x3 rotation matrix to a quaternoin
|
||||
// Although there is a method for this in Qt: QQuaternion::fromRotationMatrix, it seems to
|
||||
// result in different signs, so the following is based on Cesium code
|
||||
QQuaternion Coordinates::fromRotation(QMatrix3x3 mat)
|
||||
{
|
||||
QQuaternion q;
|
||||
|
||||
double trace = mat(0, 0) + mat(1, 1) + mat(2, 2);
|
||||
|
||||
if (trace > 0.0)
|
||||
{
|
||||
double root = sqrt(trace + 1.0);
|
||||
q.setScalar(0.5 * root);
|
||||
root = 0.5 / root;
|
||||
|
||||
q.setX((mat(2,1) - mat(1,2)) * root);
|
||||
q.setY((mat(0,2) - mat(2,0)) * root);
|
||||
q.setZ((mat(1,0) - mat(0,1)) * root);
|
||||
}
|
||||
else
|
||||
{
|
||||
double next[] = {1, 2, 0};
|
||||
int i = 0;
|
||||
if (mat(1,1) > mat(0,0)) {
|
||||
i = 1;
|
||||
}
|
||||
if (mat(2,2) > mat(0,0) && mat(2,2) > mat(1,1)) {
|
||||
i = 2;
|
||||
}
|
||||
int j = next[i];
|
||||
int k = next[j];
|
||||
|
||||
double root = sqrt(mat(i,i) - mat(j,j) - mat(k,k) + 1);
|
||||
double quat[] = {0.0, 0.0, 0.0};
|
||||
quat[i] = 0.5 * root;
|
||||
root = 0.5 / root;
|
||||
|
||||
q.setScalar((mat(j,k) - mat(k,j)) * root);
|
||||
quat[j] = (mat(i,j) + mat(j,i)) * root;
|
||||
quat[k] = (mat(i,k) + mat(k,i)) * root;
|
||||
q.setX(-quat[0]);
|
||||
q.setY(-quat[1]);
|
||||
q.setZ(-quat[2]);
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
// Calculate orientation quaternion for a model (such as an aircraft) based on position and (HPR) heading, pitch and roll (in degrees)
|
||||
// While Cesium supports specifying orientation as HPR, CZML doesn't currently. See https://github.com/CesiumGS/cesium/issues/5184
|
||||
// CZML requires the orientation to be in the Earth Centered Earth Fixed (geocentric) reference frame (https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates)
|
||||
// The orientation therefore depends not only on HPR but also on position
|
||||
//
|
||||
// glTF uses a right-handed axis convention; that is, the cross product of right and forward yields up. glTF defines +Y as up, +Z as forward, and -X as right.
|
||||
// Cesium.Quaternion.fromHeadingPitchRoll Heading is the rotation about the negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about the positive x axis.
|
||||
QQuaternion Coordinates::orientation(double longitude, double latitude, double altitude, double heading, double pitch, double roll)
|
||||
{
|
||||
// Forward direction for gltf models in Cesium seems to be Eastward, rather than Northward, so we adjust heading by -90 degrees
|
||||
heading = -90 + heading;
|
||||
|
||||
// Convert position to Earth Centered Earth Fixed (ECEF) frame
|
||||
QVector3D positionECEF = geodeticToECEF(longitude, latitude, altitude);
|
||||
|
||||
// Calculate matrix to transform from East, North, Up (ENU) frame to ECEF frame
|
||||
QMatrix4x4 enuToECEFTransform = eastNorthUpToECEF(positionECEF);
|
||||
|
||||
// Calculate rotation based on HPR in ENU frame
|
||||
QQuaternion hprENU = fromHeadingPitchRoll(heading, pitch, roll);
|
||||
|
||||
// Transform rotation from ENU to ECEF
|
||||
QMatrix3x3 hprENU3 = hprENU.toRotationMatrix();
|
||||
QMatrix4x4 hprENU4(hprENU3);
|
||||
QMatrix4x4 transform = enuToECEFTransform * hprENU4;
|
||||
|
||||
// Convert from 4x4 matrix to 3x3 matrix then to a quaternion
|
||||
QQuaternion oq = fromRotation(transform.toGenericMatrix<3,3>());
|
||||
|
||||
return oq;
|
||||
}
|
52
sdrbase/util/coordinates.h
Normal file
52
sdrbase/util/coordinates.h
Normal file
@ -0,0 +1,52 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_COORDINATES_H
|
||||
#define INCLUDE_COORDINATES_H
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QVector3D>
|
||||
#include <QMatrix3x3>
|
||||
#include <QMatrix4x4>
|
||||
#include <QQuaternion>
|
||||
|
||||
// Functions for transformations between geodetic and ECEF coordinates
|
||||
class SDRBASE_API Coordinates {
|
||||
|
||||
public:
|
||||
|
||||
static QVector3D geodeticToECEF(double longitude, double latitude, double height=0.0);
|
||||
static QVector3D geodeticRadiansToECEF(double longitude, double latitude, double height=0.0);
|
||||
static QMatrix4x4 eastNorthUpToECEF(QVector3D origin);
|
||||
static void ecefToGeodetic(double x, double y, double z, double &latitude, double &longitude, double &height);
|
||||
static void ecefVelToSpeedHeading(double latitude, double longitude,
|
||||
double velX, double velY, double velZ,
|
||||
double &speed, double &verticalRate, double &heading);
|
||||
static QQuaternion orientation(double longitude, double latitude, double altitude, double heading, double pitch, double roll);
|
||||
|
||||
protected:
|
||||
|
||||
static QVector3D scaleToGeodeticSurface(QVector3D cartesian, QVector3D oneOverRadii, QVector3D oneOverRadiiSquared);
|
||||
static QVector3D normalized(QVector3D vec);
|
||||
static QQuaternion fromHeadingPitchRoll(double heading, double pitch, double roll);
|
||||
static QQuaternion fromRotation(QMatrix3x3 mat);
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_COORDINATES_H
|
710
sdrbase/util/radiosonde.cpp
Normal file
710
sdrbase/util/radiosonde.cpp
Normal file
@ -0,0 +1,710 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// Based on code and docs by einergehtnochrein, rs1729 and bazjo //
|
||||
// //
|
||||
// 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 <QDateTime>
|
||||
#include <QVector3D>
|
||||
|
||||
#include "util/radiosonde.h"
|
||||
#include "util/coordinates.h"
|
||||
|
||||
RS41Frame::RS41Frame(const QByteArray ba) :
|
||||
m_statusValid(false),
|
||||
m_batteryVoltage(0.0),
|
||||
m_pcbTemperature(0),
|
||||
m_humiditySensorHeating(0),
|
||||
m_transmitPower(0),
|
||||
m_maxSubframeNumber(0),
|
||||
m_subframeNumber(0),
|
||||
m_measValid(false),
|
||||
m_gpsInfoValid(false),
|
||||
m_posValid(false),
|
||||
m_latitude(0.0),
|
||||
m_longitude(0.0),
|
||||
m_height(0.0),
|
||||
m_bytes(ba),
|
||||
m_temperatureCalibrated(false),
|
||||
m_pressureCalibrated(false),
|
||||
m_humidityTemperatureCalibrated(false),
|
||||
m_humidityCalibrated(false)
|
||||
{
|
||||
int length = getFrameLength(ba[RS41_OFFSET_FRAME_TYPE]);
|
||||
for (int i = RS41_OFFSET_BLOCK_0; i < length; )
|
||||
{
|
||||
uint8_t blockID = ba[i+0];
|
||||
uint8_t blockLength = ba[i+1];
|
||||
switch (blockID)
|
||||
{
|
||||
case RS41_ID_STATUS:
|
||||
decodeStatus(ba.mid(i+2, blockLength));
|
||||
break;
|
||||
case RS41_ID_MEAS:
|
||||
decodeMeas(ba.mid(i+2, blockLength));
|
||||
break;
|
||||
case RS41_ID_GPSINFO:
|
||||
decodeGPSInfo(ba.mid(i+2, blockLength));
|
||||
break;
|
||||
case RS41_ID_GPSRAW:
|
||||
break;
|
||||
case RS41_ID_GPSPOS:
|
||||
decodeGPSPos(ba.mid(i+2, blockLength));
|
||||
break;
|
||||
case RS41_ID_EMPTY:
|
||||
break;
|
||||
}
|
||||
i += 2 + blockLength + 2; // ID, length, data, CRC
|
||||
}
|
||||
}
|
||||
|
||||
QString RS41Frame::toHex()
|
||||
{
|
||||
return m_bytes.toHex();
|
||||
}
|
||||
|
||||
uint16_t RS41Frame::getUInt16(const QByteArray ba, int offset) const
|
||||
{
|
||||
return (ba[offset] & 0xff)
|
||||
| ((ba[offset+1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
uint32_t RS41Frame::getUInt24(const QByteArray ba, int offset) const
|
||||
{
|
||||
return (ba[offset] & 0xff)
|
||||
| ((ba[offset+1] & 0xff) << 8)
|
||||
| ((ba[offset+2] & 0xff) << 16);
|
||||
}
|
||||
|
||||
uint32_t RS41Frame::getUInt32(const QByteArray ba, int offset) const
|
||||
{
|
||||
return (ba[offset] & 0xff)
|
||||
| ((ba[offset+1] & 0xff) << 8)
|
||||
| ((ba[offset+2] & 0xff) << 16)
|
||||
| ((ba[offset+3] & 0xff) << 24);
|
||||
}
|
||||
|
||||
void RS41Frame::decodeStatus(const QByteArray ba)
|
||||
{
|
||||
m_statusValid = true;
|
||||
m_frameNumber = getUInt16(ba, 0);
|
||||
m_serial = QString(ba.mid(0x2, 8));
|
||||
m_batteryVoltage = (ba[0xa] & 0xff) / 10.0;
|
||||
QStringList phases = {"Ground", "Ascent", "0x2", "Descent"};
|
||||
int phase = ba[0xd] & 0x3;
|
||||
m_flightPhase = phases[phase];
|
||||
m_batteryStatus = (ba[0xe] & 0x10) == 0 ? "OK" : "Low";
|
||||
m_pcbTemperature = (ba[0x10] & 0xff);
|
||||
m_humiditySensorHeating = getUInt16(ba, 0x13);
|
||||
m_transmitPower = ba[0x15] & 0xff;
|
||||
m_maxSubframeNumber = ba[0x16] & 0xff;
|
||||
m_subframeNumber = ba[0x17] & 0xff;
|
||||
m_subframe = ba.mid(0x18, 16);
|
||||
}
|
||||
|
||||
void RS41Frame::decodeMeas(const QByteArray ba)
|
||||
{
|
||||
m_measValid = true;
|
||||
m_tempMain = getUInt24(ba, 0x0);
|
||||
m_tempRef1 = getUInt24(ba, 0x3);
|
||||
m_tempRef2 = getUInt24(ba, 0x6);
|
||||
m_humidityMain = getUInt24(ba, 0x9);
|
||||
m_humidityRef1 = getUInt24(ba, 0xc);
|
||||
m_humidityRef2 = getUInt24(ba, 0xf);
|
||||
m_humidityTempMain = getUInt24(ba, 0x12);
|
||||
m_humidityTempRef1 = getUInt24(ba, 0x15);
|
||||
m_humidityTempRef2 = getUInt24(ba, 0x18);
|
||||
m_pressureMain = getUInt24(ba, 0x1b);
|
||||
m_pressureRef1 = getUInt24(ba, 0x1e);
|
||||
m_pressureRef2 = getUInt24(ba, 0x21);
|
||||
m_pressureTemp = getUInt16(ba, 0x26) / 100.0f;
|
||||
}
|
||||
|
||||
void RS41Frame::decodeGPSInfo(const QByteArray ba)
|
||||
{
|
||||
m_gpsInfoValid = true;
|
||||
uint16_t gpsWeek = getUInt16(ba, 0x0);
|
||||
uint32_t gpsTimeOfWeek = getUInt32(ba, 0x2); // Milliseconds
|
||||
QDateTime epoch(QDate(1980, 1, 6), QTime(0, 0, 0), Qt::OffsetFromUTC, 18); // GPS doesn't count leap seconds
|
||||
m_gpsDateTime = epoch.addDays(gpsWeek*7).addMSecs(gpsTimeOfWeek);
|
||||
}
|
||||
|
||||
void RS41Frame::decodeGPSPos(const QByteArray ba)
|
||||
{
|
||||
m_satellitesUsed = ba[0x12] & 0xff;
|
||||
if (m_satellitesUsed > 0)
|
||||
{
|
||||
m_posValid = true;
|
||||
int32_t ecefX = (int32_t)getUInt32(ba, 0x0);
|
||||
int32_t ecefY = (int32_t)getUInt32(ba, 0x4);
|
||||
int32_t ecefZ = (int32_t)getUInt32(ba, 0x8);
|
||||
// Convert cm to m
|
||||
// Convert to latitude, longitude and altitude
|
||||
Coordinates::ecefToGeodetic(ecefX / 100.0, ecefY / 100.0, ecefZ / 100.0, m_latitude, m_longitude, m_height);
|
||||
int32_t velX = (int16_t)getUInt16(ba, 0xc);
|
||||
int32_t velY = (int16_t)getUInt16(ba, 0xe);
|
||||
int32_t velZ = (int16_t)getUInt16(ba, 0x10);
|
||||
// Convert cm/s to m/s
|
||||
// Calculate speed / heading
|
||||
Coordinates::ecefVelToSpeedHeading(m_latitude, m_longitude, velX / 100.0, velY / 100.0, velZ / 100.0, m_speed, m_verticalRate, m_heading);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the water vapor saturation pressure for a given temperature.
|
||||
float waterVapourSaturationPressure(float tCelsius)
|
||||
{
|
||||
// Convert to Kelvin
|
||||
float T = tCelsius + 273.15f;
|
||||
|
||||
// Correction
|
||||
T = - 0.4931358f
|
||||
+ (1.0f + 4.6094296e-3f) * T
|
||||
- 1.3746454e-5f * T * T
|
||||
+ 1.2743214e-8f * T * T * T;
|
||||
|
||||
// Hyland and Wexler equation
|
||||
float p = expf(-5800.2206f / T
|
||||
+ 1.3914993f
|
||||
+ 6.5459673f * logf(T)
|
||||
- 4.8640239e-2f * T
|
||||
+ 4.1764768e-5f * T * T
|
||||
- 1.4452093e-8f * T * T * T);
|
||||
|
||||
// Scale result to hPa
|
||||
return p / 100.0f;
|
||||
}
|
||||
|
||||
float calcT(int f, int f1, int f2, float r1, float r2, float *poly, float *cal)
|
||||
{
|
||||
/*float g = (float)(f2-f1) / (r2-r1); // gain
|
||||
float Rb = (f1*r2-f2*r1) / (float)(f2-f1); // offset
|
||||
float Rc = f/g - Rb;
|
||||
float R = Rc * cal[0];
|
||||
float T = (poly[0] + poly[1]*R + poly[2]*R*R + cal[1])*(1.0 + cal[2]);
|
||||
return T;
|
||||
*/
|
||||
|
||||
// Convert integer measurement to scale factor
|
||||
float s = (f-f1)/(float)(f2-f1);
|
||||
|
||||
// Calculate resistance (scale between two reference resistors)
|
||||
float rUncal = r1 + (r2 - r1) * s;
|
||||
float r = rUncal * cal[0];
|
||||
|
||||
// Convert resistance to temperature
|
||||
float tUncal = poly[0] + poly[1]*r + poly[2]*r*r;
|
||||
|
||||
// Correct temperature (5th order polynomial)
|
||||
float tCal = 0.0f;
|
||||
for (int i = 6; i > 0; i--)
|
||||
{
|
||||
tCal *= tUncal;
|
||||
tCal += cal[i];
|
||||
}
|
||||
tCal += tUncal;
|
||||
|
||||
return tCal;
|
||||
}
|
||||
|
||||
float calcU(int cInt, int cMin, int cMax, float c1, float c2, float T, float HT, float *capCal, float *matrixCal)
|
||||
{
|
||||
//qDebug() << "cInt " << cInt << " cMin " << cMin << " cMax " << cMax << " c1 " << c1 << " c2 " << c2 << " T " << T << " HT " << HT << " capCal[0] " << capCal[0] << " capCal[1] " << capCal[1];
|
||||
/*
|
||||
float a0 = 7.5f;
|
||||
float a1 = 350.0f / capCal[0];
|
||||
float fh = (cInt-cMin) / (float)(cMax-cMin);
|
||||
float rh = 100.0f * (a1*fh - a0);
|
||||
float T0 = 0.0f;
|
||||
float T1 = -25.0f;
|
||||
rh += T0 - T/5.5;
|
||||
if (T < T1) {
|
||||
rh *= 1.0 + (T1-T)/90.0;
|
||||
}
|
||||
if (rh < 0.0) {
|
||||
rh = 0.0;
|
||||
}
|
||||
if (rh > 100.0) {
|
||||
rh = 100.0;
|
||||
}
|
||||
if (T < -273.0) {
|
||||
rh = -1.0;
|
||||
}
|
||||
|
||||
qDebug() << "RH old method: " << rh; */
|
||||
|
||||
|
||||
// Convert integer measurement to scale factor
|
||||
float s = (cInt - cMin) / (float)(cMax-cMin);
|
||||
|
||||
// Calculate capacitance (scale between two reference caps)
|
||||
float cUncal = c1 + (c2 - c1) * s;
|
||||
float cCal = (cUncal / capCal[0] - 1.0f) * capCal[1];
|
||||
float uUncal = 0.0f;
|
||||
float t = (HT - 20.0f) / 180.0f;
|
||||
float f1 = 1.0f;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
float f2 = 1.0;
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
uUncal += f1 * f2 * matrixCal[i*6+j];
|
||||
f2 *= t;
|
||||
}
|
||||
f1 *= cCal;
|
||||
}
|
||||
|
||||
// Adjust for difference in outside air temperature and the humidty sensor temperature
|
||||
float uCal = uUncal * waterVapourSaturationPressure(T) / waterVapourSaturationPressure(HT);
|
||||
|
||||
// Ensure within range of 0..100%
|
||||
uCal = std::min(100.0f, uCal);
|
||||
uCal = std::max(0.0f, uCal);
|
||||
|
||||
return uCal;
|
||||
}
|
||||
|
||||
float calcP(int f, int f1, int f2, float pressureTemp, float *cal)
|
||||
{
|
||||
// Convert integer measurement to scale factor
|
||||
float s = (f-f1) / (float)(f2-f1);
|
||||
|
||||
float t = pressureTemp;
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
float poly[6];
|
||||
poly[0] = cal[0] + cal[7] * t + cal[11] * t2 + cal[15] * t3;
|
||||
poly[1] = cal[1] + cal[8] * t + cal[12] * t2 + cal[16] * t3;
|
||||
poly[2] = cal[2] + cal[9] * t + cal[13] * t2 + cal[17] * t3;
|
||||
poly[3] = cal[3] + cal[10] * t + cal[14] * t2;
|
||||
poly[4] = cal[4];
|
||||
poly[5] = cal[5];
|
||||
|
||||
float p = cal[6] / s;
|
||||
float p2 = p * p;
|
||||
float p3 = p2 * p;
|
||||
float p4 = p3 * p;
|
||||
float p5 = p4 * p;
|
||||
|
||||
float pCal = poly[0] + poly[1] * p + poly[2] * p2 + poly[3] * p3 + poly[4] * p4 + poly[5] * p5;
|
||||
|
||||
return pCal;
|
||||
}
|
||||
|
||||
float RS41Frame::getPressureFloat(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_pressureCalibrated) {
|
||||
calcPressure(subframe);
|
||||
}
|
||||
return m_pressure;
|
||||
}
|
||||
|
||||
QString RS41Frame::getPressureString(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_pressureCalibrated) {
|
||||
calcPressure(subframe);
|
||||
}
|
||||
return m_pressureString;
|
||||
}
|
||||
|
||||
float RS41Frame::getTemperatureFloat(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_temperatureCalibrated) {
|
||||
calcTemperature(subframe);
|
||||
}
|
||||
return m_temperature;
|
||||
}
|
||||
|
||||
QString RS41Frame::getTemperatureString(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_temperatureCalibrated) {
|
||||
calcTemperature(subframe);
|
||||
}
|
||||
return m_temperatureString;
|
||||
}
|
||||
|
||||
void RS41Frame::calcPressure(const RS41Subframe *subframe)
|
||||
{
|
||||
float cal[18];
|
||||
|
||||
if (m_pressureMain == 0)
|
||||
{
|
||||
m_pressure = 0.0f;
|
||||
m_pressureString = "";
|
||||
return;
|
||||
}
|
||||
|
||||
m_pressureCalibrated = subframe->getPressureCal(cal);
|
||||
|
||||
m_pressure = calcP(m_pressureMain, m_pressureRef1, m_pressureRef2, m_pressureTemp, cal);
|
||||
|
||||
// RS41 pressure resolution of 0.01hPa
|
||||
m_pressureString = QString::number(m_pressure, 'f', 2);
|
||||
|
||||
if (!m_pressureCalibrated) {
|
||||
m_pressureString = m_pressureString + "U"; // U for uncalibrated
|
||||
}
|
||||
}
|
||||
|
||||
void RS41Frame::calcTemperature(const RS41Subframe *subframe)
|
||||
{
|
||||
float r1, r2;
|
||||
float poly[3];
|
||||
float cal[7];
|
||||
|
||||
if (m_tempMain == 0)
|
||||
{
|
||||
m_temperature = 0.0f;
|
||||
m_temperatureString = "";
|
||||
return;
|
||||
}
|
||||
|
||||
m_temperatureCalibrated = subframe->getTempCal(r1, r2, poly, cal);
|
||||
|
||||
m_temperature = calcT(m_tempMain, m_tempRef1, m_tempRef2,
|
||||
r1, r2,
|
||||
poly, cal);
|
||||
|
||||
// RS41 temperature resolution of 0.01C
|
||||
m_temperatureString = QString::number(m_temperature, 'f', 2);
|
||||
|
||||
if (!m_temperatureCalibrated) {
|
||||
m_temperatureString = m_temperatureString + "U"; // U for uncalibrated
|
||||
}
|
||||
}
|
||||
|
||||
float RS41Frame::getHumidityTemperatureFloat(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_humidityTemperatureCalibrated) {
|
||||
calcHumidityTemperature(subframe);
|
||||
}
|
||||
return m_humidityTemperature;
|
||||
}
|
||||
|
||||
void RS41Frame::calcHumidityTemperature(const RS41Subframe *subframe)
|
||||
{
|
||||
float r1, r2;
|
||||
float poly[3];
|
||||
float cal[7];
|
||||
|
||||
if (m_humidityTempMain == 0)
|
||||
{
|
||||
m_humidityTemperature = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
m_humidityTemperatureCalibrated = subframe->getHumidityTempCal(r1, r2, poly, cal);
|
||||
|
||||
m_humidityTemperature = calcT(m_humidityTempMain, m_humidityTempRef1, m_humidityTempRef2,
|
||||
r1, r2,
|
||||
poly, cal);
|
||||
}
|
||||
|
||||
float RS41Frame::getHumidityFloat(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_humidityCalibrated) {
|
||||
calcHumidity(subframe);
|
||||
}
|
||||
return m_humidity;
|
||||
}
|
||||
|
||||
QString RS41Frame::getHumidityString(const RS41Subframe *subframe)
|
||||
{
|
||||
if (!m_humidityCalibrated) {
|
||||
calcHumidity(subframe);
|
||||
}
|
||||
return m_humidityString;
|
||||
}
|
||||
|
||||
void RS41Frame::calcHumidity(const RS41Subframe *subframe)
|
||||
{
|
||||
float c1, c2;
|
||||
float capCal[2];
|
||||
float calMatrix[7*6];
|
||||
|
||||
if (m_humidityMain == 0)
|
||||
{
|
||||
m_humidity = 0.0f;
|
||||
m_humidityString = "";
|
||||
return;
|
||||
}
|
||||
|
||||
float temperature = getTemperatureFloat(subframe);
|
||||
float humidityTemperature = getHumidityTemperatureFloat(subframe);
|
||||
|
||||
bool humidityCalibrated = subframe->getHumidityCal(c1, c2, capCal, calMatrix);
|
||||
|
||||
m_humidityCalibrated = m_temperatureCalibrated && m_humidityTemperatureCalibrated && humidityCalibrated;
|
||||
|
||||
m_humidity = calcU(m_humidityMain, m_humidityRef1, m_humidityRef2,
|
||||
c1, c2,
|
||||
temperature, humidityTemperature,
|
||||
capCal, calMatrix);
|
||||
|
||||
// RS41 humidity resolution of 0.1%
|
||||
m_humidityString = QString::number(m_humidity, 'f', 1);
|
||||
|
||||
if (!m_humidityCalibrated) {
|
||||
m_humidityString = m_humidityString + "U"; // U for uncalibrated
|
||||
}
|
||||
}
|
||||
|
||||
RS41Frame* RS41Frame::decode(const QByteArray ba)
|
||||
{
|
||||
return new RS41Frame(ba);
|
||||
}
|
||||
|
||||
int RS41Frame::getFrameLength(int frameType)
|
||||
{
|
||||
return frameType == RS41_FRAME_STD ? RS41_LENGTH_STD : RS41_LENGTH_EXT;
|
||||
}
|
||||
|
||||
RS41Subframe::RS41Subframe() :
|
||||
m_subframe(51*16, (char)0)
|
||||
{
|
||||
for (int i = 0; i < 51; i++) {
|
||||
m_subframeValid[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update subframe with subframe data from received message
|
||||
void RS41Subframe::update(RS41Frame *message)
|
||||
{
|
||||
m_subframeValid[message->m_subframeNumber] = true;
|
||||
int offset = message->m_subframeNumber * 16;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
m_subframe[offset+i] = message->m_subframe[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate if we have all the required temperature calibration data
|
||||
bool RS41Subframe::hasTempCal() const
|
||||
{
|
||||
return m_subframeValid[3] && m_subframeValid[4] && m_subframeValid[5] && m_subframeValid[6] && m_subframeValid[7];
|
||||
}
|
||||
|
||||
// Get temperature calibration data
|
||||
// r1, r2 - Temperature reference resistances (Ohms)
|
||||
// poly - Resistance to temperature 2nd order polynomial
|
||||
bool RS41Subframe::getTempCal(float &r1, float &r2, float *poly, float *cal) const
|
||||
{
|
||||
if (hasTempCal())
|
||||
{
|
||||
r1 = getFloat(0x3d);
|
||||
r2 = getFloat(0x41);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
poly[i] = getFloat(0x4d + i * 4);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
cal[i] = getFloat(0x59 + i * 4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default values
|
||||
r1 = 750.0f;
|
||||
r2 = 1100.0f;
|
||||
poly[0] = -243.9108f;
|
||||
poly[1] = 0.187654f;
|
||||
poly[2] = 8.2e-06f;
|
||||
cal[0] = 1.279928f;
|
||||
for (int i = 1; i < 7; i++) {
|
||||
cal[i] = 0.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate if we have all the required humidty calibration data
|
||||
bool RS41Subframe::hasHumidityCal() const
|
||||
{
|
||||
return m_subframeValid[4] && m_subframeValid[7]
|
||||
&& m_subframeValid[8] && m_subframeValid[9] && m_subframeValid[0xa] && m_subframeValid[0xb]
|
||||
&& m_subframeValid[0xc] && m_subframeValid[0xd] && m_subframeValid[0xe] && m_subframeValid[0xf]
|
||||
&& m_subframeValid[0x10] && m_subframeValid[0x11] && m_subframeValid[0x12];
|
||||
}
|
||||
|
||||
// Get humidty calibration data
|
||||
bool RS41Subframe::getHumidityCal(float &c1, float &c2, float *capCal, float *calMatrix) const
|
||||
{
|
||||
if (hasHumidityCal())
|
||||
{
|
||||
c1 = getFloat(0x45);
|
||||
c2 = getFloat(0x49);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
capCal[i] = getFloat(0x75 + i * 4);
|
||||
}
|
||||
for (int i = 0; i < 7*6; i++) {
|
||||
calMatrix[i] = getFloat(0x7d + i * 4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default values
|
||||
c1 = 0.0f;
|
||||
c2 = 47.0f;
|
||||
capCal[0] = 45.9068f;
|
||||
capCal[1] = 4.92924f;
|
||||
static const float calMatrixDefault[7*6] = {
|
||||
-0.002586f, -2.24367f, 9.92294f, -3.61913f, 54.3554f, -93.3012f,
|
||||
51.7056f, 38.8709f, 209.437f, -378.437f, 9.17326f, 19.5301f,
|
||||
150.257f, -150.907f, -280.315f, 182.293f, 3247.39f, 4083.65f,
|
||||
-233.568f, 345.375f, 200.217f, -388.246f, -3617.66f, 0.0f,
|
||||
225.841f, -233.051f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
-93.0635f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
|
||||
};
|
||||
std::copy(calMatrixDefault, calMatrixDefault + 7*6, calMatrix);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate if we have all the required humidty temperature sensor calibration data
|
||||
bool RS41Subframe::hasHumidityTempCal() const
|
||||
{
|
||||
return m_subframeValid[3] && m_subframeValid[4] && m_subframeValid[0x12] && m_subframeValid[0x13] && m_subframeValid[0x14];
|
||||
}
|
||||
|
||||
// Get humidty temperature sensor calibration data
|
||||
bool RS41Subframe::getHumidityTempCal(float &r1, float &r2, float *poly, float *cal) const
|
||||
{
|
||||
if (hasHumidityTempCal())
|
||||
{
|
||||
r1 = getFloat(0x3d);
|
||||
r2 = getFloat(0x41);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
poly[i] = getFloat(0x125 + i * 4);
|
||||
}
|
||||
for (int i = 0; i < 7; i++) {
|
||||
cal[i] = getFloat(0x131 + i * 4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default values
|
||||
r1 = 750.0f;
|
||||
r2 = 1100.0f;
|
||||
poly[0] = -243.9108f;
|
||||
poly[1] = 0.187654f;
|
||||
poly[2] = 8.2e-06f;
|
||||
cal[0] = 1.279928f;
|
||||
for (int i = 1; i < 7; i++) {
|
||||
cal[i] = 0.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate if we have all the required pressure calibration data
|
||||
bool RS41Subframe::hasPressureCal() const
|
||||
{
|
||||
return m_subframeValid[0x25] && m_subframeValid[0x26] && m_subframeValid[0x27]
|
||||
&& m_subframeValid[0x28] && m_subframeValid[0x29] && m_subframeValid[0x2a];
|
||||
}
|
||||
|
||||
// Get pressure calibration data
|
||||
bool RS41Subframe::getPressureCal(float *cal) const
|
||||
{
|
||||
if (hasPressureCal())
|
||||
{
|
||||
for (int i = 0; i < 18; i++) {
|
||||
cal[i] = getFloat(0x25e + i * 4);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default values - TODO: Need to obtain from inflight device
|
||||
for (int i = 0; i < 18; i++) {
|
||||
cal[i] = 0.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get type of RS41. E.g. "RS41-SGP"
|
||||
QString RS41Subframe::getType() const
|
||||
{
|
||||
if (m_subframeValid[0x21] & m_subframeValid[0x22])
|
||||
{
|
||||
return QString(m_subframe.mid(0x218, 10)).trimmed();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "RS41";
|
||||
}
|
||||
}
|
||||
|
||||
// Get transmission frequency in MHz
|
||||
QString RS41Subframe::getFrequencyMHz() const
|
||||
{
|
||||
if (m_subframeValid[0])
|
||||
{
|
||||
uint8_t lower = m_subframe[2] & 0xff;
|
||||
uint8_t upper = m_subframe[3] & 0xff;
|
||||
float freq = 400.0 + (upper + (lower / 255.0)) * 0.04;
|
||||
return QString::number(freq, 'f', 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
QString RS41Subframe::getBurstKillStatus() const
|
||||
{
|
||||
if (m_subframeValid[2])
|
||||
{
|
||||
uint8_t status = m_subframe[0x2b];
|
||||
return status == 0 ? "Inactive" : "Active";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// Seconds until power-off once active
|
||||
QString RS41Subframe::getBurstKillTimer() const
|
||||
{
|
||||
if (m_subframeValid[0x31])
|
||||
{
|
||||
uint16_t secs = getUInt16(0x316);
|
||||
QTime t(0, 0, 0);
|
||||
t = t.addSecs(secs);
|
||||
return t.toString("hh:mm:ss");
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t RS41Subframe::getUInt16(int offset) const
|
||||
{
|
||||
return (m_subframe[offset] & 0xff) | ((m_subframe[offset+1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
float RS41Subframe::getFloat(int offset) const
|
||||
{
|
||||
float f;
|
||||
// Assumes host is little endian with 32-bit float
|
||||
memcpy(&f, m_subframe.data() + offset, 4);
|
||||
return f;
|
||||
}
|
180
sdrbase/util/radiosonde.h
Normal file
180
sdrbase/util/radiosonde.h
Normal file
@ -0,0 +1,180 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_RADIOSONDE_H
|
||||
#define INCLUDE_RADIOSONDE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include <QtCore>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "util/units.h"
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#define RS41_LENGTH_STD 320
|
||||
#define RS41_LENGTH_EXT 518
|
||||
|
||||
#define RS41_OFFSET_RS 0x08
|
||||
#define RS41_OFFSET_FRAME_TYPE 0x38
|
||||
#define RS41_OFFSET_BLOCK_0 0x39
|
||||
|
||||
#define RS41_FRAME_STD 0x0f
|
||||
#define RS41_FRAME_EXT 0xf0
|
||||
|
||||
#define RS41_ID_STATUS 0x79
|
||||
#define RS41_ID_MEAS 0x7a
|
||||
#define RS41_ID_GPSINFO 0x7c
|
||||
#define RS41_ID_GPSRAW 0x7d
|
||||
#define RS41_ID_GPSPOS 0x7b
|
||||
#define RS41_ID_EMPTY 0x76
|
||||
|
||||
#define RS41_RS_N 255
|
||||
#define RS41_RS_K 231
|
||||
#define RS41_RS_2T (RS41_RS_N-RS41_RS_K)
|
||||
#define RS41_RS_INTERLEAVE 2
|
||||
#define RS41_RS_DATA (264/RS41_RS_INTERLEAVE)
|
||||
#define RS41_RS_PAD (RS41_RS_K-RS41_RS_DATA)
|
||||
|
||||
class RS41Subframe;
|
||||
|
||||
// Frame of data transmitted by RS41 radiosonde
|
||||
class SDRBASE_API RS41Frame {
|
||||
public:
|
||||
|
||||
// Status
|
||||
bool m_statusValid;
|
||||
uint16_t m_frameNumber; // Increments every frame
|
||||
QString m_serial; // Serial number
|
||||
float m_batteryVoltage; // In volts
|
||||
QString m_flightPhase; // On ground, ascent, descent
|
||||
QString m_batteryStatus; // OK or Low
|
||||
uint8_t m_pcbTemperature; // In degrees C
|
||||
uint16_t m_humiditySensorHeating; // 0..1000
|
||||
uint8_t m_transmitPower; // 0..7
|
||||
uint8_t m_maxSubframeNumber;
|
||||
uint8_t m_subframeNumber;
|
||||
QByteArray m_subframe; // 16 bytes of subframe
|
||||
|
||||
// Meas
|
||||
bool m_measValid;
|
||||
uint32_t m_tempMain;
|
||||
uint32_t m_tempRef1;
|
||||
uint32_t m_tempRef2;
|
||||
uint32_t m_humidityMain;
|
||||
uint32_t m_humidityRef1;
|
||||
uint32_t m_humidityRef2;
|
||||
uint32_t m_humidityTempMain;
|
||||
uint32_t m_humidityTempRef1;
|
||||
uint32_t m_humidityTempRef2;
|
||||
uint32_t m_pressureMain;
|
||||
uint32_t m_pressureRef1;
|
||||
uint32_t m_pressureRef2;
|
||||
float m_pressureTemp; // Pressure sensor module temperature - In degrees C
|
||||
|
||||
// GPSInfo
|
||||
bool m_gpsInfoValid;
|
||||
QDateTime m_gpsDateTime;
|
||||
|
||||
// GPSPos
|
||||
bool m_posValid;
|
||||
double m_latitude; // In degrees
|
||||
double m_longitude; // In degrees
|
||||
double m_height; // In metres
|
||||
double m_speed; // In m/s
|
||||
double m_heading; // In degreees
|
||||
double m_verticalRate; // In m/s
|
||||
int m_satellitesUsed;
|
||||
|
||||
RS41Frame(const QByteArray ba);
|
||||
~RS41Frame() {}
|
||||
QString toHex();
|
||||
void decodeStatus(const QByteArray ba);
|
||||
void decodeMeas(const QByteArray ba);
|
||||
void decodeGPSInfo(const QByteArray ba);
|
||||
void decodeGPSPos(const QByteArray ba);
|
||||
|
||||
float getPressureFloat(const RS41Subframe *subframe);
|
||||
QString getPressureString(const RS41Subframe *subframe);
|
||||
float getTemperatureFloat(const RS41Subframe *subframe);
|
||||
QString getTemperatureString(const RS41Subframe *subframe);
|
||||
float getHumidityTemperatureFloat(const RS41Subframe *subframe);
|
||||
float getHumidityFloat(const RS41Subframe *subframe);
|
||||
QString getHumidityString(const RS41Subframe *subframe);
|
||||
|
||||
static RS41Frame* decode(const QByteArray ba);
|
||||
static int getFrameLength(int frameType);
|
||||
|
||||
protected:
|
||||
uint16_t getUInt16(const QByteArray ba, int offset) const;
|
||||
uint32_t getUInt24(const QByteArray ba, int offset) const;
|
||||
uint32_t getUInt32(const QByteArray ba, int offset) const;
|
||||
|
||||
void calcPressure(const RS41Subframe *subframe);
|
||||
void calcTemperature(const RS41Subframe *subframe);
|
||||
void calcHumidityTemperature(const RS41Subframe *subframe);
|
||||
void calcHumidity(const RS41Subframe *subframe);
|
||||
|
||||
QByteArray m_bytes;
|
||||
|
||||
float m_pressure;
|
||||
QString m_pressureString;
|
||||
bool m_pressureCalibrated;
|
||||
float m_temperature;
|
||||
QString m_temperatureString;
|
||||
bool m_temperatureCalibrated;
|
||||
float m_humidityTemperature;
|
||||
bool m_humidityTemperatureCalibrated;
|
||||
float m_humidity;
|
||||
QString m_humidityString;
|
||||
bool m_humidityCalibrated;
|
||||
|
||||
};
|
||||
|
||||
// RS41 subframe holding calibration data collected from multiple RS51Frames
|
||||
class SDRBASE_API RS41Subframe {
|
||||
public:
|
||||
|
||||
RS41Subframe();
|
||||
void update(RS41Frame *message);
|
||||
bool hasTempCal() const;
|
||||
bool getTempCal(float &r1, float &r2, float *poly, float *cal) const;
|
||||
bool hasHumidityCal() const;
|
||||
bool getHumidityCal(float &c1, float &c2, float *capCal, float *calMatrix) const;
|
||||
bool hasHumidityTempCal() const;
|
||||
bool getHumidityTempCal(float &r1, float &r2, float *poly, float *cal) const;
|
||||
bool hasPressureCal() const;
|
||||
bool getPressureCal(float *cal) const;
|
||||
QString getType() const;
|
||||
QString getFrequencyMHz() const;
|
||||
QString getBurstKillStatus() const;
|
||||
QString getBurstKillTimer() const;
|
||||
|
||||
protected:
|
||||
|
||||
bool m_subframeValid[51];
|
||||
QByteArray m_subframe;
|
||||
|
||||
uint16_t getUInt16(int offset) const;
|
||||
float getFloat(int offset) const;
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOSONDE_H
|
650
sdrbase/util/reedsolomon.h
Normal file
650
sdrbase/util/reedsolomon.h
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* Reed-Solomon -- Reed-Solomon encoder / decoder library
|
||||
*
|
||||
* Copyright (c) 2014 Hard Consulting Corporation.
|
||||
* Copyright (c) 2006 Phil Karn, KA9Q
|
||||
*
|
||||
* It may be used under the terms of the GNU Lesser General Public License (LGPL).
|
||||
*
|
||||
* Simplified version of https://github.com/pjkundert/ezpwd-reed-solomon which
|
||||
* seems to be the fastest open-source decoder.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef REEDSOLOMON_H
|
||||
#define REEDSOLOMON_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
// Preprocessor defines available:
|
||||
//
|
||||
// EZPWD_NO_MOD_TAB -- define to force no "modnn" Galois modulo table acceleration
|
||||
//
|
||||
//#define EZPWD_NO_MOD_TAB
|
||||
|
||||
namespace ReedSolomon {
|
||||
|
||||
//
|
||||
// reed_solomon_base - Reed-Solomon codec generic base class
|
||||
//
|
||||
class reed_solomon_base {
|
||||
public:
|
||||
virtual size_t datum() const = 0; // a data element's bits
|
||||
virtual size_t symbol() const = 0; // a symbol's bits
|
||||
virtual int size() const = 0; // R-S block size (maximum total symbols)
|
||||
virtual int nroots() const = 0; // R-S roots (parity symbols)
|
||||
virtual int load() const = 0; // R-S net payload (data symbols)
|
||||
|
||||
virtual ~reed_solomon_base() {}
|
||||
|
||||
reed_solomon_base() {}
|
||||
|
||||
//
|
||||
// {en,de}code -- Compute/Correct errors/erasures in a Reed-Solomon encoded container
|
||||
//
|
||||
/// For decode, optionally specify some known erasure positions (up to nroots()). If
|
||||
/// non-empty 'erasures' is provided, it contains the positions of each erasure. If a
|
||||
/// non-zero pointer to a 'position' vector is provided, its capacity will be increased to
|
||||
/// be capable of storing up to 'nroots()' ints; the actual deduced error locations will be
|
||||
/// returned.
|
||||
///
|
||||
/// RETURN VALUE
|
||||
///
|
||||
/// Return -1 on error. The encode returns the number of parity symbols produced;
|
||||
/// decode returns the number of symbols corrected. Both errors and erasures are included,
|
||||
/// so long as they are actually different than the deduced value. In other words, if a
|
||||
/// symbol is marked as an erasure but it actually turns out to be correct, it's index will
|
||||
/// NOT be included in the returned count, nor the modified erasure vector!
|
||||
///
|
||||
|
||||
virtual int encode(const uint8_t *data, int len, uint8_t *parity) const = 0;
|
||||
|
||||
virtual int decode1(uint8_t *data, int len, uint8_t *parity,
|
||||
const std::vector<int> &erasure = std::vector<int>(), std::vector<int> *position = 0) const = 0;
|
||||
|
||||
int decode(uint8_t *data,
|
||||
int len,
|
||||
int pad = 0, // ignore 'pad' symbols at start of array
|
||||
const std::vector<int> &erasure = std::vector<int>(),
|
||||
std::vector<int> *position = 0) const
|
||||
{
|
||||
return decode1((uint8_t*)(data + pad), len, (uint8_t*)(data + len), erasure, position);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// gfpoly - default field polynomial generator functor.
|
||||
//
|
||||
template <int PLY>
|
||||
struct gfpoly {
|
||||
int operator()(int sr) const
|
||||
{
|
||||
if (sr == 0) {
|
||||
sr = 1;
|
||||
} else {
|
||||
sr <<= 1;
|
||||
if (sr & (1 << 8))
|
||||
sr ^= PLY;
|
||||
sr &= ((1 << 8) - 1);
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// class reed_solomon_tabs -- R-S tables common to all RS(NN,*) with same SYM, PRM and PLY
|
||||
//
|
||||
template <int PRM, class PLY>
|
||||
class reed_solomon_tabs : public reed_solomon_base {
|
||||
public:
|
||||
typedef uint8_t symbol_t;
|
||||
static const size_t DATUM = 8; // bits
|
||||
static const size_t SYMBOL = 8; // bits / symbol
|
||||
static const int MM = 8;
|
||||
static const int SIZE = (1 << 8) - 1; // maximum symbols in field
|
||||
static const int NN = SIZE;
|
||||
static const int A0 = SIZE;
|
||||
static const int MODS // modulo table: 1/2 the symbol size squared, up to 4k
|
||||
#if defined(EZPWD_NO_MOD_TAB)
|
||||
= 0;
|
||||
#else
|
||||
= 8 > 8 ? (1 << 12) : (1 << 8 << 8 / 2);
|
||||
#endif
|
||||
|
||||
static int iprim; // initialized to -1, below
|
||||
|
||||
protected:
|
||||
static std::array<uint8_t, NN + 1> alpha_to;
|
||||
static std::array<uint8_t, NN + 1> index_of;
|
||||
static std::array<uint8_t, MODS> mod_of;
|
||||
virtual ~reed_solomon_tabs() {}
|
||||
|
||||
reed_solomon_tabs() : reed_solomon_base()
|
||||
{
|
||||
// Do init if not already done. We check one value which is initialized to -1; this is
|
||||
// safe, 'cause the value will not be set 'til the initializing thread has completely
|
||||
// initialized the structure. Worst case scenario: multiple threads will initialize
|
||||
// identically. No mutex necessary.
|
||||
if (iprim >= 0)
|
||||
return;
|
||||
|
||||
// Generate Galois field lookup tables
|
||||
index_of[0] = A0; // log(zero) = -inf
|
||||
alpha_to[A0] = 0; // alpha**-inf = 0
|
||||
PLY poly;
|
||||
int sr = poly(0);
|
||||
for (int i = 0; i < NN; i++) {
|
||||
index_of[sr] = i;
|
||||
alpha_to[i] = sr;
|
||||
sr = poly(sr);
|
||||
}
|
||||
// If it's not primitive, raise exception or abort
|
||||
if (sr != alpha_to[0]) {
|
||||
abort();
|
||||
}
|
||||
|
||||
// Generate modulo table for some commonly used (non-trivial) values
|
||||
for (int x = NN; x < NN + MODS; ++x)
|
||||
mod_of[x - NN] = _modnn(x);
|
||||
// Find prim-th root of 1, index form, used in decoding.
|
||||
int iptmp = 1;
|
||||
while (iptmp % PRM != 0)
|
||||
iptmp += NN;
|
||||
iprim = iptmp / PRM;
|
||||
}
|
||||
|
||||
//
|
||||
// modnn -- modulo replacement for galois field arithmetics, optionally w/ table acceleration
|
||||
//
|
||||
// @x: the value to reduce (will never be -'ve)
|
||||
//
|
||||
// where
|
||||
// MM = number of bits per symbol
|
||||
// NN = (2^MM) - 1
|
||||
//
|
||||
// Simple arithmetic modulo would return a wrong result for values >= 3 * NN
|
||||
//
|
||||
uint8_t _modnn(int x) const
|
||||
{
|
||||
while (x >= NN) {
|
||||
x -= NN;
|
||||
x = (x >> MM) + (x & NN);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
uint8_t modnn(int x) const
|
||||
{
|
||||
while (x >= NN + MODS) {
|
||||
x -= NN;
|
||||
x = (x >> MM) + (x & NN);
|
||||
}
|
||||
if (MODS && x >= NN)
|
||||
x = mod_of[x - NN];
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// class reed_solomon - Reed-Solomon codec
|
||||
//
|
||||
// @TYP: A symbol datum; {en,de}code operates on arrays of these
|
||||
// @DATUM: Bits per datum (a TYP())
|
||||
// @SYM{BOL}, MM: Bits per symbol
|
||||
// @NN: Symbols per block (== (1<<MM)-1)
|
||||
// @alpha_to: log lookup table
|
||||
// @index_of: Antilog lookup table
|
||||
// @genpoly: Generator polynomial
|
||||
// @NROOTS: Number of generator roots = number of parity symbols
|
||||
// @FCR: First consecutive root, index form
|
||||
// @PRM: Primitive element, index form
|
||||
// @iprim: prim-th root of 1, index form
|
||||
// @PLY: The primitive generator polynominal functor
|
||||
//
|
||||
// All reed_solomon<T, ...> instances with the same template type parameters share a common
|
||||
// (static) set of alpha_to, index_of and genpoly tables. The first instance to be constructed
|
||||
// initializes the tables.
|
||||
//
|
||||
// Each specialized type of reed_solomon implements a specific encode/decode method
|
||||
// appropriate to its datum 'TYP'. When accessed via a generic reed_solomon_base pointer, only
|
||||
// access via "safe" (size specifying) containers or iterators is available.
|
||||
//
|
||||
template <int RTS, int FCR, int PRM, class PLY>
|
||||
class reed_solomon : public reed_solomon_tabs<PRM, PLY> {
|
||||
public:
|
||||
typedef reed_solomon_tabs<PRM, PLY> tabs_t;
|
||||
using tabs_t::A0;
|
||||
using tabs_t::DATUM;
|
||||
using tabs_t::MM;
|
||||
using tabs_t::NN;
|
||||
using tabs_t::SIZE;
|
||||
using tabs_t::SYMBOL;
|
||||
|
||||
using tabs_t::iprim;
|
||||
|
||||
using tabs_t::alpha_to;
|
||||
using tabs_t::index_of;
|
||||
|
||||
using tabs_t::modnn;
|
||||
|
||||
static const int NROOTS = RTS;
|
||||
static const int LOAD = SIZE - NROOTS; // maximum non-parity symbol payload
|
||||
|
||||
protected:
|
||||
static std::array<uint8_t, NROOTS + 1> genpoly;
|
||||
|
||||
public:
|
||||
virtual size_t datum() const { return DATUM; }
|
||||
|
||||
virtual size_t symbol() const { return SYMBOL; }
|
||||
|
||||
virtual int size() const { return SIZE; }
|
||||
|
||||
virtual int nroots() const { return NROOTS; }
|
||||
|
||||
virtual int load() const { return LOAD; }
|
||||
|
||||
using reed_solomon_base::decode;
|
||||
virtual int decode1(uint8_t *data, int len, uint8_t *parity,
|
||||
const std::vector<int> &erasure = std::vector<int>(), std::vector<int> *position = 0) const
|
||||
{
|
||||
return decode_mask(data, len, parity, erasure, position);
|
||||
}
|
||||
|
||||
//
|
||||
// decode_mask -- mask INP data into valid SYMBOL data
|
||||
//
|
||||
/// Incoming data may be in a variety of sizes, and may contain information beyond the
|
||||
/// R-S symbol capacity. For example, we might use a 6-bit R-S symbol to correct the lower
|
||||
/// 6 bits of an 8-bit data character. This would allow us to correct common substitution
|
||||
/// errors (such as '2' for '3', 'R' for 'T', 'n' for 'm').
|
||||
///
|
||||
int decode_mask(uint8_t *data, int len,
|
||||
uint8_t *parity = 0, // either 0, or pointer to all parity symbols
|
||||
const std::vector<int> &erasure = std::vector<int>(), std::vector<int> *position = 0) const
|
||||
{
|
||||
if (!parity) {
|
||||
len -= NROOTS;
|
||||
parity = data + len;
|
||||
}
|
||||
|
||||
std::array<uint8_t, SIZE> tmp;
|
||||
uint8_t msk = static_cast<uint8_t>(~0UL << SYMBOL);
|
||||
|
||||
|
||||
int corrects;
|
||||
if (!erasure.size() && !position) {
|
||||
// No erasures, and error position info not wanted.
|
||||
corrects = decode(data, len, parity);
|
||||
} else {
|
||||
// Either erasure location info specified, or resultant error position info wanted;
|
||||
// Prepare pos (a temporary, if no position vector provided), and copy any provided
|
||||
// erasure positions. After number of corrections is known, resize the position
|
||||
// vector. Thus, we use any supplied erasure info, and optionally return any
|
||||
// correction position info separately.
|
||||
std::vector<int> _pos;
|
||||
std::vector<int> &pos = position ? *position : _pos;
|
||||
pos.resize(std::max(size_t(NROOTS), erasure.size()));
|
||||
std::copy(erasure.begin(), erasure.end(), pos.begin());
|
||||
corrects = decode(data, len, parity, &pos.front(), erasure.size());
|
||||
if (corrects > int(pos.size())) {
|
||||
return -1;
|
||||
}
|
||||
pos.resize(std::max(0, corrects));
|
||||
}
|
||||
|
||||
return corrects;
|
||||
}
|
||||
|
||||
virtual ~reed_solomon()
|
||||
{
|
||||
}
|
||||
|
||||
reed_solomon() : reed_solomon_tabs<PRM, PLY>()
|
||||
{
|
||||
// We check one element of the array; this is safe, 'cause the value will not be
|
||||
// initialized 'til the initializing thread has completely initialized the array. Worst
|
||||
// case scenario: multiple threads will initialize identically. No mutex necessary.
|
||||
if (genpoly[0])
|
||||
return;
|
||||
|
||||
std::array<uint8_t, NROOTS + 1> tmppoly; // uninitialized
|
||||
// Form RS code generator polynomial from its roots. Only lower-index entries are
|
||||
// consulted, when computing subsequent entries; only index 0 needs initialization.
|
||||
tmppoly[0] = 1;
|
||||
for (int i = 0, root = FCR * PRM; i < NROOTS; i++, root += PRM) {
|
||||
tmppoly[i + 1] = 1;
|
||||
// Multiply tmppoly[] by @**(root + x)
|
||||
for (int j = i; j > 0; j--) {
|
||||
if (tmppoly[j] != 0)
|
||||
tmppoly[j] = tmppoly[j - 1] ^ alpha_to[modnn(index_of[tmppoly[j]] + root)];
|
||||
else
|
||||
tmppoly[j] = tmppoly[j - 1];
|
||||
}
|
||||
// tmppoly[0] can never be zero
|
||||
tmppoly[0] = alpha_to[modnn(index_of[tmppoly[0]] + root)];
|
||||
}
|
||||
// convert NROOTS entries of tmppoly[] to genpoly[] in index form for quicker encoding,
|
||||
// in reverse order so genpoly[0] is last element initialized.
|
||||
for (int i = NROOTS; i >= 0; --i)
|
||||
genpoly[i] = index_of[tmppoly[i]];
|
||||
}
|
||||
|
||||
virtual int encode(const uint8_t *data, int len, uint8_t *parity) // at least nroots
|
||||
const
|
||||
{
|
||||
// Check length parameter for validity
|
||||
int pad = NN - NROOTS - len;
|
||||
for (int i = 0; i < NROOTS; i++) parity[i] = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
uint8_t feedback = index_of[data[i] ^ parity[0]];
|
||||
if (feedback != A0) {
|
||||
for (int j = 1; j < NROOTS; j++)
|
||||
parity[j] ^= alpha_to[modnn(feedback + genpoly[NROOTS - j])];
|
||||
}
|
||||
|
||||
std::rotate(parity, parity + 1, parity + NROOTS);
|
||||
if (feedback != A0)
|
||||
parity[NROOTS - 1] = alpha_to[modnn(feedback + genpoly[0])];
|
||||
else
|
||||
parity[NROOTS - 1] = 0;
|
||||
}
|
||||
return NROOTS;
|
||||
}
|
||||
|
||||
int decode(uint8_t *data, int len,
|
||||
uint8_t *parity, // Requires: at least NROOTS
|
||||
int *eras_pos = 0, // Capacity: at least NROOTS
|
||||
int no_eras = 0, // Maximum: at most NROOTS
|
||||
uint8_t *corr = 0) // Capacity: at least NROOTS
|
||||
const
|
||||
{
|
||||
typedef std::array<uint8_t, NROOTS> typ_nroots;
|
||||
typedef std::array<uint8_t, NROOTS + 1> typ_nroots_1;
|
||||
typedef std::array<int, NROOTS> int_nroots;
|
||||
|
||||
typ_nroots_1 lambda{{0}};
|
||||
typ_nroots syn;
|
||||
typ_nroots_1 b;
|
||||
typ_nroots_1 t;
|
||||
typ_nroots_1 omega;
|
||||
int_nroots root;
|
||||
typ_nroots_1 reg;
|
||||
int_nroots loc;
|
||||
int count = 0;
|
||||
|
||||
// Check length parameter and erasures for validity
|
||||
int pad = NN - NROOTS - len;
|
||||
if (no_eras) {
|
||||
if (no_eras > NROOTS) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < no_eras; ++i) {
|
||||
if (eras_pos[i] < 0 || eras_pos[i] >= len + NROOTS) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// form the syndromes; i.e., evaluate data(x) at roots of g(x)
|
||||
for (int i = 0; i < NROOTS; i++)
|
||||
syn[i] = data[0];
|
||||
|
||||
for (int j = 1; j < len; j++) {
|
||||
for (int i = 0; i < NROOTS; i++) {
|
||||
if (syn[i] == 0) {
|
||||
syn[i] = data[j];
|
||||
} else {
|
||||
syn[i] = data[j] ^ alpha_to[modnn(index_of[syn[i]] + (FCR + i) * PRM)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < NROOTS; j++) {
|
||||
for (int i = 0; i < NROOTS; i++) {
|
||||
if (syn[i] == 0) {
|
||||
syn[i] = parity[j];
|
||||
} else {
|
||||
syn[i] = parity[j] ^ alpha_to[modnn(index_of[syn[i]] + (FCR + i) * PRM)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert syndromes to index form, checking for nonzero condition
|
||||
uint8_t syn_error = 0;
|
||||
for (int i = 0; i < NROOTS; i++) {
|
||||
syn_error |= syn[i];
|
||||
syn[i] = index_of[syn[i]];
|
||||
}
|
||||
|
||||
int deg_lambda = 0;
|
||||
int deg_omega = 0;
|
||||
int r = no_eras;
|
||||
int el = no_eras;
|
||||
if (!syn_error) {
|
||||
// if syndrome is zero, data[] is a codeword and there are no errors to correct.
|
||||
count = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
lambda[0] = 1;
|
||||
if (no_eras > 0) {
|
||||
// Init lambda to be the erasure locator polynomial. Convert erasure positions
|
||||
// from index into data, to index into Reed-Solomon block.
|
||||
lambda[1] = alpha_to[modnn(PRM * (NN - 1 - (eras_pos[0] + pad)))];
|
||||
for (int i = 1; i < no_eras; i++) {
|
||||
uint8_t u = modnn(PRM * (NN - 1 - (eras_pos[i] + pad)));
|
||||
for (int j = i + 1; j > 0; j--) {
|
||||
uint8_t tmp = index_of[lambda[j - 1]];
|
||||
if (tmp != A0) {
|
||||
lambda[j] ^= alpha_to[modnn(u + tmp)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NROOTS + 1; i++)
|
||||
b[i] = index_of[lambda[i]];
|
||||
|
||||
//
|
||||
// Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial
|
||||
//
|
||||
while (++r <= NROOTS) { // r is the step number
|
||||
// Compute discrepancy at the r-th step in poly-form
|
||||
uint8_t discr_r = 0;
|
||||
for (int i = 0; i < r; i++) {
|
||||
if ((lambda[i] != 0) && (syn[r - i - 1] != A0)) {
|
||||
discr_r ^= alpha_to[modnn(index_of[lambda[i]] + syn[r - i - 1])];
|
||||
}
|
||||
}
|
||||
discr_r = index_of[discr_r]; // Index form
|
||||
if (discr_r == A0) {
|
||||
// 2 lines below: B(x) <-- x*B(x)
|
||||
// Rotate the last element of b[NROOTS+1] to b[0]
|
||||
std::rotate(b.begin(), b.begin() + NROOTS, b.end());
|
||||
b[0] = A0;
|
||||
} else {
|
||||
// 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x)
|
||||
t[0] = lambda[0];
|
||||
for (int i = 0; i < NROOTS; i++) {
|
||||
if (b[i] != A0) {
|
||||
t[i + 1] = lambda[i + 1] ^ alpha_to[modnn(discr_r + b[i])];
|
||||
} else
|
||||
t[i + 1] = lambda[i + 1];
|
||||
}
|
||||
if (2 * el <= r + no_eras - 1) {
|
||||
el = r + no_eras - el;
|
||||
// 2 lines below: B(x) <-- inv(discr_r) * lambda(x)
|
||||
for (int i = 0; i <= NROOTS; i++) {
|
||||
b[i] = ((lambda[i] == 0) ? A0 : modnn(index_of[lambda[i]] - discr_r + NN));
|
||||
}
|
||||
} else {
|
||||
// 2 lines below: B(x) <-- x*B(x)
|
||||
std::rotate(b.begin(), b.begin() + NROOTS, b.end());
|
||||
b[0] = A0;
|
||||
}
|
||||
lambda = t;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert lambda to index form and compute deg(lambda(x))
|
||||
for (int i = 0; i < NROOTS + 1; i++) {
|
||||
lambda[i] = index_of[lambda[i]];
|
||||
if (lambda[i] != NN)
|
||||
deg_lambda = i;
|
||||
}
|
||||
// Find roots of error+erasure locator polynomial by Chien search
|
||||
reg = lambda;
|
||||
count = 0; // Number of roots of lambda(x)
|
||||
for (int i = 1, k = iprim - 1; i <= NN; i++, k = modnn(k + iprim)) {
|
||||
uint8_t q = 1; // lambda[0] is always 0
|
||||
for (int j = deg_lambda; j > 0; j--) {
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = modnn(reg[j] + j);
|
||||
q ^= alpha_to[reg[j]];
|
||||
}
|
||||
}
|
||||
if (q != 0)
|
||||
continue; // Not a root
|
||||
// store root (index-form) and error location number
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
// If we've already found max possible roots, abort the search to save time
|
||||
if (++count == deg_lambda)
|
||||
break;
|
||||
}
|
||||
if (deg_lambda != count) {
|
||||
// deg(lambda) unequal to number of roots => uncorrectable error detected
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
//
|
||||
// Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo x**NROOTS). in
|
||||
// index form. Also find deg(omega).
|
||||
//
|
||||
deg_omega = deg_lambda - 1;
|
||||
for (int i = 0; i <= deg_omega; i++) {
|
||||
uint8_t tmp = 0;
|
||||
for (int j = i; j >= 0; j--) {
|
||||
if ((syn[i - j] != A0) && (lambda[j] != A0))
|
||||
tmp ^= alpha_to[modnn(syn[i - j] + lambda[j])];
|
||||
}
|
||||
omega[i] = index_of[tmp];
|
||||
}
|
||||
|
||||
//
|
||||
// Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = inv(X(l))**(fcr-1)
|
||||
// and den = lambda_pr(inv(X(l))) all in poly-form
|
||||
//
|
||||
for (int j = count - 1; j >= 0; j--) {
|
||||
uint8_t num1 = 0;
|
||||
for (int i = deg_omega; i >= 0; i--) {
|
||||
if (omega[i] != A0)
|
||||
num1 ^= alpha_to[modnn(omega[i] + i * root[j])];
|
||||
}
|
||||
uint8_t num2 = alpha_to[modnn(root[j] * (FCR - 1) + NN)];
|
||||
uint8_t den = 0;
|
||||
|
||||
// lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i]
|
||||
for (int i = std::min(deg_lambda, NROOTS - 1) & ~1; i >= 0; i -= 2) {
|
||||
if (lambda[i + 1] != A0) {
|
||||
den ^= alpha_to[modnn(lambda[i + 1] + i * root[j])];
|
||||
}
|
||||
}
|
||||
// Apply error to data. Padding ('pad' unused symbols) begin at index 0.
|
||||
if (num1 != 0) {
|
||||
if (loc[j] < pad) {
|
||||
// If the computed error position is in the 'pad' (the unused portion of the
|
||||
// R-S data capacity), then our solution has failed -- we've computed a
|
||||
// correction location outside of the data and parity we've been provided!
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
uint8_t cor = alpha_to[modnn(index_of[num1] + index_of[num2] + NN - index_of[den])];
|
||||
// Store the error correction pattern, if a correction buffer is available
|
||||
if (corr)
|
||||
corr[j] = cor;
|
||||
// If a data/parity buffer is given and the error is inside the message or
|
||||
// parity data, correct it
|
||||
if (loc[j] < (NN - NROOTS)) {
|
||||
if (data) {
|
||||
data[loc[j] - pad] ^= cor;
|
||||
}
|
||||
} else if (loc[j] < NN) {
|
||||
if (parity)
|
||||
parity[loc[j] - (NN - NROOTS)] ^= cor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
if (eras_pos != NULL) {
|
||||
for (int i = 0; i < count; i++)
|
||||
eras_pos[i] = loc[i] - pad;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Define the static reed_solomon...<...> members; allowed in header for template types.
|
||||
//
|
||||
// The reed_solomon_tags<...>::iprim < 0 is used to indicate to the first instance that the
|
||||
// static tables require initialization.
|
||||
//
|
||||
template <int PRM, class PLY>
|
||||
int reed_solomon_tabs<PRM, PLY>::iprim = -1;
|
||||
|
||||
template <int PRM, class PLY>
|
||||
std::array<uint8_t, reed_solomon_tabs<PRM, PLY>::NN + 1> reed_solomon_tabs<PRM, PLY>::alpha_to;
|
||||
|
||||
template <int PRM, class PLY>
|
||||
std::array<uint8_t, reed_solomon_tabs<PRM, PLY>::NN + 1> reed_solomon_tabs<PRM, PLY>::index_of;
|
||||
template <int PRM, class PLY>
|
||||
std::array<uint8_t, reed_solomon_tabs<PRM, PLY>::MODS> reed_solomon_tabs<PRM, PLY>::mod_of;
|
||||
|
||||
template <int RTS, int FCR, int PRM, class PLY>
|
||||
std::array<uint8_t, reed_solomon<RTS, FCR, PRM, PLY>::NROOTS + 1> reed_solomon<RTS, FCR, PRM, PLY>::genpoly;
|
||||
|
||||
//
|
||||
// RS( ... ) -- Define a reed-solomon codec
|
||||
//
|
||||
// @SYMBOLS: Total number of symbols; must be a power of 2 minus 1, eg 2^8-1 == 255
|
||||
// @PAYLOAD: The maximum number of non-parity symbols, eg 253 ==> 2 parity symbols
|
||||
// @POLY: A primitive polynomial appropriate to the SYMBOLS size
|
||||
// @FCR: The first consecutive root of the Reed-Solomon generator polynomial
|
||||
// @PRIM: The primitive root of the generator polynomial
|
||||
//
|
||||
|
||||
//
|
||||
// RS<SYMBOLS, PAYLOAD> -- Standard partial specializations for Reed-Solomon codec type access
|
||||
//
|
||||
// Normally, Reed-Solomon codecs are described with terms like RS(255,252). Obtain various
|
||||
// standard Reed-Solomon codecs using macros of a similar form, eg. RS<255, 252>. Standard PLY,
|
||||
// FCR and PRM values are provided for various SYMBOL sizes, along with appropriate basic types
|
||||
// capable of holding all internal Reed-Solomon tabular data.
|
||||
//
|
||||
// In order to provide "default initialization" of const RS<...> types, a user-provided
|
||||
// default constructor must be provided.
|
||||
//
|
||||
template <size_t SYMBOLS, size_t PAYLOAD>
|
||||
struct RS;
|
||||
template <size_t PAYLOAD>
|
||||
struct RS<255, PAYLOAD> : public ReedSolomon::reed_solomon<(255) - (PAYLOAD), 0, 1, ReedSolomon::gfpoly<0x11d>>
|
||||
{
|
||||
RS()
|
||||
: ReedSolomon::reed_solomon<(255) - (PAYLOAD), 0, 1, ReedSolomon::gfpoly<0x11d>>()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ReedSolomon
|
||||
|
||||
#endif // REEDSOLOMON_H
|
@ -4317,6 +4317,11 @@ bool WebAPIRequestMapper::getChannelSettings(
|
||||
channelSettings->setRadioClockSettings(new SWGSDRangel::SWGRadioClockSettings());
|
||||
channelSettings->getRadioClockSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (channelSettingsKey == "RadiosondeDemodSettings")
|
||||
{
|
||||
channelSettings->setRadiosondeDemodSettings(new SWGSDRangel::SWGRadiosondeDemodSettings());
|
||||
channelSettings->getRadiosondeDemodSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (channelSettingsKey == "RemoteSinkSettings")
|
||||
{
|
||||
channelSettings->setRemoteSinkSettings(new SWGSDRangel::SWGRemoteSinkSettings());
|
||||
@ -4840,6 +4845,11 @@ bool WebAPIRequestMapper::getFeatureSettings(
|
||||
featureSettings->setStarTrackerSettings(new SWGSDRangel::SWGStarTrackerSettings());
|
||||
featureSettings->getStarTrackerSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (featureSettingsKey == "RadiosondeSettings")
|
||||
{
|
||||
featureSettings->setRadiosondeSettings(new SWGSDRangel::SWGRadiosondeSettings());
|
||||
featureSettings->getRadiosondeSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (featureSettingsKey == "RigCtlServerSettings")
|
||||
{
|
||||
featureSettings->setRigCtlServerSettings(new SWGSDRangel::SWGRigCtlServerSettings());
|
||||
@ -5060,6 +5070,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
|
||||
channelSettings.setPagerDemodSettings(nullptr);
|
||||
channelSettings.setRadioAstronomySettings(nullptr);
|
||||
channelSettings.setRadioClockSettings(nullptr);
|
||||
channelSettings.setRadiosondeDemodSettings(nullptr);
|
||||
channelSettings.setRemoteSinkSettings(nullptr);
|
||||
channelSettings.setRemoteSourceSettings(nullptr);
|
||||
channelSettings.setSsbDemodSettings(nullptr);
|
||||
@ -5091,6 +5102,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
|
||||
channelReport.setPacketModReport(nullptr);
|
||||
channelReport.setRadioAstronomyReport(nullptr);
|
||||
channelReport.setRadioClockReport(nullptr);
|
||||
channelReport.setRadiosondeDemodReport(nullptr);
|
||||
channelReport.setRemoteSourceReport(nullptr);
|
||||
channelReport.setSsbDemodReport(nullptr);
|
||||
channelReport.setSsbModReport(nullptr);
|
||||
@ -5130,6 +5142,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
|
||||
{
|
||||
featureSettings.cleanup();
|
||||
featureSettings.setFeatureType(nullptr);
|
||||
featureSettings.setAisSettings(nullptr);
|
||||
featureSettings.setAntennaToolsSettings(nullptr);
|
||||
featureSettings.setAprsSettings(nullptr);
|
||||
featureSettings.setGs232ControllerSettings(nullptr);
|
||||
@ -5138,6 +5151,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
|
||||
featureSettings.setSatelliteTrackerSettings(nullptr);
|
||||
featureSettings.setSimplePttSettings(nullptr);
|
||||
featureSettings.setStarTrackerSettings(nullptr);
|
||||
featureSettings.setRadiosondeSettings(nullptr);
|
||||
featureSettings.setRigCtlServerSettings(nullptr);
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
|
||||
{"sdrangel.channeltx.modpacket", "PacketModSettings"},
|
||||
{"sdrangel.channeltx.mod802.15.4", "IEEE_802_15_4_ModSettings"},
|
||||
{"sdrangel.channel.radioclock", "RadioClockSettings"},
|
||||
{"sdrangel.channel.radiosondedemod", "RadiosondeDemodSettings"},
|
||||
{"sdrangel.demod.remotesink", "RemoteSinkSettings"},
|
||||
{"sdrangel.channeltx.remotesource", "RemoteSourceSettings"},
|
||||
{"sdrangel.channeltx.modssb", "SSBModSettings"},
|
||||
@ -159,6 +160,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
|
||||
{"LocalSource", "LocalSourceSettings"},
|
||||
{"RadioAstronomy", "RadioAstronomySettings"},
|
||||
{"RadioClock", "RadioClockSettings"},
|
||||
{"RadiosondeDemod", "RadiosondeDemodSettings"},
|
||||
{"RemoteSink", "RemoteSinkSettings"},
|
||||
{"RemoteSource", "RemoteSourceSettings"},
|
||||
{"SSBMod", "SSBModSettings"},
|
||||
@ -273,6 +275,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToSettingsKey = {
|
||||
{"GS232Controller", "GS232ControllerSettings"}, // a.k.a Rotator Controller
|
||||
{"Map", "MapSettings"},
|
||||
{"PERTester", "PERTesterSettings"},
|
||||
{"Radiosonde", "RadiosondeSettings"},
|
||||
{"RigCtlServer", "RigCtlServerSettings"},
|
||||
{"SatelliteTracker", "SatelliteTrackerSettings"},
|
||||
{"SimplePTT", "SimplePTTSettings"},
|
||||
@ -302,6 +305,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureURIToSettingsKey = {
|
||||
{"sdrangel.feature.gs232controller", "GS232ControllerSettings"},
|
||||
{"sdrangel.feature.map", "MapSettings"},
|
||||
{"sdrangel.feature.pertester", "PERTesterSettings"},
|
||||
{"sdrangel.feature.radiosonde", "RadiosondeSettings"},
|
||||
{"sdrangel.feature.rigctlserver", "RigCtlServerSettings"},
|
||||
{"sdrangel.feature.satellitetracker", "SatelliteTrackerSettings"},
|
||||
{"sdrangel.feature.simpleptt", "SimplePTTSettings"},
|
||||
|
@ -25,6 +25,8 @@ set(sdrgui_SOURCES
|
||||
gui/crightclickenabler.cpp
|
||||
gui/customtextedit.cpp
|
||||
gui/cwkeyergui.cpp
|
||||
gui/datetimedelegate.cpp
|
||||
gui/decimaldelegate.cpp
|
||||
gui/devicestreamselectiondialog.cpp
|
||||
gui/deviceuserargsdialog.cpp
|
||||
gui/dmsspinbox.cpp
|
||||
@ -62,6 +64,7 @@ set(sdrgui_SOURCES
|
||||
gui/spectrumcalibrationpointsdialog.cpp
|
||||
gui/spectrummarkersdialog.cpp
|
||||
gui/tickedslider.cpp
|
||||
gui/timedelegate.cpp
|
||||
gui/transverterbutton.cpp
|
||||
gui/transverterdialog.cpp
|
||||
gui/tvscreen.cpp
|
||||
@ -113,6 +116,8 @@ set(sdrgui_HEADERS
|
||||
gui/crightclickenabler.h
|
||||
gui/customtextedit.h
|
||||
gui/cwkeyergui.h
|
||||
gui/datetimedelegate.h
|
||||
gui/decimaldelegate.h
|
||||
gui/devicestreamselectiondialog.h
|
||||
gui/deviceuserargsdialog.h
|
||||
gui/dmsspinbox.h
|
||||
@ -152,6 +157,7 @@ set(sdrgui_HEADERS
|
||||
gui/spectrumcalibrationpointsdialog.h
|
||||
gui/spectrummarkersdialog.h
|
||||
gui/tickedslider.h
|
||||
gui/timedelegate.h
|
||||
gui/transverterbutton.h
|
||||
gui/transverterdialog.h
|
||||
gui/tvscreen.h
|
||||
|
36
sdrgui/gui/datetimedelegate.cpp
Normal file
36
sdrgui/gui/datetimedelegate.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDateTime>
|
||||
|
||||
#include "datetimedelegate.h"
|
||||
|
||||
DateTimeDelegate::DateTimeDelegate(QString format) :
|
||||
m_format(format)
|
||||
{
|
||||
}
|
||||
|
||||
QString DateTimeDelegate::displayText(const QVariant &value, const QLocale &locale) const
|
||||
{
|
||||
(void) locale;
|
||||
if (value.toString() == "") {
|
||||
return "";
|
||||
} else {
|
||||
return value.toDateTime().toString(m_format);
|
||||
}
|
||||
}
|
||||
|
37
sdrgui/gui/datetimedelegate.h
Normal file
37
sdrgui/gui/datetimedelegate.h
Normal file
@ -0,0 +1,37 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 SDRGUI_GUI_DATETIMEDELGATE_H
|
||||
#define SDRGUI_GUI_DATETIMEDELGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
// Delegate for table to display time
|
||||
class SDRGUI_API DateTimeDelegate : public QStyledItemDelegate {
|
||||
|
||||
public:
|
||||
DateTimeDelegate(QString format = "yyyy/MM/dd hh:mm:ss");
|
||||
virtual QString displayText(const QVariant &value, const QLocale &locale) const override;
|
||||
|
||||
private:
|
||||
QString m_format;
|
||||
|
||||
};
|
||||
|
||||
#endif // SDRGUI_GUI_DATETIMEDELGATE_H
|
35
sdrgui/gui/decimaldelegate.cpp
Normal file
35
sdrgui/gui/decimaldelegate.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "decimaldelegate.h"
|
||||
|
||||
DecimalDelegate::DecimalDelegate(int precision) :
|
||||
m_precision(precision)
|
||||
{
|
||||
}
|
||||
|
||||
QString DecimalDelegate::displayText(const QVariant &value, const QLocale &locale) const
|
||||
{
|
||||
(void) locale;
|
||||
bool ok;
|
||||
double d = value.toDouble(&ok);
|
||||
if (ok) {
|
||||
return QString::number(d, 'f', m_precision);
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
38
sdrgui/gui/decimaldelegate.h
Normal file
38
sdrgui/gui/decimaldelegate.h
Normal file
@ -0,0 +1,38 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 SDRGUI_GUI_DECIMALDELGATE_H
|
||||
#define SDRGUI_GUI_DECIMALDELGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
// Deligate for table to control precision used to display floating point values - also supports strings
|
||||
class SDRGUI_API DecimalDelegate : public QStyledItemDelegate {
|
||||
|
||||
public:
|
||||
DecimalDelegate(int precision = 2);
|
||||
|
||||
virtual QString displayText(const QVariant &value, const QLocale &locale) const override;
|
||||
|
||||
private:
|
||||
int m_precision;
|
||||
|
||||
};
|
||||
|
||||
#endif // SDRGUI_GUI_DECIMALDELGATE_H
|
36
sdrgui/gui/timedelegate.cpp
Normal file
36
sdrgui/gui/timedelegate.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 "timedelegate.h"
|
||||
|
||||
TimeDelegate::TimeDelegate(QString format) :
|
||||
m_format(format)
|
||||
{
|
||||
}
|
||||
|
||||
QString TimeDelegate::displayText(const QVariant &value, const QLocale &locale) const
|
||||
{
|
||||
(void) locale;
|
||||
if (value.toString() == "") {
|
||||
return "";
|
||||
} else {
|
||||
return value.toTime().toString(m_format);
|
||||
}
|
||||
}
|
||||
|
37
sdrgui/gui/timedelegate.h
Normal file
37
sdrgui/gui/timedelegate.h
Normal file
@ -0,0 +1,37 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 SDRGUI_GUI_TIMEDELGATE_H
|
||||
#define SDRGUI_GUI_TIMEDELGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
// Delegate for table to display time
|
||||
class SDRGUI_API TimeDelegate : public QStyledItemDelegate {
|
||||
|
||||
public:
|
||||
TimeDelegate(QString format = "hh:mm:ss");
|
||||
virtual QString displayText(const QVariant &value, const QLocale &locale) const override;
|
||||
|
||||
private:
|
||||
QString m_format;
|
||||
|
||||
};
|
||||
|
||||
#endif // SDRGUI_GUI_DECIMALDELGATE_H
|
@ -59,6 +59,8 @@ ChannelReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioAstronomy.yaml#/RadioAstronomyReport"
|
||||
RadioClockReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioClock.yaml#/RadioClockReport"
|
||||
RadiosondeDemodReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadiosondeDemod.yaml#/RadiosondeDemodReport"
|
||||
RemoteSourceReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RemoteSource.yaml#/RemoteSourceReport"
|
||||
PacketDemodReport:
|
||||
|
@ -85,6 +85,8 @@ ChannelSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioAstronomy.yaml#/RadioAstronomySettings"
|
||||
RadioClockSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioClock.yaml#/RadioClockSettings"
|
||||
RadiosondeDemodSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadiosondeDemod.yaml#/RadiosondeDemodSettings"
|
||||
RemoteSinkSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RemoteSink.yaml#/RemoteSinkSettings"
|
||||
RemoteSourceSettings:
|
||||
|
@ -31,6 +31,8 @@ FeatureSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/Map.yaml#/MapSettings"
|
||||
PERTesterSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/PERTester.yaml#/PERTesterSettings"
|
||||
RadiosondeSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/Radiosonde.yaml#/RadiosondeSettings"
|
||||
RigCtlServerSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RigCtlServer.yaml#/RigCtlServerSettings"
|
||||
SatelliteTrackerSettings:
|
||||
|
20
swagger/sdrangel/api/swagger/include/Radiosonde.yaml
Normal file
20
swagger/sdrangel/api/swagger/include/Radiosonde.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
RadiosondeSettings:
|
||||
description: "Radiosonde settings"
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
rgbColor:
|
||||
type: integer
|
||||
useReverseAPI:
|
||||
description: Synchronize with reverse API (1 for yes, 0 for no)
|
||||
type: integer
|
||||
reverseAPIAddress:
|
||||
type: string
|
||||
reverseAPIPort:
|
||||
type: integer
|
||||
reverseAPIFeatureSetIndex:
|
||||
type: integer
|
||||
reverseAPIFeatureIndex:
|
||||
type: integer
|
||||
rollupState:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
|
65
swagger/sdrangel/api/swagger/include/RadiosondeDemod.yaml
Normal file
65
swagger/sdrangel/api/swagger/include/RadiosondeDemod.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
RadiosondeDemodSettings:
|
||||
description: RadiosondeDemod
|
||||
properties:
|
||||
baud:
|
||||
type: integer
|
||||
description: baud rate (nominal is 4800)
|
||||
inputFrequencyOffset:
|
||||
type: integer
|
||||
format: int64
|
||||
rfBandwidth:
|
||||
type: number
|
||||
format: float
|
||||
fmDeviation:
|
||||
type: number
|
||||
format: float
|
||||
correlationThreshold:
|
||||
type: number
|
||||
format: float
|
||||
udpEnabled:
|
||||
description: "Whether to forward received messages to specified UDP port"
|
||||
type: integer
|
||||
udpAddress:
|
||||
description: "UDP address to forward received messages to"
|
||||
type: string
|
||||
udpPort:
|
||||
description: "UDP port to forward received messages to"
|
||||
type: integer
|
||||
logFilename:
|
||||
type: string
|
||||
logEnabled:
|
||||
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
|
||||
scopeConfig:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope"
|
||||
channelMarker:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
|
||||
rollupState:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
|
||||
|
||||
RadiosondeDemodReport:
|
||||
description: RadiosondeDemod
|
||||
properties:
|
||||
channelPowerDB:
|
||||
description: power received in channel (dB)
|
||||
type: number
|
||||
format: float
|
||||
channelSampleRate:
|
||||
type: integer
|
@ -80,6 +80,8 @@ SWGChannelReport::SWGChannelReport() {
|
||||
m_radio_astronomy_report_isSet = false;
|
||||
radio_clock_report = nullptr;
|
||||
m_radio_clock_report_isSet = false;
|
||||
radiosonde_demod_report = nullptr;
|
||||
m_radiosonde_demod_report_isSet = false;
|
||||
remote_source_report = nullptr;
|
||||
m_remote_source_report_isSet = false;
|
||||
packet_demod_report = nullptr;
|
||||
@ -164,6 +166,8 @@ SWGChannelReport::init() {
|
||||
m_radio_astronomy_report_isSet = false;
|
||||
radio_clock_report = new SWGRadioClockReport();
|
||||
m_radio_clock_report_isSet = false;
|
||||
radiosonde_demod_report = new SWGRadiosondeDemodReport();
|
||||
m_radiosonde_demod_report_isSet = false;
|
||||
remote_source_report = new SWGRemoteSourceReport();
|
||||
m_remote_source_report_isSet = false;
|
||||
packet_demod_report = new SWGPacketDemodReport();
|
||||
@ -268,6 +272,9 @@ SWGChannelReport::cleanup() {
|
||||
if(radio_clock_report != nullptr) {
|
||||
delete radio_clock_report;
|
||||
}
|
||||
if(radiosonde_demod_report != nullptr) {
|
||||
delete radiosonde_demod_report;
|
||||
}
|
||||
if(remote_source_report != nullptr) {
|
||||
delete remote_source_report;
|
||||
}
|
||||
@ -369,6 +376,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&radio_clock_report, pJson["RadioClockReport"], "SWGRadioClockReport", "SWGRadioClockReport");
|
||||
|
||||
::SWGSDRangel::setValue(&radiosonde_demod_report, pJson["RadiosondeDemodReport"], "SWGRadiosondeDemodReport", "SWGRadiosondeDemodReport");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_source_report, pJson["RemoteSourceReport"], "SWGRemoteSourceReport", "SWGRemoteSourceReport");
|
||||
|
||||
::SWGSDRangel::setValue(&packet_demod_report, pJson["PacketDemodReport"], "SWGPacketDemodReport", "SWGPacketDemodReport");
|
||||
@ -487,6 +496,9 @@ SWGChannelReport::asJsonObject() {
|
||||
if((radio_clock_report != nullptr) && (radio_clock_report->isSet())){
|
||||
toJsonValue(QString("RadioClockReport"), radio_clock_report, obj, QString("SWGRadioClockReport"));
|
||||
}
|
||||
if((radiosonde_demod_report != nullptr) && (radiosonde_demod_report->isSet())){
|
||||
toJsonValue(QString("RadiosondeDemodReport"), radiosonde_demod_report, obj, QString("SWGRadiosondeDemodReport"));
|
||||
}
|
||||
if((remote_source_report != nullptr) && (remote_source_report->isSet())){
|
||||
toJsonValue(QString("RemoteSourceReport"), remote_source_report, obj, QString("SWGRemoteSourceReport"));
|
||||
}
|
||||
@ -787,6 +799,16 @@ SWGChannelReport::setRadioClockReport(SWGRadioClockReport* radio_clock_report) {
|
||||
this->m_radio_clock_report_isSet = true;
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodReport*
|
||||
SWGChannelReport::getRadiosondeDemodReport() {
|
||||
return radiosonde_demod_report;
|
||||
}
|
||||
void
|
||||
SWGChannelReport::setRadiosondeDemodReport(SWGRadiosondeDemodReport* radiosonde_demod_report) {
|
||||
this->radiosonde_demod_report = radiosonde_demod_report;
|
||||
this->m_radiosonde_demod_report_isSet = true;
|
||||
}
|
||||
|
||||
SWGRemoteSourceReport*
|
||||
SWGChannelReport::getRemoteSourceReport() {
|
||||
return remote_source_report;
|
||||
@ -990,6 +1012,9 @@ SWGChannelReport::isSet(){
|
||||
if(radio_clock_report && radio_clock_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(radiosonde_demod_report && radiosonde_demod_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(remote_source_report && remote_source_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "SWGPagerDemodReport.h"
|
||||
#include "SWGRadioAstronomyReport.h"
|
||||
#include "SWGRadioClockReport.h"
|
||||
#include "SWGRadiosondeDemodReport.h"
|
||||
#include "SWGRemoteSourceReport.h"
|
||||
#include "SWGSSBDemodReport.h"
|
||||
#include "SWGSSBModReport.h"
|
||||
@ -156,6 +157,9 @@ public:
|
||||
SWGRadioClockReport* getRadioClockReport();
|
||||
void setRadioClockReport(SWGRadioClockReport* radio_clock_report);
|
||||
|
||||
SWGRadiosondeDemodReport* getRadiosondeDemodReport();
|
||||
void setRadiosondeDemodReport(SWGRadiosondeDemodReport* radiosonde_demod_report);
|
||||
|
||||
SWGRemoteSourceReport* getRemoteSourceReport();
|
||||
void setRemoteSourceReport(SWGRemoteSourceReport* remote_source_report);
|
||||
|
||||
@ -274,6 +278,9 @@ private:
|
||||
SWGRadioClockReport* radio_clock_report;
|
||||
bool m_radio_clock_report_isSet;
|
||||
|
||||
SWGRadiosondeDemodReport* radiosonde_demod_report;
|
||||
bool m_radiosonde_demod_report_isSet;
|
||||
|
||||
SWGRemoteSourceReport* remote_source_report;
|
||||
bool m_remote_source_report_isSet;
|
||||
|
||||
|
@ -104,6 +104,8 @@ SWGChannelSettings::SWGChannelSettings() {
|
||||
m_radio_astronomy_settings_isSet = false;
|
||||
radio_clock_settings = nullptr;
|
||||
m_radio_clock_settings_isSet = false;
|
||||
radiosonde_demod_settings = nullptr;
|
||||
m_radiosonde_demod_settings_isSet = false;
|
||||
remote_sink_settings = nullptr;
|
||||
m_remote_sink_settings_isSet = false;
|
||||
remote_source_settings = nullptr;
|
||||
@ -210,6 +212,8 @@ SWGChannelSettings::init() {
|
||||
m_radio_astronomy_settings_isSet = false;
|
||||
radio_clock_settings = new SWGRadioClockSettings();
|
||||
m_radio_clock_settings_isSet = false;
|
||||
radiosonde_demod_settings = new SWGRadiosondeDemodSettings();
|
||||
m_radiosonde_demod_settings_isSet = false;
|
||||
remote_sink_settings = new SWGRemoteSinkSettings();
|
||||
m_remote_sink_settings_isSet = false;
|
||||
remote_source_settings = new SWGRemoteSourceSettings();
|
||||
@ -344,6 +348,9 @@ SWGChannelSettings::cleanup() {
|
||||
if(radio_clock_settings != nullptr) {
|
||||
delete radio_clock_settings;
|
||||
}
|
||||
if(radiosonde_demod_settings != nullptr) {
|
||||
delete radiosonde_demod_settings;
|
||||
}
|
||||
if(remote_sink_settings != nullptr) {
|
||||
delete remote_sink_settings;
|
||||
}
|
||||
@ -466,6 +473,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&radio_clock_settings, pJson["RadioClockSettings"], "SWGRadioClockSettings", "SWGRadioClockSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&radiosonde_demod_settings, pJson["RadiosondeDemodSettings"], "SWGRadiosondeDemodSettings", "SWGRadiosondeDemodSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_sink_settings, pJson["RemoteSinkSettings"], "SWGRemoteSinkSettings", "SWGRemoteSinkSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_source_settings, pJson["RemoteSourceSettings"], "SWGRemoteSourceSettings", "SWGRemoteSourceSettings");
|
||||
@ -618,6 +627,9 @@ SWGChannelSettings::asJsonObject() {
|
||||
if((radio_clock_settings != nullptr) && (radio_clock_settings->isSet())){
|
||||
toJsonValue(QString("RadioClockSettings"), radio_clock_settings, obj, QString("SWGRadioClockSettings"));
|
||||
}
|
||||
if((radiosonde_demod_settings != nullptr) && (radiosonde_demod_settings->isSet())){
|
||||
toJsonValue(QString("RadiosondeDemodSettings"), radiosonde_demod_settings, obj, QString("SWGRadiosondeDemodSettings"));
|
||||
}
|
||||
if((remote_sink_settings != nullptr) && (remote_sink_settings->isSet())){
|
||||
toJsonValue(QString("RemoteSinkSettings"), remote_sink_settings, obj, QString("SWGRemoteSinkSettings"));
|
||||
}
|
||||
@ -1035,6 +1047,16 @@ SWGChannelSettings::setRadioClockSettings(SWGRadioClockSettings* radio_clock_set
|
||||
this->m_radio_clock_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodSettings*
|
||||
SWGChannelSettings::getRadiosondeDemodSettings() {
|
||||
return radiosonde_demod_settings;
|
||||
}
|
||||
void
|
||||
SWGChannelSettings::setRadiosondeDemodSettings(SWGRadiosondeDemodSettings* radiosonde_demod_settings) {
|
||||
this->radiosonde_demod_settings = radiosonde_demod_settings;
|
||||
this->m_radiosonde_demod_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRemoteSinkSettings*
|
||||
SWGChannelSettings::getRemoteSinkSettings() {
|
||||
return remote_sink_settings;
|
||||
@ -1264,6 +1286,9 @@ SWGChannelSettings::isSet(){
|
||||
if(radio_clock_settings && radio_clock_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(radiosonde_demod_settings && radiosonde_demod_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(remote_sink_settings && remote_sink_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "SWGPagerDemodSettings.h"
|
||||
#include "SWGRadioAstronomySettings.h"
|
||||
#include "SWGRadioClockSettings.h"
|
||||
#include "SWGRadiosondeDemodSettings.h"
|
||||
#include "SWGRemoteSinkSettings.h"
|
||||
#include "SWGRemoteSourceSettings.h"
|
||||
#include "SWGSSBDemodSettings.h"
|
||||
@ -201,6 +202,9 @@ public:
|
||||
SWGRadioClockSettings* getRadioClockSettings();
|
||||
void setRadioClockSettings(SWGRadioClockSettings* radio_clock_settings);
|
||||
|
||||
SWGRadiosondeDemodSettings* getRadiosondeDemodSettings();
|
||||
void setRadiosondeDemodSettings(SWGRadiosondeDemodSettings* radiosonde_demod_settings);
|
||||
|
||||
SWGRemoteSinkSettings* getRemoteSinkSettings();
|
||||
void setRemoteSinkSettings(SWGRemoteSinkSettings* remote_sink_settings);
|
||||
|
||||
@ -352,6 +356,9 @@ private:
|
||||
SWGRadioClockSettings* radio_clock_settings;
|
||||
bool m_radio_clock_settings_isSet;
|
||||
|
||||
SWGRadiosondeDemodSettings* radiosonde_demod_settings;
|
||||
bool m_radiosonde_demod_settings_isSet;
|
||||
|
||||
SWGRemoteSinkSettings* remote_sink_settings;
|
||||
bool m_remote_sink_settings_isSet;
|
||||
|
||||
|
@ -52,6 +52,8 @@ SWGFeatureSettings::SWGFeatureSettings() {
|
||||
m_map_settings_isSet = false;
|
||||
per_tester_settings = nullptr;
|
||||
m_per_tester_settings_isSet = false;
|
||||
radiosonde_settings = nullptr;
|
||||
m_radiosonde_settings_isSet = false;
|
||||
rig_ctl_server_settings = nullptr;
|
||||
m_rig_ctl_server_settings_isSet = false;
|
||||
satellite_tracker_settings = nullptr;
|
||||
@ -94,6 +96,8 @@ SWGFeatureSettings::init() {
|
||||
m_map_settings_isSet = false;
|
||||
per_tester_settings = new SWGPERTesterSettings();
|
||||
m_per_tester_settings_isSet = false;
|
||||
radiosonde_settings = new SWGRadiosondeSettings();
|
||||
m_radiosonde_settings_isSet = false;
|
||||
rig_ctl_server_settings = new SWGRigCtlServerSettings();
|
||||
m_rig_ctl_server_settings_isSet = false;
|
||||
satellite_tracker_settings = new SWGSatelliteTrackerSettings();
|
||||
@ -140,6 +144,9 @@ SWGFeatureSettings::cleanup() {
|
||||
if(per_tester_settings != nullptr) {
|
||||
delete per_tester_settings;
|
||||
}
|
||||
if(radiosonde_settings != nullptr) {
|
||||
delete radiosonde_settings;
|
||||
}
|
||||
if(rig_ctl_server_settings != nullptr) {
|
||||
delete rig_ctl_server_settings;
|
||||
}
|
||||
@ -192,6 +199,8 @@ SWGFeatureSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&per_tester_settings, pJson["PERTesterSettings"], "SWGPERTesterSettings", "SWGPERTesterSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&radiosonde_settings, pJson["RadiosondeSettings"], "SWGRadiosondeSettings", "SWGRadiosondeSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&rig_ctl_server_settings, pJson["RigCtlServerSettings"], "SWGRigCtlServerSettings", "SWGRigCtlServerSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&satellite_tracker_settings, pJson["SatelliteTrackerSettings"], "SWGSatelliteTrackerSettings", "SWGSatelliteTrackerSettings");
|
||||
@ -254,6 +263,9 @@ SWGFeatureSettings::asJsonObject() {
|
||||
if((per_tester_settings != nullptr) && (per_tester_settings->isSet())){
|
||||
toJsonValue(QString("PERTesterSettings"), per_tester_settings, obj, QString("SWGPERTesterSettings"));
|
||||
}
|
||||
if((radiosonde_settings != nullptr) && (radiosonde_settings->isSet())){
|
||||
toJsonValue(QString("RadiosondeSettings"), radiosonde_settings, obj, QString("SWGRadiosondeSettings"));
|
||||
}
|
||||
if((rig_ctl_server_settings != nullptr) && (rig_ctl_server_settings->isSet())){
|
||||
toJsonValue(QString("RigCtlServerSettings"), rig_ctl_server_settings, obj, QString("SWGRigCtlServerSettings"));
|
||||
}
|
||||
@ -393,6 +405,16 @@ SWGFeatureSettings::setPerTesterSettings(SWGPERTesterSettings* per_tester_settin
|
||||
this->m_per_tester_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRadiosondeSettings*
|
||||
SWGFeatureSettings::getRadiosondeSettings() {
|
||||
return radiosonde_settings;
|
||||
}
|
||||
void
|
||||
SWGFeatureSettings::setRadiosondeSettings(SWGRadiosondeSettings* radiosonde_settings) {
|
||||
this->radiosonde_settings = radiosonde_settings;
|
||||
this->m_radiosonde_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRigCtlServerSettings*
|
||||
SWGFeatureSettings::getRigCtlServerSettings() {
|
||||
return rig_ctl_server_settings;
|
||||
@ -484,6 +506,9 @@ SWGFeatureSettings::isSet(){
|
||||
if(per_tester_settings && per_tester_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(radiosonde_settings && radiosonde_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(rig_ctl_server_settings && rig_ctl_server_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "SWGJogdialControllerSettings.h"
|
||||
#include "SWGMapSettings.h"
|
||||
#include "SWGPERTesterSettings.h"
|
||||
#include "SWGRadiosondeSettings.h"
|
||||
#include "SWGRigCtlServerSettings.h"
|
||||
#include "SWGSatelliteTrackerSettings.h"
|
||||
#include "SWGSimplePTTSettings.h"
|
||||
@ -92,6 +93,9 @@ public:
|
||||
SWGPERTesterSettings* getPerTesterSettings();
|
||||
void setPerTesterSettings(SWGPERTesterSettings* per_tester_settings);
|
||||
|
||||
SWGRadiosondeSettings* getRadiosondeSettings();
|
||||
void setRadiosondeSettings(SWGRadiosondeSettings* radiosonde_settings);
|
||||
|
||||
SWGRigCtlServerSettings* getRigCtlServerSettings();
|
||||
void setRigCtlServerSettings(SWGRigCtlServerSettings* rig_ctl_server_settings);
|
||||
|
||||
@ -147,6 +151,9 @@ private:
|
||||
SWGPERTesterSettings* per_tester_settings;
|
||||
bool m_per_tester_settings_isSet;
|
||||
|
||||
SWGRadiosondeSettings* radiosonde_settings;
|
||||
bool m_radiosonde_settings_isSet;
|
||||
|
||||
SWGRigCtlServerSettings* rig_ctl_server_settings;
|
||||
bool m_rig_ctl_server_settings_isSet;
|
||||
|
||||
|
@ -221,6 +221,9 @@
|
||||
#include "SWGRadioAstronomySettings.h"
|
||||
#include "SWGRadioClockReport.h"
|
||||
#include "SWGRadioClockSettings.h"
|
||||
#include "SWGRadiosondeDemodReport.h"
|
||||
#include "SWGRadiosondeDemodSettings.h"
|
||||
#include "SWGRadiosondeSettings.h"
|
||||
#include "SWGRange.h"
|
||||
#include "SWGRangeFloat.h"
|
||||
#include "SWGRemoteInputReport.h"
|
||||
@ -1355,6 +1358,21 @@ namespace SWGSDRangel {
|
||||
obj->init();
|
||||
return obj;
|
||||
}
|
||||
if(QString("SWGRadiosondeDemodReport").compare(type) == 0) {
|
||||
SWGRadiosondeDemodReport *obj = new SWGRadiosondeDemodReport();
|
||||
obj->init();
|
||||
return obj;
|
||||
}
|
||||
if(QString("SWGRadiosondeDemodSettings").compare(type) == 0) {
|
||||
SWGRadiosondeDemodSettings *obj = new SWGRadiosondeDemodSettings();
|
||||
obj->init();
|
||||
return obj;
|
||||
}
|
||||
if(QString("SWGRadiosondeSettings").compare(type) == 0) {
|
||||
SWGRadiosondeSettings *obj = new SWGRadiosondeSettings();
|
||||
obj->init();
|
||||
return obj;
|
||||
}
|
||||
if(QString("SWGRange").compare(type) == 0) {
|
||||
SWGRange *obj = new SWGRange();
|
||||
obj->init();
|
||||
|
131
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodReport.cpp
Normal file
131
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodReport.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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 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: 6.0.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 "SWGRadiosondeDemodReport.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGRadiosondeDemodReport::SWGRadiosondeDemodReport(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodReport::SWGRadiosondeDemodReport() {
|
||||
channel_power_db = 0.0f;
|
||||
m_channel_power_db_isSet = false;
|
||||
channel_sample_rate = 0;
|
||||
m_channel_sample_rate_isSet = false;
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodReport::~SWGRadiosondeDemodReport() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodReport::init() {
|
||||
channel_power_db = 0.0f;
|
||||
m_channel_power_db_isSet = false;
|
||||
channel_sample_rate = 0;
|
||||
m_channel_sample_rate_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodReport::cleanup() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodReport*
|
||||
SWGRadiosondeDemodReport::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodReport::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
SWGRadiosondeDemodReport::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGRadiosondeDemodReport::asJsonObject() {
|
||||
QJsonObject* obj = new QJsonObject();
|
||||
if(m_channel_power_db_isSet){
|
||||
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
|
||||
}
|
||||
if(m_channel_sample_rate_isSet){
|
||||
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadiosondeDemodReport::getChannelPowerDb() {
|
||||
return channel_power_db;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodReport::setChannelPowerDb(float channel_power_db) {
|
||||
this->channel_power_db = channel_power_db;
|
||||
this->m_channel_power_db_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodReport::getChannelSampleRate() {
|
||||
return channel_sample_rate;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodReport::setChannelSampleRate(qint32 channel_sample_rate) {
|
||||
this->channel_sample_rate = channel_sample_rate;
|
||||
this->m_channel_sample_rate_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGRadiosondeDemodReport::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(m_channel_power_db_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_channel_sample_rate_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
}
|
||||
|
64
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodReport.h
Normal file
64
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodReport.h
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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 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: 6.0.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGRadiosondeDemodReport.h
|
||||
*
|
||||
* RadiosondeDemod
|
||||
*/
|
||||
|
||||
#ifndef SWGRadiosondeDemodReport_H_
|
||||
#define SWGRadiosondeDemodReport_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGRadiosondeDemodReport: public SWGObject {
|
||||
public:
|
||||
SWGRadiosondeDemodReport();
|
||||
SWGRadiosondeDemodReport(QString* json);
|
||||
virtual ~SWGRadiosondeDemodReport();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGRadiosondeDemodReport* fromJson(QString &jsonString) override;
|
||||
|
||||
float getChannelPowerDb();
|
||||
void setChannelPowerDb(float channel_power_db);
|
||||
|
||||
qint32 getChannelSampleRate();
|
||||
void setChannelSampleRate(qint32 channel_sample_rate);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
private:
|
||||
float channel_power_db;
|
||||
bool m_channel_power_db_isSet;
|
||||
|
||||
qint32 channel_sample_rate;
|
||||
bool m_channel_sample_rate_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SWGRadiosondeDemodReport_H_ */
|
582
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodSettings.cpp
Normal file
582
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodSettings.cpp
Normal file
@ -0,0 +1,582 @@
|
||||
/**
|
||||
* 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 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: 6.0.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 "SWGRadiosondeDemodSettings.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGRadiosondeDemodSettings::SWGRadiosondeDemodSettings(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodSettings::SWGRadiosondeDemodSettings() {
|
||||
baud = 0;
|
||||
m_baud_isSet = false;
|
||||
input_frequency_offset = 0L;
|
||||
m_input_frequency_offset_isSet = false;
|
||||
rf_bandwidth = 0.0f;
|
||||
m_rf_bandwidth_isSet = false;
|
||||
fm_deviation = 0.0f;
|
||||
m_fm_deviation_isSet = false;
|
||||
correlation_threshold = 0.0f;
|
||||
m_correlation_threshold_isSet = false;
|
||||
udp_enabled = 0;
|
||||
m_udp_enabled_isSet = false;
|
||||
udp_address = nullptr;
|
||||
m_udp_address_isSet = false;
|
||||
udp_port = 0;
|
||||
m_udp_port_isSet = false;
|
||||
log_filename = nullptr;
|
||||
m_log_filename_isSet = false;
|
||||
log_enabled = 0;
|
||||
m_log_enabled_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;
|
||||
scope_config = nullptr;
|
||||
m_scope_config_isSet = false;
|
||||
channel_marker = nullptr;
|
||||
m_channel_marker_isSet = false;
|
||||
rollup_state = nullptr;
|
||||
m_rollup_state_isSet = false;
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodSettings::~SWGRadiosondeDemodSettings() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodSettings::init() {
|
||||
baud = 0;
|
||||
m_baud_isSet = false;
|
||||
input_frequency_offset = 0L;
|
||||
m_input_frequency_offset_isSet = false;
|
||||
rf_bandwidth = 0.0f;
|
||||
m_rf_bandwidth_isSet = false;
|
||||
fm_deviation = 0.0f;
|
||||
m_fm_deviation_isSet = false;
|
||||
correlation_threshold = 0.0f;
|
||||
m_correlation_threshold_isSet = false;
|
||||
udp_enabled = 0;
|
||||
m_udp_enabled_isSet = false;
|
||||
udp_address = new QString("");
|
||||
m_udp_address_isSet = false;
|
||||
udp_port = 0;
|
||||
m_udp_port_isSet = false;
|
||||
log_filename = new QString("");
|
||||
m_log_filename_isSet = false;
|
||||
log_enabled = 0;
|
||||
m_log_enabled_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;
|
||||
scope_config = new SWGGLScope();
|
||||
m_scope_config_isSet = false;
|
||||
channel_marker = new SWGChannelMarker();
|
||||
m_channel_marker_isSet = false;
|
||||
rollup_state = new SWGRollupState();
|
||||
m_rollup_state_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodSettings::cleanup() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(udp_address != nullptr) {
|
||||
delete udp_address;
|
||||
}
|
||||
|
||||
if(log_filename != nullptr) {
|
||||
delete log_filename;
|
||||
}
|
||||
|
||||
|
||||
if(title != nullptr) {
|
||||
delete title;
|
||||
}
|
||||
|
||||
|
||||
if(reverse_api_address != nullptr) {
|
||||
delete reverse_api_address;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(scope_config != nullptr) {
|
||||
delete scope_config;
|
||||
}
|
||||
if(channel_marker != nullptr) {
|
||||
delete channel_marker;
|
||||
}
|
||||
if(rollup_state != nullptr) {
|
||||
delete rollup_state;
|
||||
}
|
||||
}
|
||||
|
||||
SWGRadiosondeDemodSettings*
|
||||
SWGRadiosondeDemodSettings::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeDemodSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&baud, pJson["baud"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
|
||||
|
||||
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&correlation_threshold, pJson["correlationThreshold"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "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", "");
|
||||
|
||||
::SWGSDRangel::setValue(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope");
|
||||
|
||||
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
|
||||
|
||||
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
SWGRadiosondeDemodSettings::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGRadiosondeDemodSettings::asJsonObject() {
|
||||
QJsonObject* obj = new QJsonObject();
|
||||
if(m_baud_isSet){
|
||||
obj->insert("baud", QJsonValue(baud));
|
||||
}
|
||||
if(m_input_frequency_offset_isSet){
|
||||
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
|
||||
}
|
||||
if(m_rf_bandwidth_isSet){
|
||||
obj->insert("rfBandwidth", QJsonValue(rf_bandwidth));
|
||||
}
|
||||
if(m_fm_deviation_isSet){
|
||||
obj->insert("fmDeviation", QJsonValue(fm_deviation));
|
||||
}
|
||||
if(m_correlation_threshold_isSet){
|
||||
obj->insert("correlationThreshold", QJsonValue(correlation_threshold));
|
||||
}
|
||||
if(m_udp_enabled_isSet){
|
||||
obj->insert("udpEnabled", QJsonValue(udp_enabled));
|
||||
}
|
||||
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(log_filename != nullptr && *log_filename != QString("")){
|
||||
toJsonValue(QString("logFilename"), log_filename, obj, QString("QString"));
|
||||
}
|
||||
if(m_log_enabled_isSet){
|
||||
obj->insert("logEnabled", QJsonValue(log_enabled));
|
||||
}
|
||||
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));
|
||||
}
|
||||
if((scope_config != nullptr) && (scope_config->isSet())){
|
||||
toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope"));
|
||||
}
|
||||
if((channel_marker != nullptr) && (channel_marker->isSet())){
|
||||
toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker"));
|
||||
}
|
||||
if((rollup_state != nullptr) && (rollup_state->isSet())){
|
||||
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getBaud() {
|
||||
return baud;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setBaud(qint32 baud) {
|
||||
this->baud = baud;
|
||||
this->m_baud_isSet = true;
|
||||
}
|
||||
|
||||
qint64
|
||||
SWGRadiosondeDemodSettings::getInputFrequencyOffset() {
|
||||
return input_frequency_offset;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
|
||||
this->input_frequency_offset = input_frequency_offset;
|
||||
this->m_input_frequency_offset_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadiosondeDemodSettings::getRfBandwidth() {
|
||||
return rf_bandwidth;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setRfBandwidth(float rf_bandwidth) {
|
||||
this->rf_bandwidth = rf_bandwidth;
|
||||
this->m_rf_bandwidth_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadiosondeDemodSettings::getFmDeviation() {
|
||||
return fm_deviation;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setFmDeviation(float fm_deviation) {
|
||||
this->fm_deviation = fm_deviation;
|
||||
this->m_fm_deviation_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadiosondeDemodSettings::getCorrelationThreshold() {
|
||||
return correlation_threshold;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setCorrelationThreshold(float correlation_threshold) {
|
||||
this->correlation_threshold = correlation_threshold;
|
||||
this->m_correlation_threshold_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getUdpEnabled() {
|
||||
return udp_enabled;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setUdpEnabled(qint32 udp_enabled) {
|
||||
this->udp_enabled = udp_enabled;
|
||||
this->m_udp_enabled_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeDemodSettings::getUdpAddress() {
|
||||
return udp_address;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setUdpAddress(QString* udp_address) {
|
||||
this->udp_address = udp_address;
|
||||
this->m_udp_address_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getUdpPort() {
|
||||
return udp_port;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setUdpPort(qint32 udp_port) {
|
||||
this->udp_port = udp_port;
|
||||
this->m_udp_port_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeDemodSettings::getLogFilename() {
|
||||
return log_filename;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setLogFilename(QString* log_filename) {
|
||||
this->log_filename = log_filename;
|
||||
this->m_log_filename_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getLogEnabled() {
|
||||
return log_enabled;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setLogEnabled(qint32 log_enabled) {
|
||||
this->log_enabled = log_enabled;
|
||||
this->m_log_enabled_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getRgbColor() {
|
||||
return rgb_color;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setRgbColor(qint32 rgb_color) {
|
||||
this->rgb_color = rgb_color;
|
||||
this->m_rgb_color_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeDemodSettings::getTitle() {
|
||||
return title;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setTitle(QString* title) {
|
||||
this->title = title;
|
||||
this->m_title_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getStreamIndex() {
|
||||
return stream_index;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setStreamIndex(qint32 stream_index) {
|
||||
this->stream_index = stream_index;
|
||||
this->m_stream_index_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getUseReverseApi() {
|
||||
return use_reverse_api;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
|
||||
this->use_reverse_api = use_reverse_api;
|
||||
this->m_use_reverse_api_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeDemodSettings::getReverseApiAddress() {
|
||||
return reverse_api_address;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
|
||||
this->reverse_api_address = reverse_api_address;
|
||||
this->m_reverse_api_address_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getReverseApiPort() {
|
||||
return reverse_api_port;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
|
||||
this->reverse_api_port = reverse_api_port;
|
||||
this->m_reverse_api_port_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getReverseApiDeviceIndex() {
|
||||
return reverse_api_device_index;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
|
||||
this->reverse_api_device_index = reverse_api_device_index;
|
||||
this->m_reverse_api_device_index_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeDemodSettings::getReverseApiChannelIndex() {
|
||||
return reverse_api_channel_index;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
|
||||
this->reverse_api_channel_index = reverse_api_channel_index;
|
||||
this->m_reverse_api_channel_index_isSet = true;
|
||||
}
|
||||
|
||||
SWGGLScope*
|
||||
SWGRadiosondeDemodSettings::getScopeConfig() {
|
||||
return scope_config;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setScopeConfig(SWGGLScope* scope_config) {
|
||||
this->scope_config = scope_config;
|
||||
this->m_scope_config_isSet = true;
|
||||
}
|
||||
|
||||
SWGChannelMarker*
|
||||
SWGRadiosondeDemodSettings::getChannelMarker() {
|
||||
return channel_marker;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) {
|
||||
this->channel_marker = channel_marker;
|
||||
this->m_channel_marker_isSet = true;
|
||||
}
|
||||
|
||||
SWGRollupState*
|
||||
SWGRadiosondeDemodSettings::getRollupState() {
|
||||
return rollup_state;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeDemodSettings::setRollupState(SWGRollupState* rollup_state) {
|
||||
this->rollup_state = rollup_state;
|
||||
this->m_rollup_state_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGRadiosondeDemodSettings::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(m_baud_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_input_frequency_offset_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_rf_bandwidth_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_fm_deviation_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_correlation_threshold_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_udp_enabled_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(udp_address && *udp_address != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_udp_port_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(log_filename && *log_filename != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_log_enabled_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;
|
||||
}
|
||||
if(scope_config && scope_config->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(channel_marker && channel_marker->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(rollup_state && rollup_state->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
}
|
||||
|
182
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodSettings.h
Normal file
182
swagger/sdrangel/code/qt5/client/SWGRadiosondeDemodSettings.h
Normal file
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* 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 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: 6.0.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGRadiosondeDemodSettings.h
|
||||
*
|
||||
* RadiosondeDemod
|
||||
*/
|
||||
|
||||
#ifndef SWGRadiosondeDemodSettings_H_
|
||||
#define SWGRadiosondeDemodSettings_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include "SWGChannelMarker.h"
|
||||
#include "SWGGLScope.h"
|
||||
#include "SWGRollupState.h"
|
||||
#include <QString>
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGRadiosondeDemodSettings: public SWGObject {
|
||||
public:
|
||||
SWGRadiosondeDemodSettings();
|
||||
SWGRadiosondeDemodSettings(QString* json);
|
||||
virtual ~SWGRadiosondeDemodSettings();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGRadiosondeDemodSettings* fromJson(QString &jsonString) override;
|
||||
|
||||
qint32 getBaud();
|
||||
void setBaud(qint32 baud);
|
||||
|
||||
qint64 getInputFrequencyOffset();
|
||||
void setInputFrequencyOffset(qint64 input_frequency_offset);
|
||||
|
||||
float getRfBandwidth();
|
||||
void setRfBandwidth(float rf_bandwidth);
|
||||
|
||||
float getFmDeviation();
|
||||
void setFmDeviation(float fm_deviation);
|
||||
|
||||
float getCorrelationThreshold();
|
||||
void setCorrelationThreshold(float correlation_threshold);
|
||||
|
||||
qint32 getUdpEnabled();
|
||||
void setUdpEnabled(qint32 udp_enabled);
|
||||
|
||||
QString* getUdpAddress();
|
||||
void setUdpAddress(QString* udp_address);
|
||||
|
||||
qint32 getUdpPort();
|
||||
void setUdpPort(qint32 udp_port);
|
||||
|
||||
QString* getLogFilename();
|
||||
void setLogFilename(QString* log_filename);
|
||||
|
||||
qint32 getLogEnabled();
|
||||
void setLogEnabled(qint32 log_enabled);
|
||||
|
||||
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);
|
||||
|
||||
SWGGLScope* getScopeConfig();
|
||||
void setScopeConfig(SWGGLScope* scope_config);
|
||||
|
||||
SWGChannelMarker* getChannelMarker();
|
||||
void setChannelMarker(SWGChannelMarker* channel_marker);
|
||||
|
||||
SWGRollupState* getRollupState();
|
||||
void setRollupState(SWGRollupState* rollup_state);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
private:
|
||||
qint32 baud;
|
||||
bool m_baud_isSet;
|
||||
|
||||
qint64 input_frequency_offset;
|
||||
bool m_input_frequency_offset_isSet;
|
||||
|
||||
float rf_bandwidth;
|
||||
bool m_rf_bandwidth_isSet;
|
||||
|
||||
float fm_deviation;
|
||||
bool m_fm_deviation_isSet;
|
||||
|
||||
float correlation_threshold;
|
||||
bool m_correlation_threshold_isSet;
|
||||
|
||||
qint32 udp_enabled;
|
||||
bool m_udp_enabled_isSet;
|
||||
|
||||
QString* udp_address;
|
||||
bool m_udp_address_isSet;
|
||||
|
||||
qint32 udp_port;
|
||||
bool m_udp_port_isSet;
|
||||
|
||||
QString* log_filename;
|
||||
bool m_log_filename_isSet;
|
||||
|
||||
qint32 log_enabled;
|
||||
bool m_log_enabled_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;
|
||||
|
||||
SWGGLScope* scope_config;
|
||||
bool m_scope_config_isSet;
|
||||
|
||||
SWGChannelMarker* channel_marker;
|
||||
bool m_channel_marker_isSet;
|
||||
|
||||
SWGRollupState* rollup_state;
|
||||
bool m_rollup_state_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SWGRadiosondeDemodSettings_H_ */
|
275
swagger/sdrangel/code/qt5/client/SWGRadiosondeSettings.cpp
Normal file
275
swagger/sdrangel/code/qt5/client/SWGRadiosondeSettings.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 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 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: 6.0.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 "SWGRadiosondeSettings.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGRadiosondeSettings::SWGRadiosondeSettings(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGRadiosondeSettings::SWGRadiosondeSettings() {
|
||||
title = nullptr;
|
||||
m_title_isSet = false;
|
||||
rgb_color = 0;
|
||||
m_rgb_color_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_feature_set_index = 0;
|
||||
m_reverse_api_feature_set_index_isSet = false;
|
||||
reverse_api_feature_index = 0;
|
||||
m_reverse_api_feature_index_isSet = false;
|
||||
rollup_state = nullptr;
|
||||
m_rollup_state_isSet = false;
|
||||
}
|
||||
|
||||
SWGRadiosondeSettings::~SWGRadiosondeSettings() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeSettings::init() {
|
||||
title = new QString("");
|
||||
m_title_isSet = false;
|
||||
rgb_color = 0;
|
||||
m_rgb_color_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_feature_set_index = 0;
|
||||
m_reverse_api_feature_set_index_isSet = false;
|
||||
reverse_api_feature_index = 0;
|
||||
m_reverse_api_feature_index_isSet = false;
|
||||
rollup_state = new SWGRollupState();
|
||||
m_rollup_state_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeSettings::cleanup() {
|
||||
if(title != nullptr) {
|
||||
delete title;
|
||||
}
|
||||
|
||||
|
||||
if(reverse_api_address != nullptr) {
|
||||
delete reverse_api_address;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(rollup_state != nullptr) {
|
||||
delete rollup_state;
|
||||
}
|
||||
}
|
||||
|
||||
SWGRadiosondeSettings*
|
||||
SWGRadiosondeSettings::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadiosondeSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "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_feature_set_index, pJson["reverseAPIFeatureSetIndex"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&reverse_api_feature_index, pJson["reverseAPIFeatureIndex"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
SWGRadiosondeSettings::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGRadiosondeSettings::asJsonObject() {
|
||||
QJsonObject* obj = new QJsonObject();
|
||||
if(title != nullptr && *title != QString("")){
|
||||
toJsonValue(QString("title"), title, obj, QString("QString"));
|
||||
}
|
||||
if(m_rgb_color_isSet){
|
||||
obj->insert("rgbColor", QJsonValue(rgb_color));
|
||||
}
|
||||
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_feature_set_index_isSet){
|
||||
obj->insert("reverseAPIFeatureSetIndex", QJsonValue(reverse_api_feature_set_index));
|
||||
}
|
||||
if(m_reverse_api_feature_index_isSet){
|
||||
obj->insert("reverseAPIFeatureIndex", QJsonValue(reverse_api_feature_index));
|
||||
}
|
||||
if((rollup_state != nullptr) && (rollup_state->isSet())){
|
||||
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeSettings::getTitle() {
|
||||
return title;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setTitle(QString* title) {
|
||||
this->title = title;
|
||||
this->m_title_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeSettings::getRgbColor() {
|
||||
return rgb_color;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setRgbColor(qint32 rgb_color) {
|
||||
this->rgb_color = rgb_color;
|
||||
this->m_rgb_color_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeSettings::getUseReverseApi() {
|
||||
return use_reverse_api;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setUseReverseApi(qint32 use_reverse_api) {
|
||||
this->use_reverse_api = use_reverse_api;
|
||||
this->m_use_reverse_api_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadiosondeSettings::getReverseApiAddress() {
|
||||
return reverse_api_address;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setReverseApiAddress(QString* reverse_api_address) {
|
||||
this->reverse_api_address = reverse_api_address;
|
||||
this->m_reverse_api_address_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeSettings::getReverseApiPort() {
|
||||
return reverse_api_port;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setReverseApiPort(qint32 reverse_api_port) {
|
||||
this->reverse_api_port = reverse_api_port;
|
||||
this->m_reverse_api_port_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeSettings::getReverseApiFeatureSetIndex() {
|
||||
return reverse_api_feature_set_index;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index) {
|
||||
this->reverse_api_feature_set_index = reverse_api_feature_set_index;
|
||||
this->m_reverse_api_feature_set_index_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadiosondeSettings::getReverseApiFeatureIndex() {
|
||||
return reverse_api_feature_index;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setReverseApiFeatureIndex(qint32 reverse_api_feature_index) {
|
||||
this->reverse_api_feature_index = reverse_api_feature_index;
|
||||
this->m_reverse_api_feature_index_isSet = true;
|
||||
}
|
||||
|
||||
SWGRollupState*
|
||||
SWGRadiosondeSettings::getRollupState() {
|
||||
return rollup_state;
|
||||
}
|
||||
void
|
||||
SWGRadiosondeSettings::setRollupState(SWGRollupState* rollup_state) {
|
||||
this->rollup_state = rollup_state;
|
||||
this->m_rollup_state_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGRadiosondeSettings::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(title && *title != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_rgb_color_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_feature_set_index_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_reverse_api_feature_index_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(rollup_state && rollup_state->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
}
|
||||
|
102
swagger/sdrangel/code/qt5/client/SWGRadiosondeSettings.h
Normal file
102
swagger/sdrangel/code/qt5/client/SWGRadiosondeSettings.h
Normal file
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 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: 6.0.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGRadiosondeSettings.h
|
||||
*
|
||||
* Radiosonde settings
|
||||
*/
|
||||
|
||||
#ifndef SWGRadiosondeSettings_H_
|
||||
#define SWGRadiosondeSettings_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include "SWGRollupState.h"
|
||||
#include <QString>
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGRadiosondeSettings: public SWGObject {
|
||||
public:
|
||||
SWGRadiosondeSettings();
|
||||
SWGRadiosondeSettings(QString* json);
|
||||
virtual ~SWGRadiosondeSettings();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGRadiosondeSettings* fromJson(QString &jsonString) override;
|
||||
|
||||
QString* getTitle();
|
||||
void setTitle(QString* title);
|
||||
|
||||
qint32 getRgbColor();
|
||||
void setRgbColor(qint32 rgb_color);
|
||||
|
||||
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 getReverseApiFeatureSetIndex();
|
||||
void setReverseApiFeatureSetIndex(qint32 reverse_api_feature_set_index);
|
||||
|
||||
qint32 getReverseApiFeatureIndex();
|
||||
void setReverseApiFeatureIndex(qint32 reverse_api_feature_index);
|
||||
|
||||
SWGRollupState* getRollupState();
|
||||
void setRollupState(SWGRollupState* rollup_state);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
private:
|
||||
QString* title;
|
||||
bool m_title_isSet;
|
||||
|
||||
qint32 rgb_color;
|
||||
bool m_rgb_color_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_feature_set_index;
|
||||
bool m_reverse_api_feature_set_index_isSet;
|
||||
|
||||
qint32 reverse_api_feature_index;
|
||||
bool m_reverse_api_feature_index_isSet;
|
||||
|
||||
SWGRollupState* rollup_state;
|
||||
bool m_rollup_state_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SWGRadiosondeSettings_H_ */
|
Loading…
Reference in New Issue
Block a user