mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 09:18:54 -05:00
Add radio clock plugin
This commit is contained in:
parent
4e449b397c
commit
0faaa8397d
BIN
doc/img/RadioClock_plugin.png
Normal file
BIN
doc/img/RadioClock_plugin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -19,6 +19,7 @@ add_subdirectory(demodvorsc)
|
||||
add_subdirectory(demodpacket)
|
||||
add_subdirectory(demodais)
|
||||
add_subdirectory(noisefigure)
|
||||
add_subdirectory(radioclock)
|
||||
|
||||
if(DAB_FOUND AND ZLIB_FOUND AND FAAD_FOUND)
|
||||
add_subdirectory(demoddab)
|
||||
|
58
plugins/channelrx/radioclock/CMakeLists.txt
Normal file
58
plugins/channelrx/radioclock/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
||||
project(radioclock)
|
||||
|
||||
set(radioclock_SOURCES
|
||||
radioclock.cpp
|
||||
radioclocksettings.cpp
|
||||
radioclockbaseband.cpp
|
||||
radioclocksink.cpp
|
||||
radioclockplugin.cpp
|
||||
radioclockwebapiadapter.cpp
|
||||
)
|
||||
|
||||
set(radioclock_HEADERS
|
||||
radioclock.h
|
||||
radioclocksettings.h
|
||||
radioclockbaseband.h
|
||||
radioclocksink.h
|
||||
radioclockplugin.h
|
||||
radioclockwebapiadapter.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
set(radioclock_SOURCES
|
||||
${radioclock_SOURCES}
|
||||
radioclockgui.cpp
|
||||
radioclockgui.ui
|
||||
)
|
||||
set(radioclock_HEADERS
|
||||
${radioclock_HEADERS}
|
||||
radioclockgui.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME radioclock)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME radioclocksrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${radioclock_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
475
plugins/channelrx/radioclock/radioclock.cpp
Normal file
475
plugins/channelrx/radioclock/radioclock.cpp
Normal file
@ -0,0 +1,475 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "radioclock.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"
|
||||
#include "radioclocksink.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RadioClock::MsgConfigureRadioClock, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RadioClock::MsgDateTime, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RadioClock::MsgStatus, Message)
|
||||
|
||||
const char * const RadioClock::m_channelIdURI = "sdrangel.channel.radioclock";
|
||||
const char * const RadioClock::m_channelId = "RadioClock";
|
||||
|
||||
RadioClock::RadioClock(DeviceAPI *deviceAPI) :
|
||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_basebandSampleRate(0)
|
||||
{
|
||||
setObjectName(m_channelId);
|
||||
|
||||
m_basebandSink = new RadioClockBaseband(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*)));
|
||||
}
|
||||
|
||||
RadioClock::~RadioClock()
|
||||
{
|
||||
qDebug("RadioClock::~RadioClock");
|
||||
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 RadioClock::getNumberOfDeviceStreams() const
|
||||
{
|
||||
return m_deviceAPI->getNbSourceStreams();
|
||||
}
|
||||
|
||||
void RadioClock::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
|
||||
{
|
||||
(void) firstOfBurst;
|
||||
m_basebandSink->feed(begin, end);
|
||||
}
|
||||
|
||||
void RadioClock::start()
|
||||
{
|
||||
qDebug("RadioClock::start");
|
||||
|
||||
m_basebandSink->reset();
|
||||
m_basebandSink->startWork();
|
||||
m_thread.start();
|
||||
|
||||
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
|
||||
m_basebandSink->getInputMessageQueue()->push(dspMsg);
|
||||
|
||||
RadioClockBaseband::MsgConfigureRadioClockBaseband *msg = RadioClockBaseband::MsgConfigureRadioClockBaseband::create(m_settings, true);
|
||||
m_basebandSink->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
|
||||
void RadioClock::stop()
|
||||
{
|
||||
qDebug("RadioClock::stop");
|
||||
m_basebandSink->stopWork();
|
||||
m_thread.quit();
|
||||
m_thread.wait();
|
||||
}
|
||||
|
||||
bool RadioClock::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureRadioClock::match(cmd))
|
||||
{
|
||||
MsgConfigureRadioClock& cfg = (MsgConfigureRadioClock&) cmd;
|
||||
qDebug() << "RadioClock::handleMessage: MsgConfigureRadioClock";
|
||||
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() << "RadioClock::handleMessage: DSPSignalNotification";
|
||||
m_basebandSink->getInputMessageQueue()->push(rep);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgDateTime::match(cmd))
|
||||
{
|
||||
MsgDateTime& report = (MsgDateTime&)cmd;
|
||||
// Save time for web report
|
||||
m_dateTime = report.getDateTime();
|
||||
// Forward to GUI
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
getMessageQueueToGUI()->push(new MsgDateTime(report));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgStatus::match(cmd))
|
||||
{
|
||||
// Forward to GUI
|
||||
MsgStatus& report = (MsgStatus&)cmd;
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
getMessageQueueToGUI()->push(new MsgStatus(report));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ScopeVis *RadioClock::getScopeSink()
|
||||
{
|
||||
return m_basebandSink->getScopeSink();
|
||||
}
|
||||
|
||||
void RadioClock::applySettings(const RadioClockSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RadioClock::applySettings:"
|
||||
<< " 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_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
|
||||
reverseAPIKeys.append("inputFrequencyOffset");
|
||||
}
|
||||
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
|
||||
reverseAPIKeys.append("rfBandwidth");
|
||||
}
|
||||
if ((settings.m_threshold != m_settings.m_threshold) || force) {
|
||||
reverseAPIKeys.append("threshold");
|
||||
}
|
||||
if ((settings.m_modulation != m_settings.m_modulation) || force) {
|
||||
reverseAPIKeys.append("modulation");
|
||||
}
|
||||
if ((settings.m_timezone != m_settings.m_timezone) || force) {
|
||||
reverseAPIKeys.append("timezone");
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
RadioClockBaseband::MsgConfigureRadioClockBaseband *msg = RadioClockBaseband::MsgConfigureRadioClockBaseband::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);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
QByteArray RadioClock::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RadioClock::deserialize(const QByteArray& data)
|
||||
{
|
||||
if (m_settings.deserialize(data))
|
||||
{
|
||||
MsgConfigureRadioClock *msg = MsgConfigureRadioClock::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
MsgConfigureRadioClock *msg = MsgConfigureRadioClock::create(m_settings, true);
|
||||
m_inputMessageQueue.push(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int RadioClock::webapiSettingsGet(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadioClockSettings(new SWGSDRangel::SWGRadioClockSettings());
|
||||
response.getRadioClockSettings()->init();
|
||||
webapiFormatChannelSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RadioClock::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
RadioClockSettings settings = m_settings;
|
||||
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
|
||||
|
||||
MsgConfigureRadioClock *msg = MsgConfigureRadioClock::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
qDebug("RadioClock::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureRadioClock *msgToGUI = MsgConfigureRadioClock::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatChannelSettings(response, settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
void RadioClock::webapiUpdateChannelSettings(
|
||||
RadioClockSettings& settings,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response)
|
||||
{
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
|
||||
settings.m_inputFrequencyOffset = response.getRadioClockSettings()->getInputFrequencyOffset();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rfBandwidth")) {
|
||||
settings.m_rfBandwidth = response.getRadioClockSettings()->getRfBandwidth();
|
||||
}
|
||||
if (channelSettingsKeys.contains("threshold")) {
|
||||
settings.m_threshold = response.getRadioClockSettings()->getThreshold();
|
||||
}
|
||||
if (channelSettingsKeys.contains("modulation")) {
|
||||
settings.m_modulation = (RadioClockSettings::Modulation)response.getRadioClockSettings()->getModulation();
|
||||
}
|
||||
if (channelSettingsKeys.contains("timezone")) {
|
||||
settings.m_timezone = (RadioClockSettings::DisplayTZ)response.getRadioClockSettings()->getTimezone();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getRadioClockSettings()->getRgbColor();
|
||||
}
|
||||
if (channelSettingsKeys.contains("title")) {
|
||||
settings.m_title = *response.getRadioClockSettings()->getTitle();
|
||||
}
|
||||
if (channelSettingsKeys.contains("streamIndex")) {
|
||||
settings.m_streamIndex = response.getRadioClockSettings()->getStreamIndex();
|
||||
}
|
||||
if (channelSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getRadioClockSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getRadioClockSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getRadioClockSettings()->getReverseApiPort();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
|
||||
settings.m_reverseAPIDeviceIndex = response.getRadioClockSettings()->getReverseApiDeviceIndex();
|
||||
}
|
||||
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
|
||||
settings.m_reverseAPIChannelIndex = response.getRadioClockSettings()->getReverseApiChannelIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClock::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const RadioClockSettings& settings)
|
||||
{
|
||||
response.getRadioClockSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
response.getRadioClockSettings()->setRfBandwidth(settings.m_rfBandwidth);
|
||||
response.getRadioClockSettings()->setThreshold(settings.m_threshold);
|
||||
response.getRadioClockSettings()->setModulation((int)settings.m_modulation);
|
||||
response.getRadioClockSettings()->setTimezone((int)settings.m_timezone);
|
||||
|
||||
response.getRadioClockSettings()->setRgbColor(settings.m_rgbColor);
|
||||
if (response.getRadioClockSettings()->getTitle()) {
|
||||
*response.getRadioClockSettings()->getTitle() = settings.m_title;
|
||||
} else {
|
||||
response.getRadioClockSettings()->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
|
||||
response.getRadioClockSettings()->setStreamIndex(settings.m_streamIndex);
|
||||
response.getRadioClockSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getRadioClockSettings()->getReverseApiAddress()) {
|
||||
*response.getRadioClockSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getRadioClockSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getRadioClockSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getRadioClockSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getRadioClockSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
}
|
||||
|
||||
void RadioClock::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
|
||||
{
|
||||
double magsqAvg, magsqPeak;
|
||||
int nbMagsqSamples;
|
||||
getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
|
||||
|
||||
response.getRadioClockReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg));
|
||||
response.getRadioClockReport()->setChannelSampleRate(RADIOCLOCK_CHANNEL_SAMPLE_RATE);
|
||||
response.getRadioClockReport()->setDate(new QString(m_dateTime.date().toString()));
|
||||
response.getRadioClockReport()->setTime(new QString(m_dateTime.time().toString()));
|
||||
}
|
||||
|
||||
void RadioClock::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RadioClockSettings& 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;
|
||||
}
|
||||
|
||||
int RadioClock::webapiReportGet(
|
||||
SWGSDRangel::SWGChannelReport& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadioClockReport(new SWGSDRangel::SWGRadioClockReport());
|
||||
response.getRadioClockReport()->init();
|
||||
webapiFormatChannelReport(response);
|
||||
return 200;
|
||||
}
|
||||
|
||||
void RadioClock::webapiFormatChannelSettings(
|
||||
QList<QString>& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
|
||||
const RadioClockSettings& settings,
|
||||
bool force
|
||||
)
|
||||
{
|
||||
swgChannelSettings->setDirection(0); // Single sink (Rx)
|
||||
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
|
||||
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
|
||||
swgChannelSettings->setChannelType(new QString("RadioClock"));
|
||||
swgChannelSettings->setRadioClockSettings(new SWGSDRangel::SWGRadioClockSettings());
|
||||
SWGSDRangel::SWGRadioClockSettings *swgRadioClockSettings = swgChannelSettings->getRadioClockSettings();
|
||||
|
||||
// transfer data that has been modified. When force is on transfer all data except reverse API data
|
||||
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
|
||||
swgRadioClockSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rfBandwidth") || force) {
|
||||
swgRadioClockSettings->setRfBandwidth(settings.m_rfBandwidth);
|
||||
}
|
||||
if (channelSettingsKeys.contains("threshold") || force) {
|
||||
swgRadioClockSettings->setThreshold(settings.m_threshold);
|
||||
}
|
||||
if (channelSettingsKeys.contains("modulation") || force) {
|
||||
swgRadioClockSettings->setModulation(settings.m_modulation);
|
||||
}
|
||||
if (channelSettingsKeys.contains("timezone") || force) {
|
||||
swgRadioClockSettings->setTimezone(settings.m_timezone);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||
swgRadioClockSettings->setRgbColor(settings.m_rgbColor);
|
||||
}
|
||||
if (channelSettingsKeys.contains("title") || force) {
|
||||
swgRadioClockSettings->setTitle(new QString(settings.m_title));
|
||||
}
|
||||
if (channelSettingsKeys.contains("streamIndex") || force) {
|
||||
swgRadioClockSettings->setStreamIndex(settings.m_streamIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClock::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "RadioClock::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("RadioClock::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
199
plugins/channelrx/radioclock/radioclock.h
Normal file
199
plugins/channelrx/radioclock/radioclock.h
Normal file
@ -0,0 +1,199 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_RADIOCLOCK_H
|
||||
#define INCLUDE_RADIOCLOCK_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QThread>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "channel/channelapi.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "radioclockbaseband.h"
|
||||
#include "radioclocksettings.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QThread;
|
||||
class DeviceAPI;
|
||||
class ScopeVis;
|
||||
|
||||
class RadioClock : public BasebandSampleSink, public ChannelAPI {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureRadioClock : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RadioClockSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRadioClock* create(const RadioClockSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureRadioClock(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RadioClockSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRadioClock(const RadioClockSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgDateTime : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QDateTime getDateTime() const { return m_dateTime; }
|
||||
|
||||
static MsgDateTime* create(QDateTime dateTime)
|
||||
{
|
||||
return new MsgDateTime(dateTime);
|
||||
}
|
||||
|
||||
private:
|
||||
QDateTime m_dateTime;
|
||||
|
||||
MsgDateTime(QDateTime dateTime) :
|
||||
Message(),
|
||||
m_dateTime(dateTime)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class MsgStatus : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QString getStatus() const { return m_status; }
|
||||
|
||||
static MsgStatus* create(QString status)
|
||||
{
|
||||
return new MsgStatus(status);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_status;
|
||||
|
||||
MsgStatus(QString status) :
|
||||
Message(),
|
||||
m_status(status)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
RadioClock(DeviceAPI *deviceAPI);
|
||||
virtual ~RadioClock();
|
||||
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 bool handleMessage(const Message& cmd);
|
||||
|
||||
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 0; }
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
|
||||
virtual int getNbSinkStreams() const { return 1; }
|
||||
virtual int getNbSourceStreams() const { return 0; }
|
||||
|
||||
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
|
||||
{
|
||||
(void) streamIndex;
|
||||
(void) sinkElseSource;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiReportGet(
|
||||
SWGSDRangel::SWGChannelReport& response,
|
||||
QString& errorMessage);
|
||||
|
||||
static void webapiFormatChannelSettings(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
const RadioClockSettings& settings);
|
||||
|
||||
static void webapiUpdateChannelSettings(
|
||||
RadioClockSettings& 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;
|
||||
RadioClockBaseband* m_basebandSink;
|
||||
RadioClockSettings m_settings;
|
||||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||
qint64 m_centerFrequency;
|
||||
QDateTime m_dateTime; //!< Received data and time
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void applySettings(const RadioClockSettings& settings, bool force = false);
|
||||
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
|
||||
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const RadioClockSettings& settings, bool force);
|
||||
void webapiFormatChannelSettings(
|
||||
QList<QString>& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
|
||||
const RadioClockSettings& settings,
|
||||
bool force
|
||||
);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOCLOCK_H
|
176
plugins/channelrx/radioclock/radioclockbaseband.cpp
Normal file
176
plugins/channelrx/radioclock/radioclockbaseband.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 "radioclockbaseband.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RadioClockBaseband::MsgConfigureRadioClockBaseband, Message)
|
||||
|
||||
RadioClockBaseband::RadioClockBaseband(RadioClock *radioClock) :
|
||||
m_sink(radioClock),
|
||||
m_running(false),
|
||||
m_mutex(QMutex::Recursive)
|
||||
{
|
||||
qDebug("RadioClockBaseband::RadioClockBaseband");
|
||||
|
||||
m_sink.setScopeSink(&m_scopeSink);
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
|
||||
m_channelizer = new DownChannelizer(&m_sink);
|
||||
}
|
||||
|
||||
RadioClockBaseband::~RadioClockBaseband()
|
||||
{
|
||||
m_inputMessageQueue.clear();
|
||||
|
||||
delete m_channelizer;
|
||||
}
|
||||
|
||||
void RadioClockBaseband::reset()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_inputMessageQueue.clear();
|
||||
m_sampleFifo.reset();
|
||||
}
|
||||
|
||||
void RadioClockBaseband::startWork()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
QObject::connect(
|
||||
&m_sampleFifo,
|
||||
&SampleSinkFifo::dataReady,
|
||||
this,
|
||||
&RadioClockBaseband::handleData,
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
void RadioClockBaseband::stopWork()
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
QObject::disconnect(
|
||||
&m_sampleFifo,
|
||||
&SampleSinkFifo::dataReady,
|
||||
this,
|
||||
&RadioClockBaseband::handleData
|
||||
);
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void RadioClockBaseband::setChannel(ChannelAPI *channel)
|
||||
{
|
||||
m_sink.setChannel(channel);
|
||||
}
|
||||
|
||||
void RadioClockBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
|
||||
{
|
||||
m_sampleFifo.write(begin, end);
|
||||
}
|
||||
|
||||
void RadioClockBaseband::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 RadioClockBaseband::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != nullptr)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RadioClockBaseband::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigureRadioClockBaseband::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
MsgConfigureRadioClockBaseband& cfg = (MsgConfigureRadioClockBaseband&) cmd;
|
||||
qDebug() << "RadioClockBaseband::handleMessage: MsgConfigureRadioClockBaseband";
|
||||
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
qDebug() << "RadioClockBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
|
||||
setBasebandSampleRate(notif.getSampleRate());
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockBaseband::applySettings(const RadioClockSettings& settings, bool force)
|
||||
{
|
||||
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
|
||||
{
|
||||
m_channelizer->setChannelization(RADIOCLOCK_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
|
||||
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
|
||||
}
|
||||
|
||||
m_sink.applySettings(settings, force);
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void RadioClockBaseband::setBasebandSampleRate(int sampleRate)
|
||||
{
|
||||
m_channelizer->setBasebandSampleRate(sampleRate);
|
||||
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
|
||||
}
|
99
plugins/channelrx/radioclock/radioclockbaseband.h
Normal file
99
plugins/channelrx/radioclock/radioclockbaseband.h
Normal file
@ -0,0 +1,99 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_RADIOCLOCKBASEBAND_H
|
||||
#define INCLUDE_RADIOCLOCKBASEBAND_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
#include "dsp/samplesinkfifo.h"
|
||||
#include "dsp/scopevis.h"
|
||||
#include "util/message.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "radioclocksink.h"
|
||||
|
||||
class DownChannelizer;
|
||||
class ChannelAPI;
|
||||
class RadioClock;
|
||||
class ScopeVis;
|
||||
|
||||
class RadioClockBaseband : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureRadioClockBaseband : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const RadioClockSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureRadioClockBaseband* create(const RadioClockSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureRadioClockBaseband(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
RadioClockSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureRadioClockBaseband(const RadioClockSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
RadioClockBaseband(RadioClock *radioClock);
|
||||
~RadioClockBaseband();
|
||||
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;
|
||||
RadioClockSink m_sink;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
RadioClockSettings m_settings;
|
||||
ScopeVis m_scopeSink;
|
||||
bool m_running;
|
||||
QMutex m_mutex;
|
||||
|
||||
bool handleMessage(const Message& cmd);
|
||||
void applySettings(const RadioClockSettings& settings, bool force = false);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void handleData(); //!< Handle data when samples have to be processed
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOCLOCKBASEBAND_H
|
388
plugins/channelrx/radioclock/radioclockgui.cpp
Normal file
388
plugins/channelrx/radioclock/radioclockgui.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 <limits>
|
||||
#include <ctype.h>
|
||||
#include <QDockWidget>
|
||||
#include <QDebug>
|
||||
|
||||
#include "radioclockgui.h"
|
||||
|
||||
#include "device/deviceuiset.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "ui_radioclockgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "util/db.h"
|
||||
#include "gui/basicchannelsettingsdialog.h"
|
||||
#include "gui/devicestreamselectiondialog.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/glscopesettings.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "radioclock.h"
|
||||
#include "radioclocksink.h"
|
||||
|
||||
RadioClockGUI* RadioClockGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
|
||||
{
|
||||
RadioClockGUI* gui = new RadioClockGUI(pluginAPI, deviceUISet, rxChannel);
|
||||
return gui;
|
||||
}
|
||||
|
||||
void RadioClockGUI::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void RadioClockGUI::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
QByteArray RadioClockGUI::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool RadioClockGUI::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockGUI::displayDateTime()
|
||||
{
|
||||
QDateTime dateTime = m_dateTime;
|
||||
if (m_settings.m_timezone == RadioClockSettings::UTC) {
|
||||
dateTime = dateTime.toUTC();
|
||||
} else if (m_settings.m_timezone == RadioClockSettings::LOCAL) {
|
||||
dateTime = dateTime.toLocalTime();
|
||||
}
|
||||
ui->date->setText(dateTime.date().toString());
|
||||
ui->time->setText(dateTime.time().toString());
|
||||
}
|
||||
|
||||
bool RadioClockGUI::handleMessage(const Message& message)
|
||||
{
|
||||
if (RadioClock::MsgConfigureRadioClock::match(message))
|
||||
{
|
||||
qDebug("RadioClockGUI::handleMessage: RadioClock::MsgConfigureRadioClock");
|
||||
const RadioClock::MsgConfigureRadioClock& cfg = (RadioClock::MsgConfigureRadioClock&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
blockApplySettings(false);
|
||||
return true;
|
||||
}
|
||||
else if (RadioClock::MsgDateTime::match(message))
|
||||
{
|
||||
RadioClock::MsgDateTime& report = (RadioClock::MsgDateTime&) message;
|
||||
m_dateTime = report.getDateTime();
|
||||
displayDateTime();
|
||||
return true;
|
||||
}
|
||||
else if (RadioClock::MsgStatus::match(message))
|
||||
{
|
||||
RadioClock::MsgStatus& report = (RadioClock::MsgStatus&) message;
|
||||
ui->status->setText(report.getStatus());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RadioClockGUI::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = getInputMessageQueue()->pop()) != 0)
|
||||
{
|
||||
if (handleMessage(*message))
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockGUI::channelMarkerChangedByCursor()
|
||||
{
|
||||
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
|
||||
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::channelMarkerHighlightedByCursor()
|
||||
{
|
||||
setHighlighted(m_channelMarker.getHighlighted());
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_deltaFrequency_changed(qint64 value)
|
||||
{
|
||||
m_channelMarker.setCenterFrequency(value);
|
||||
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_rfBW_valueChanged(int value)
|
||||
{
|
||||
ui->rfBWText->setText(QString("%1 Hz").arg(value));
|
||||
m_channelMarker.setBandwidth(value);
|
||||
m_settings.m_rfBandwidth = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_threshold_valueChanged(int value)
|
||||
{
|
||||
ui->thresholdText->setText(QString("%1 dB").arg(value));
|
||||
m_settings.m_threshold = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_modulation_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_modulation = (RadioClockSettings::Modulation)index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_timezone_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_timezone = (RadioClockSettings::DisplayTZ)index;
|
||||
displayDateTime();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_channel1_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_scopeCh1 = index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::on_channel2_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_scopeCh2 = index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void RadioClockGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
(void) rollDown;
|
||||
}
|
||||
|
||||
void RadioClockGUI::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_radioClock->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();
|
||||
}
|
||||
|
||||
RadioClockGUI::RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
|
||||
ChannelGUI(parent),
|
||||
ui(new Ui::RadioClockGUI),
|
||||
m_pluginAPI(pluginAPI),
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_channelMarker(this),
|
||||
m_doApplySettings(true),
|
||||
m_tickCount(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
||||
|
||||
m_radioClock = reinterpret_cast<RadioClock*>(rxChannel);
|
||||
m_radioClock->setMessageQueueToGUI(getInputMessageQueue());
|
||||
|
||||
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
|
||||
|
||||
m_scopeVis = m_radioClock->getScopeSink();
|
||||
m_scopeVis->setGLScope(ui->glScope);
|
||||
m_scopeVis->setNbStreams(1);
|
||||
m_scopeVis->setLiveRate(1000);
|
||||
ui->glScope->connectTimer(MainCore::instance()->getMasterTimer());
|
||||
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
|
||||
|
||||
ui->status->setText("Looking for minute marker");
|
||||
|
||||
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("Radio Clock");
|
||||
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_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()));
|
||||
|
||||
ui->scopeContainer->setVisible(false);
|
||||
|
||||
displaySettings();
|
||||
applySettings(true);
|
||||
}
|
||||
|
||||
RadioClockGUI::~RadioClockGUI()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RadioClockGUI::blockApplySettings(bool block)
|
||||
{
|
||||
m_doApplySettings = !block;
|
||||
}
|
||||
|
||||
void RadioClockGUI::applySettings(bool force)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
RadioClock::MsgConfigureRadioClock* message = RadioClock::MsgConfigureRadioClock::create( m_settings, force);
|
||||
m_radioClock->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockGUI::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("%1 Hz").arg((int)m_settings.m_rfBandwidth));
|
||||
ui->rfBW->setValue(m_settings.m_rfBandwidth);
|
||||
|
||||
ui->thresholdText->setText(QString("%1 dB").arg(m_settings.m_threshold));
|
||||
ui->threshold->setValue(m_settings.m_threshold);
|
||||
|
||||
ui->modulation->setCurrentIndex((int)m_settings.m_modulation);
|
||||
ui->timezone->setCurrentIndex((int)m_settings.m_timezone);
|
||||
|
||||
displayStreamIndex();
|
||||
|
||||
ui->channel1->setCurrentIndex(m_settings.m_scopeCh1);
|
||||
ui->channel2->setCurrentIndex(m_settings.m_scopeCh2);
|
||||
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void RadioClockGUI::displayStreamIndex()
|
||||
{
|
||||
if (m_deviceUISet->m_deviceMIMOEngine) {
|
||||
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
|
||||
} else {
|
||||
setStreamIndicator("S"); // single channel indicator
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockGUI::leaveEvent(QEvent*)
|
||||
{
|
||||
m_channelMarker.setHighlighted(false);
|
||||
}
|
||||
|
||||
void RadioClockGUI::enterEvent(QEvent*)
|
||||
{
|
||||
m_channelMarker.setHighlighted(true);
|
||||
}
|
||||
|
||||
void RadioClockGUI::tick()
|
||||
{
|
||||
double magsqAvg, magsqPeak;
|
||||
int nbMagsqSamples;
|
||||
m_radioClock->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++;
|
||||
}
|
100
plugins/channelrx/radioclock/radioclockgui.h
Normal file
100
plugins/channelrx/radioclock/radioclockgui.h
Normal file
@ -0,0 +1,100 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_RADIOCLOCKGUI_H
|
||||
#define INCLUDE_RADIOCLOCKGUI_H
|
||||
|
||||
#include "channel/channelgui.h"
|
||||
#include "dsp/channelmarker.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "radioclocksettings.h"
|
||||
#include "radioclock.h"
|
||||
|
||||
class PluginAPI;
|
||||
class DeviceUISet;
|
||||
class BasebandSampleSink;
|
||||
class ScopeVis;
|
||||
class ScopeVisXY;
|
||||
class RadioClock;
|
||||
class RadioClockGUI;
|
||||
|
||||
namespace Ui {
|
||||
class RadioClockGUI;
|
||||
}
|
||||
class RadioClockGUI;
|
||||
|
||||
class RadioClockGUI : public ChannelGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static RadioClockGUI* 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::RadioClockGUI* ui;
|
||||
PluginAPI* m_pluginAPI;
|
||||
DeviceUISet* m_deviceUISet;
|
||||
ChannelMarker m_channelMarker;
|
||||
RadioClockSettings m_settings;
|
||||
bool m_doApplySettings;
|
||||
ScopeVis* m_scopeVis;
|
||||
|
||||
RadioClock* m_radioClock;
|
||||
uint32_t m_tickCount;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
QDateTime m_dateTime;
|
||||
|
||||
explicit RadioClockGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~RadioClockGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displayStreamIndex();
|
||||
bool handleMessage(const Message& message);
|
||||
void displayDateTime();
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_rfBW_valueChanged(int index);
|
||||
void on_threshold_valueChanged(int value);
|
||||
void on_modulation_currentIndexChanged(int index);
|
||||
void on_timezone_currentIndexChanged(int index);
|
||||
void on_channel1_currentIndexChanged(int index);
|
||||
void on_channel2_currentIndexChanged(int index);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void handleInputMessages();
|
||||
void tick();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOCLOCKGUI_H
|
788
plugins/channelrx/radioclock/radioclockgui.ui
Normal file
788
plugins/channelrx/radioclock/radioclockgui.ui
Normal file
@ -0,0 +1,788 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RadioClockGUI</class>
|
||||
<widget class="RollupWidget" name="RadioClockGUI">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>399</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>352</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Radio Clock</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Radio Clock</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>390</width>
|
||||
<height>151</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Settings</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>
|
||||
<layout class="QHBoxLayout" name="powLayout">
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="deltaFrequencyLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Df</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDialZ" name="deltaFrequency" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Demod shift frequency from center in Hz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="deltaUnits">
|
||||
<property name="text">
|
||||
<string>Hz </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="channelPowerLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPower">
|
||||
<property name="toolTip">
|
||||
<string>Channel power</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPowerUnits">
|
||||
<property name="text">
|
||||
<string> dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="powerLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPowerMeterUnits">
|
||||
<property name="text">
|
||||
<string>dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LevelMeterSignalDB" name="channelPowerMeter" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Level meter (dB) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="demodLayout1">
|
||||
<item>
|
||||
<widget class="QLabel" name="rfBWLabel">
|
||||
<property name="text">
|
||||
<string>BW</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="rfBW">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>RF bandwidth</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="rfBWText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>100 Hz</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="thresholdLabel">
|
||||
<property name="text">
|
||||
<string>TH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="threshold">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Data threshold in dB below average carrier level</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="thresholdText">
|
||||
<property name="text">
|
||||
<string>20 dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="demodLayout2">
|
||||
<item>
|
||||
<widget class="QLabel" name="modulationLabel">
|
||||
<property name="text">
|
||||
<string>Modulation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="modulation">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Modulation of the radio clock transmission</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MSF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DCF77</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>TDF</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="timezoneLabel">
|
||||
<property name="text">
|
||||
<string>Display Timezone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="timezone">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Timezone to display the received time in</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Broadcast</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Local</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>UTC</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="clockContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>160</y>
|
||||
<width>391</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Clock</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<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>
|
||||
<layout class="QHBoxLayout" name="clockLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="dateLabel">
|
||||
<property name="text">
|
||||
<string>Date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="date">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Date</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="timeLabel">
|
||||
<property name="text">
|
||||
<string>Time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="time">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Time</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="statusLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>Status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="status">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Demodulator status</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="scopeContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>250</y>
|
||||
<width>351</width>
|
||||
<height>341</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Waveforms</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="transmittedLayout_2">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="scopelLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channel1Label">
|
||||
<property name="text">
|
||||
<string>Real</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="channel1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Signal to feed to scope as stream 0 real data</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>I</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mag Sq</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mag Sq LPF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Threshold</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FM demod LPF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Data</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Sample</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Got minute marker</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channel2Label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Imag</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="channel2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Signal to feed to scope as stream 0 imag data</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Q</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mag Sq</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mag Sq LPF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Threshold</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FM Demod LPF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Data</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Sample</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Got minute marker</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLScope" name="glScope" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>250</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="GLScopeGUI" name="scopeGUI" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RollupWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/rollupwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ValueDialZ</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedialz.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LevelMeterSignalDB</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/levelmeter.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLScope</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glscope.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GLScopeGUI</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/glscopegui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>deltaFrequency</tabstop>
|
||||
<tabstop>rfBW</tabstop>
|
||||
<tabstop>threshold</tabstop>
|
||||
<tabstop>modulation</tabstop>
|
||||
<tabstop>timezone</tabstop>
|
||||
<tabstop>date</tabstop>
|
||||
<tabstop>time</tabstop>
|
||||
<tabstop>channel1</tabstop>
|
||||
<tabstop>channel2</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
92
plugins/channelrx/radioclock/radioclockplugin.cpp
Normal file
92
plugins/channelrx/radioclock/radioclockplugin.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 "radioclockgui.h"
|
||||
#endif
|
||||
#include "radioclock.h"
|
||||
#include "radioclockwebapiadapter.h"
|
||||
#include "radioclockplugin.h"
|
||||
|
||||
const PluginDescriptor RadioClockPlugin::m_pluginDescriptor = {
|
||||
RadioClock::m_channelId,
|
||||
QStringLiteral("Radio Clock"),
|
||||
QStringLiteral("6.14.1"),
|
||||
QStringLiteral("(c) Jon Beniston, M7RCE"),
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QStringLiteral("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
RadioClockPlugin::RadioClockPlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(0)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& RadioClockPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void RadioClockPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
m_pluginAPI = pluginAPI;
|
||||
|
||||
m_pluginAPI->registerRxChannel(RadioClock::m_channelIdURI, RadioClock::m_channelId, this);
|
||||
}
|
||||
|
||||
void RadioClockPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
|
||||
{
|
||||
if (bs || cs)
|
||||
{
|
||||
RadioClock *instance = new RadioClock(deviceAPI);
|
||||
|
||||
if (bs) {
|
||||
*bs = instance;
|
||||
}
|
||||
|
||||
if (cs) {
|
||||
*cs = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
ChannelGUI* RadioClockPlugin::createRxChannelGUI(
|
||||
DeviceUISet *deviceUISet,
|
||||
BasebandSampleSink *rxChannel) const
|
||||
{
|
||||
(void) deviceUISet;
|
||||
(void) rxChannel;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
ChannelGUI* RadioClockPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
|
||||
{
|
||||
return RadioClockGUI::create(m_pluginAPI, deviceUISet, rxChannel);
|
||||
}
|
||||
#endif
|
||||
|
||||
ChannelWebAPIAdapter* RadioClockPlugin::createChannelWebAPIAdapter() const
|
||||
{
|
||||
return new RadioClockWebAPIAdapter();
|
||||
}
|
49
plugins/channelrx/radioclock/radioclockplugin.h
Normal file
49
plugins/channelrx/radioclock/radioclockplugin.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_RADIOCLOCKPLUGIN_H
|
||||
#define INCLUDE_RADIOCLOCKPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class DeviceUISet;
|
||||
class BasebandSampleSink;
|
||||
|
||||
class RadioClockPlugin : public QObject, PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.channel.radioclock")
|
||||
|
||||
public:
|
||||
explicit RadioClockPlugin(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_RADIOCLOCKPLUGIN_H
|
137
plugins/channelrx/radioclock/radioclocksettings.cpp
Normal file
137
plugins/channelrx/radioclock/radioclocksettings.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "radioclocksettings.h"
|
||||
|
||||
RadioClockSettings::RadioClockSettings() :
|
||||
m_channelMarker(0),
|
||||
m_scopeGUI(0)
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
void RadioClockSettings::resetToDefaults()
|
||||
{
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_rfBandwidth = 50.0f;
|
||||
m_threshold = 5;
|
||||
m_modulation = MSF;
|
||||
m_timezone = BROADCAST;
|
||||
m_scopeCh1 = 2;
|
||||
m_scopeCh2 = 3;
|
||||
m_rgbColor = QColor(102, 0, 0).rgb();
|
||||
m_title = "Radio Clock";
|
||||
m_streamIndex = 0;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
m_reverseAPIChannelIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray RadioClockSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_inputFrequencyOffset);
|
||||
s.writeFloat(2, m_rfBandwidth);
|
||||
s.writeFloat(4, m_threshold);
|
||||
s.writeS32(5, (int)m_modulation);
|
||||
s.writeS32(6, (int)m_timezone);
|
||||
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());
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool RadioClockSettings::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, 50.0f);
|
||||
d.readFloat(4, &m_threshold, 30);
|
||||
d.readS32(5, (int *)&m_modulation, DCF77);
|
||||
d.readS32(6, (int *)&m_timezone, BROADCAST);
|
||||
d.readS32(10, &m_scopeCh1, 2);
|
||||
d.readS32(11, &m_scopeCh2, 3);
|
||||
d.readU32(12, &m_rgbColor, QColor(102, 0, 0).rgb());
|
||||
d.readString(13, &m_title, "Radio Clock");
|
||||
d.readBlob(14, &bytetmp);
|
||||
if (m_channelMarker) {
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
66
plugins/channelrx/radioclock/radioclocksettings.h
Normal file
66
plugins/channelrx/radioclock/radioclocksettings.h
Normal file
@ -0,0 +1,66 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_RADIOCLOCKSETTINGS_H
|
||||
#define INCLUDE_RADIOCLOCKSETTINGS_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
|
||||
class Serializable;
|
||||
|
||||
struct RadioClockSettings
|
||||
{
|
||||
qint32 m_inputFrequencyOffset;
|
||||
Real m_rfBandwidth;
|
||||
Real m_threshold; //!< For MSF and DCF in dB
|
||||
enum Modulation {
|
||||
MSF,
|
||||
DCF77,
|
||||
TDF
|
||||
} m_modulation;
|
||||
enum DisplayTZ {
|
||||
BROADCAST,
|
||||
LOCAL,
|
||||
UTC
|
||||
} m_timezone;
|
||||
int m_scopeCh1;
|
||||
int m_scopeCh2;
|
||||
|
||||
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;
|
||||
|
||||
RadioClockSettings();
|
||||
void resetToDefaults();
|
||||
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
|
||||
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_RADIOCLOCKSETTINGS_H */
|
714
plugins/channelrx/radioclock/radioclocksink.cpp
Normal file
714
plugins/channelrx/radioclock/radioclocksink.cpp
Normal file
@ -0,0 +1,714 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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/scopevis.h"
|
||||
#include "util/db.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "radioclock.h"
|
||||
#include "radioclocksink.h"
|
||||
|
||||
RadioClockSink::RadioClockSink(RadioClock *radioClock) :
|
||||
m_scopeSink(nullptr),
|
||||
m_radioClock(radioClock),
|
||||
m_channelSampleRate(RADIOCLOCK_CHANNEL_SAMPLE_RATE),
|
||||
m_channelFrequencyOffset(0),
|
||||
m_magsq(0.0),
|
||||
m_magsqSum(0.0),
|
||||
m_magsqPeak(0.0),
|
||||
m_magsqCount(0),
|
||||
m_messageQueueToChannel(nullptr),
|
||||
m_data(0),
|
||||
m_prevData(0),
|
||||
m_sample(0),
|
||||
m_lowCount(0),
|
||||
m_highCount(0),
|
||||
m_periodCount(0),
|
||||
m_gotMinuteMarker(false),
|
||||
m_second(0),
|
||||
m_zeroCount(0)
|
||||
{
|
||||
m_phaseDiscri.setFMScaling(RADIOCLOCK_CHANNEL_SAMPLE_RATE / (2.0f * 20.0/M_PI));
|
||||
applySettings(m_settings, true);
|
||||
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
||||
}
|
||||
|
||||
RadioClockSink::~RadioClockSink()
|
||||
{
|
||||
}
|
||||
|
||||
void RadioClockSink::setScopeSink(ScopeVis* scopeSink)
|
||||
{
|
||||
m_scopeSink = scopeSink;
|
||||
}
|
||||
|
||||
void RadioClockSink::sampleToScope(Complex sample)
|
||||
{
|
||||
if (m_scopeSink)
|
||||
{
|
||||
Real r = std::real(sample) * SDR_RX_SCALEF;
|
||||
Real i = std::imag(sample) * SDR_RX_SCALEF;
|
||||
SampleVector m_sampleBuffer1;
|
||||
m_sampleBuffer1.push_back(Sample(r, i));
|
||||
std::vector<SampleVector::const_iterator> vbegin;
|
||||
vbegin.push_back(m_sampleBuffer1.begin());
|
||||
m_scopeSink->feed(vbegin, m_sampleBuffer1.end() - m_sampleBuffer1.begin());
|
||||
m_sampleBuffer1.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioClockSink::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract binary-coded decimal from time code - LSB first
|
||||
int RadioClockSink::bcd(int firstBit, int lastBit)
|
||||
{
|
||||
const int vals[] = {1, 2, 4, 8, 10, 20, 40, 80};
|
||||
int idx = 0;
|
||||
int val = 0;
|
||||
for (int i = firstBit; i <= lastBit; i++)
|
||||
{
|
||||
if (m_timeCode[i]) {
|
||||
val += vals[idx];
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// Extract binary-coded decimal from time code - MSB first
|
||||
int RadioClockSink::bcdMSB(int firstBit, int lastBit)
|
||||
{
|
||||
const int vals[] = {1, 2, 4, 8, 10, 20, 40, 80};
|
||||
int idx = 0;
|
||||
int val = 0;
|
||||
for (int i = lastBit; i >= firstBit; i--)
|
||||
{
|
||||
if (m_timeCode[i]) {
|
||||
val += vals[idx];
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// XOR bits together for parity check
|
||||
int RadioClockSink::xorBits(int firstBit, int lastBit)
|
||||
{
|
||||
int x = 0;
|
||||
for (int i = firstBit; i <= lastBit; i++)
|
||||
{
|
||||
x ^= m_timeCode[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
bool RadioClockSink::evenParity(int firstBit, int lastBit, int parityBit)
|
||||
{
|
||||
return xorBits(firstBit, lastBit) == parityBit;
|
||||
}
|
||||
|
||||
bool RadioClockSink::oddParity(int firstBit, int lastBit, int parityBit)
|
||||
{
|
||||
return xorBits(firstBit, lastBit) != parityBit;
|
||||
}
|
||||
|
||||
// German DCF77
|
||||
// https://en.wikipedia.org/wiki/DCF77
|
||||
void RadioClockSink::dcf77()
|
||||
{
|
||||
// DCF77 reduces carrier by -16.5dB
|
||||
m_threshold = m_thresholdMovingAverage.asDouble() * m_linearThreshold; // xdB below average
|
||||
m_data = m_magsq > m_threshold;
|
||||
|
||||
// Look for minute marker - 59th second carrier is held high
|
||||
if ((m_data == 0) && (m_prevData == 1))
|
||||
{
|
||||
if ( (m_highCount <= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 2)
|
||||
&& (m_highCount >= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 1.6)
|
||||
&& (m_lowCount <= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.3)
|
||||
&& (m_lowCount >= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.1)
|
||||
)
|
||||
{
|
||||
qDebug() << "RadioClockSink::dcf77 - Minute marker: (low " << m_lowCount << " high " << m_highCount << ") prev period " << m_periodCount;
|
||||
if (getMessageQueueToChannel() && !m_gotMinuteMarker) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Got minute marker"));
|
||||
}
|
||||
m_periodCount = 0;
|
||||
m_second = 0;
|
||||
m_gotMinuteMarker = true;
|
||||
m_secondMarkers = 1;
|
||||
}
|
||||
|
||||
m_lowCount = 0;
|
||||
}
|
||||
else if ((m_data == 1) && (m_prevData == 0))
|
||||
{
|
||||
m_highCount = 0;
|
||||
}
|
||||
else if (m_data == 1)
|
||||
{
|
||||
m_highCount++;
|
||||
}
|
||||
else if (m_data == 0)
|
||||
{
|
||||
m_lowCount++;
|
||||
}
|
||||
|
||||
m_sample = false;
|
||||
if (m_gotMinuteMarker)
|
||||
{
|
||||
m_periodCount++;
|
||||
if (m_periodCount == 50)
|
||||
{
|
||||
// Check we get second marker
|
||||
m_secondMarkers += m_data == 0;
|
||||
// If we see too many 1s instead of 0s, assume we've lost the signal
|
||||
if ((m_second > 10) && (m_secondMarkers / m_second < 0.7))
|
||||
{
|
||||
qDebug() << "RadioClockSink::dcf77 - Lost lock: " << m_secondMarkers << m_second;
|
||||
m_gotMinuteMarker = false;
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Looking for minute marker"));
|
||||
}
|
||||
}
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 150)
|
||||
{
|
||||
// Get data for timecode
|
||||
m_timeCode[m_second] = !m_data; // No carrier = 1, carrier = 0
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 950)
|
||||
{
|
||||
if (m_second == 59)
|
||||
{
|
||||
// Decode timecode to a time and date
|
||||
int minute = bcd(21, 27);
|
||||
int hour = bcd(29, 34);
|
||||
int day = bcd(36, 41);
|
||||
int month = bcd(45, 49);
|
||||
int year = 2000 + bcd(50, 57);
|
||||
|
||||
QString parityError;
|
||||
if (!evenParity(21, 27, m_timeCode[28])) {
|
||||
parityError = "Minute parity error";
|
||||
}
|
||||
if (!evenParity(29, 34, m_timeCode[35])) {
|
||||
parityError = "Hour parity error";
|
||||
}
|
||||
if (!evenParity(36, 57, m_timeCode[58])) {
|
||||
parityError= "Data parity error";
|
||||
}
|
||||
|
||||
if (parityError.isEmpty())
|
||||
{
|
||||
// Bit 17 indicates CEST rather than CET
|
||||
m_dateTime = QDateTime(QDate(year, month, day), QTime(hour, minute), Qt::OffsetFromUTC, m_timeCode[17] ? 2*3600 : 3600);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("OK"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create(parityError));
|
||||
}
|
||||
}
|
||||
m_second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_second++;
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
}
|
||||
|
||||
if (getMessageQueueToChannel())
|
||||
{
|
||||
RadioClock::MsgDateTime *msg = RadioClock::MsgDateTime::create(m_dateTime);
|
||||
getMessageQueueToChannel()->push(msg);
|
||||
}
|
||||
}
|
||||
else if (m_periodCount == 1000)
|
||||
{
|
||||
m_periodCount = 0;
|
||||
}
|
||||
}
|
||||
m_prevData = m_data;
|
||||
}
|
||||
|
||||
// French TDF 162kHz
|
||||
// https://en.wikipedia.org/wiki/TDF_time_signal
|
||||
// Uses phase modulation, rather than OOK
|
||||
void RadioClockSink::tdf(Complex &ci)
|
||||
{
|
||||
// FM demodulation
|
||||
double magsqRaw;
|
||||
Real deviation;
|
||||
Real fmDemod = m_phaseDiscri.phaseDiscriminatorDelta(ci, magsqRaw, deviation);
|
||||
|
||||
// Filter
|
||||
m_fmDemodMovingAverage(fmDemod);
|
||||
|
||||
// Ternary encoding
|
||||
Real avg = m_fmDemodMovingAverage.asDouble();
|
||||
if (avg >= 0.5) {
|
||||
m_data = 1;
|
||||
} else if (avg <= -0.5) {
|
||||
m_data = -1;
|
||||
} else {
|
||||
m_data = 0;
|
||||
}
|
||||
|
||||
// Look for minute marker - 59th second is not phase modulated
|
||||
if ((m_data == 1) && (m_prevData == 0))
|
||||
{
|
||||
if ( (m_zeroCount <= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 2)
|
||||
&& (m_zeroCount >= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 1)
|
||||
)
|
||||
{
|
||||
qDebug() << "RadioClockSink::tdf - Minute marker: (zero " << m_zeroCount << ") prev period " << m_periodCount;
|
||||
if (getMessageQueueToChannel() && !m_gotMinuteMarker) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Got minute marker"));
|
||||
}
|
||||
m_periodCount = 0;
|
||||
m_second = 0;
|
||||
m_gotMinuteMarker = true;
|
||||
m_secondMarkers = 1;
|
||||
}
|
||||
}
|
||||
else if ((m_data == 0) && (m_prevData != 0))
|
||||
{
|
||||
m_zeroCount = 0;
|
||||
}
|
||||
else if (m_data == 0)
|
||||
{
|
||||
m_zeroCount++;
|
||||
}
|
||||
|
||||
m_sample = false;
|
||||
if (m_gotMinuteMarker)
|
||||
{
|
||||
m_periodCount++;
|
||||
if (m_periodCount == 12)
|
||||
{
|
||||
m_bits[0] = m_data;
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 12+50)
|
||||
{
|
||||
m_bits[1] = m_data;
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 12+100)
|
||||
{
|
||||
m_bits[2] = m_data;
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 12+150)
|
||||
{
|
||||
m_bits[3] = m_data;
|
||||
m_sample = true;
|
||||
|
||||
// Check we got second marker
|
||||
m_secondMarkers += ((m_bits[0] == 1) && (m_bits[1] == -1));
|
||||
// If too many second markers are missing, assume we've lost the signal
|
||||
if ((m_second > 10) && (m_secondMarkers / m_second < 0.7))
|
||||
{
|
||||
qDebug() << "RadioClockSink::tdf - Lost lock: " << m_secondMarkers << m_second;
|
||||
m_gotMinuteMarker = false;
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Looking for minute marker"));
|
||||
}
|
||||
}
|
||||
|
||||
// No phase modulation from 50ms to 150ms is 0, pos then neg is 1
|
||||
if ((m_bits[2] == 0) && (m_bits[3] == 0)) {
|
||||
m_timeCode[m_second] = 0;
|
||||
} else if ((m_bits[2] == 1) && (m_bits[3] == -1)) {
|
||||
m_timeCode[m_second] = 1;
|
||||
} else {
|
||||
//qDebug() << "Unexpected modulation " << m_second;
|
||||
}
|
||||
}
|
||||
else if (m_periodCount == 950)
|
||||
{
|
||||
if (m_second == 59)
|
||||
{
|
||||
// Decode timecode to time and date
|
||||
int minute = bcd(21, 27);
|
||||
int hour = bcd(29, 34);
|
||||
int day = bcd(36, 41);
|
||||
int month = bcd(45, 49);
|
||||
int year = 2000 + bcd(50, 57);
|
||||
|
||||
QString parityError;
|
||||
if (!evenParity(21, 27, m_timeCode[28])) {
|
||||
parityError = "Minute parity error";
|
||||
}
|
||||
if (!evenParity(29, 34, m_timeCode[35])) {
|
||||
parityError = "Hour parity error";
|
||||
}
|
||||
if (!evenParity(36, 57, m_timeCode[58])) {
|
||||
parityError= "Data parity error";
|
||||
}
|
||||
|
||||
if (parityError.isEmpty())
|
||||
{
|
||||
// Bit 17 indicates CEST rather than CET
|
||||
m_dateTime = QDateTime(QDate(year, month, day), QTime(hour, minute), Qt::OffsetFromUTC, m_timeCode[17] ? 2*3600 : 3600);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("OK"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create(parityError));
|
||||
}
|
||||
}
|
||||
m_second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_second++;
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
}
|
||||
|
||||
if (getMessageQueueToChannel())
|
||||
{
|
||||
RadioClock::MsgDateTime *msg = RadioClock::MsgDateTime::create(m_dateTime);
|
||||
getMessageQueueToChannel()->push(msg);
|
||||
}
|
||||
}
|
||||
else if (m_periodCount == 1000)
|
||||
{
|
||||
m_periodCount = 0;
|
||||
}
|
||||
}
|
||||
m_prevData = m_data;
|
||||
}
|
||||
|
||||
// UK MSF 60kHz
|
||||
// https://www.npl.co.uk/products-services/time-frequency/msf-radio-time-signal/msf_time_date_code
|
||||
void RadioClockSink::msf60()
|
||||
{
|
||||
m_threshold = m_thresholdMovingAverage.asDouble() * m_linearThreshold; // xdB below average
|
||||
m_data = m_magsq > m_threshold;
|
||||
|
||||
// Look for minute marker - 500ms low, then 500ms high
|
||||
if ((m_data == 0) && (m_prevData == 1))
|
||||
{
|
||||
if ( (m_highCount <= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.6)
|
||||
&& (m_highCount >= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.4)
|
||||
&& (m_lowCount <= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.6)
|
||||
&& (m_lowCount >= RADIOCLOCK_CHANNEL_SAMPLE_RATE * 0.4)
|
||||
)
|
||||
{
|
||||
qDebug() << "RadioClockSink::msf60 - Minute marker: (low " << m_lowCount << " high " << m_highCount << ") prev period " << m_periodCount;
|
||||
if (getMessageQueueToChannel() && !m_gotMinuteMarker) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Got minute marker"));
|
||||
}
|
||||
m_periodCount = 0;
|
||||
m_second = 1;
|
||||
m_gotMinuteMarker = true;
|
||||
m_secondMarkers = 1;
|
||||
}
|
||||
m_lowCount = 0;
|
||||
}
|
||||
else if ((m_data == 1) && (m_prevData == 0))
|
||||
{
|
||||
m_highCount = 0;
|
||||
}
|
||||
else if (m_data == 1)
|
||||
{
|
||||
m_highCount++;
|
||||
}
|
||||
else if (m_data == 0)
|
||||
{
|
||||
m_lowCount++;
|
||||
}
|
||||
|
||||
m_sample = false;
|
||||
if (m_gotMinuteMarker)
|
||||
{
|
||||
m_periodCount++;
|
||||
if (m_periodCount == 50)
|
||||
{
|
||||
// Check we get second marker
|
||||
m_secondMarkers += m_data == 0;
|
||||
// If we see too many 1s instead of 0s, assume we've lost the signal
|
||||
if ((m_second > 10) && (m_secondMarkers / m_second < 0.7))
|
||||
{
|
||||
qDebug() << "RadioClockSink::msf60 - Lost lock: " << m_secondMarkers << m_second;
|
||||
m_gotMinuteMarker = false;
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Looking for minute marker"));
|
||||
}
|
||||
}
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 150)
|
||||
{
|
||||
// Get data bit A for timecode
|
||||
m_timeCode[m_second] = !m_data; // No carrier = 1, carrier = 0
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 250)
|
||||
{
|
||||
// Get data bit B for timecode
|
||||
m_timeCodeB[m_second] = !m_data;
|
||||
m_sample = true;
|
||||
}
|
||||
else if (m_periodCount == 950)
|
||||
{
|
||||
if (m_second == 59)
|
||||
{
|
||||
// Decode timecode to time and date
|
||||
int minute = bcdMSB(45, 51);
|
||||
int hour = bcdMSB(39, 44);
|
||||
int day = bcdMSB(30, 35);
|
||||
//int dayOfWeek = bcdMSB(36, 38);
|
||||
int month = bcdMSB(25, 29);
|
||||
int year = 2000 + bcdMSB(17, 24);
|
||||
|
||||
QString parityError;
|
||||
if (!oddParity(39, 51, m_timeCodeB[57])) {
|
||||
parityError = "Hour/minute parity error";
|
||||
}
|
||||
if (!oddParity(25, 35, m_timeCodeB[55])) {
|
||||
parityError= "Day/month parity error";
|
||||
}
|
||||
if (!oddParity(17, 24, m_timeCodeB[54])) {
|
||||
parityError = "Hour/minute parity error";
|
||||
}
|
||||
|
||||
if (parityError.isEmpty())
|
||||
{
|
||||
// Bit 58B indicates BST rather than GMT
|
||||
m_dateTime = QDateTime(QDate(year, month, day), QTime(hour, minute), Qt::OffsetFromUTC, m_timeCodeB[58] ? 1*3600 : 0);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("OK"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create(parityError));
|
||||
}
|
||||
}
|
||||
m_second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_second++;
|
||||
m_dateTime = m_dateTime.addSecs(1);
|
||||
}
|
||||
|
||||
if (getMessageQueueToChannel())
|
||||
{
|
||||
RadioClock::MsgDateTime *msg = RadioClock::MsgDateTime::create(m_dateTime);
|
||||
getMessageQueueToChannel()->push(msg);
|
||||
}
|
||||
}
|
||||
else if (m_periodCount == 1000)
|
||||
{
|
||||
m_periodCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_prevData = m_data;
|
||||
}
|
||||
|
||||
void RadioClockSink::processOneSample(Complex &ci)
|
||||
{
|
||||
Complex ca;
|
||||
|
||||
// Calculate average and peak levels for level meter
|
||||
Real re = ci.real() / SDR_RX_SCALEF;
|
||||
Real im = ci.imag() / SDR_RX_SCALEF;
|
||||
Real magsq = re*re + im*im;
|
||||
m_movingAverage(magsq);
|
||||
m_thresholdMovingAverage(magsq);
|
||||
m_magsq = m_movingAverage.asDouble();
|
||||
m_magsqSum += magsq;
|
||||
if (magsq > m_magsqPeak)
|
||||
{
|
||||
m_magsqPeak = magsq;
|
||||
}
|
||||
m_magsqCount++;
|
||||
|
||||
// Demodulate
|
||||
if (m_settings.m_modulation == RadioClockSettings::DCF77) {
|
||||
dcf77();
|
||||
} else if (m_settings.m_modulation == RadioClockSettings::TDF) {
|
||||
tdf(ci);
|
||||
} else {
|
||||
msf60();
|
||||
}
|
||||
|
||||
// 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(magsq * 1e6);
|
||||
break;
|
||||
case 2:
|
||||
scopeSample.real(m_magsq * 1e6);
|
||||
break;
|
||||
case 3:
|
||||
scopeSample.real(m_threshold * 1e6);
|
||||
break;
|
||||
case 4:
|
||||
scopeSample.real(m_fmDemodMovingAverage.asDouble());
|
||||
break;
|
||||
case 5:
|
||||
scopeSample.real(m_data);
|
||||
break;
|
||||
case 6:
|
||||
scopeSample.real(m_sample);
|
||||
break;
|
||||
case 7:
|
||||
scopeSample.real(m_gotMinuteMarker);
|
||||
break;
|
||||
}
|
||||
switch (m_settings.m_scopeCh2)
|
||||
{
|
||||
case 0:
|
||||
scopeSample.imag(ci.imag() / SDR_RX_SCALEF);
|
||||
break;
|
||||
case 1:
|
||||
scopeSample.imag(magsq * 1e6);
|
||||
break;
|
||||
case 2:
|
||||
scopeSample.imag(m_magsq * 1e6);
|
||||
break;
|
||||
case 3:
|
||||
scopeSample.imag(m_threshold * 1e6);
|
||||
break;
|
||||
case 4:
|
||||
scopeSample.imag(m_fmDemodMovingAverage.asDouble());
|
||||
break;
|
||||
case 5:
|
||||
scopeSample.imag(m_data);
|
||||
break;
|
||||
case 6:
|
||||
scopeSample.imag(m_sample);
|
||||
break;
|
||||
case 7:
|
||||
scopeSample.imag(m_gotMinuteMarker);
|
||||
break;
|
||||
}
|
||||
sampleToScope(scopeSample);
|
||||
}
|
||||
|
||||
void RadioClockSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
|
||||
{
|
||||
qDebug() << "RadioClockSink::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) RADIOCLOCK_CHANNEL_SAMPLE_RATE;
|
||||
m_interpolatorDistanceRemain = m_interpolatorDistance;
|
||||
}
|
||||
|
||||
m_channelSampleRate = channelSampleRate;
|
||||
m_channelFrequencyOffset = channelFrequencyOffset;
|
||||
}
|
||||
|
||||
void RadioClockSink::applySettings(const RadioClockSettings& settings, bool force)
|
||||
{
|
||||
qDebug() << "RadioClockSink::applySettings:"
|
||||
<< " m_rfBandwidth: " << settings.m_rfBandwidth
|
||||
<< " m_threshold: " << settings.m_threshold
|
||||
<< " m_modulation: " << settings.m_modulation
|
||||
<< " 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) RADIOCLOCK_CHANNEL_SAMPLE_RATE;
|
||||
m_interpolatorDistanceRemain = m_interpolatorDistance;
|
||||
}
|
||||
|
||||
if ((settings.m_threshold != m_settings.m_threshold) || force)
|
||||
{
|
||||
m_linearThreshold = CalcDb::powerFromdB(-settings.m_threshold);
|
||||
}
|
||||
|
||||
if ((settings.m_modulation != m_settings.m_modulation) || force)
|
||||
{
|
||||
m_gotMinuteMarker = false;
|
||||
m_lowCount = 0;
|
||||
m_highCount = 0;
|
||||
m_zeroCount = 0;
|
||||
m_second = 0;
|
||||
if (getMessageQueueToChannel()) {
|
||||
getMessageQueueToChannel()->push(RadioClock::MsgStatus::create("Looking for minute marker"));
|
||||
}
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
158
plugins/channelrx/radioclock/radioclocksink.h
Normal file
158
plugins/channelrx/radioclock/radioclocksink.h
Normal file
@ -0,0 +1,158 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_RADIOCLOCKSINK_H
|
||||
#define INCLUDE_RADIOCLOCKSINK_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "dsp/channelsamplesink.h"
|
||||
#include "dsp/phasediscri.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "dsp/firfilter.h"
|
||||
#include "dsp/fftfactory.h"
|
||||
#include "dsp/fftengine.h"
|
||||
#include "dsp/fftwindow.h"
|
||||
#include "util/movingaverage.h"
|
||||
#include "util/doublebufferfifo.h"
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "radioclocksettings.h"
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define RADIOCLOCK_CHANNEL_SAMPLE_RATE 1000
|
||||
|
||||
class ChannelAPI;
|
||||
class RadioClock;
|
||||
class ScopeVis;
|
||||
|
||||
class RadioClockSink : public ChannelSampleSink {
|
||||
public:
|
||||
RadioClockSink(RadioClock *radioClock);
|
||||
~RadioClockSink();
|
||||
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
|
||||
void setScopeSink(ScopeVis* scopeSink);
|
||||
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
||||
void applySettings(const RadioClockSettings& 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 debug waveforms
|
||||
RadioClock *m_radioClock;
|
||||
RadioClockSettings m_settings;
|
||||
ChannelAPI *m_channel;
|
||||
int m_channelSampleRate;
|
||||
int m_channelFrequencyOffset;
|
||||
|
||||
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, 40> m_movingAverage; //!< Moving average has sharpest step response of LPFs
|
||||
|
||||
MovingAverageUtil<Real, double, RADIOCLOCK_CHANNEL_SAMPLE_RATE> m_thresholdMovingAverage; // Average over 1 second
|
||||
|
||||
int m_data; //!< Demod data before clocking
|
||||
int m_prevData; //!< Previous value of m_data
|
||||
int m_sample; //!< For scope, indicates when data is sampled
|
||||
|
||||
int m_lowCount; //!< Number of consecutive 0 samples
|
||||
int m_highCount; //!< Number of consecutive 1 samples
|
||||
int m_periodCount; //!< Counts from 1-RADIOCLOCK_CHANNEL_SAMPLE_RATE
|
||||
bool m_gotMinuteMarker; //!< Minute marker has been seen
|
||||
int m_second; //!< Which second we are in
|
||||
int m_timeCode[61]; //!< Timecode from data in each second
|
||||
QDateTime m_dateTime; //!< Decoded date and time
|
||||
|
||||
int m_secondMarkers; //!< Counts number of second markers that have been seen
|
||||
|
||||
Real m_threshold; //!< Current threshold for display on scope
|
||||
Real m_linearThreshold; //!< settings.m_threshold as a linear value rather than dB
|
||||
|
||||
// MSF demod state
|
||||
int m_timeCodeB[61];
|
||||
|
||||
// TDF demod state
|
||||
PhaseDiscriminators m_phaseDiscri; // FM demodulator
|
||||
int m_zeroCount;
|
||||
MovingAverageUtil<Real, double, 10> m_fmDemodMovingAverage;
|
||||
int m_bits[4];
|
||||
|
||||
void processOneSample(Complex &ci);
|
||||
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
|
||||
void sampleToScope(Complex sample);
|
||||
int bcd(int firstBit, int lastBit);
|
||||
int bcdMSB(int firstBit, int lastBit);
|
||||
int xorBits(int firstBit, int lastBit);
|
||||
bool evenParity(int firstBit, int lastBit, int parityBit);
|
||||
bool oddParity(int firstBit, int lastBit, int parityBit);
|
||||
|
||||
void dcf77();
|
||||
void tdf(Complex &ci);
|
||||
void msf60();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOCLOCKSINK_H
|
52
plugins/channelrx/radioclock/radioclockwebapiadapter.cpp
Normal file
52
plugins/channelrx/radioclock/radioclockwebapiadapter.cpp
Normal file
@ -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 "radioclock.h"
|
||||
#include "radioclockwebapiadapter.h"
|
||||
|
||||
RadioClockWebAPIAdapter::RadioClockWebAPIAdapter()
|
||||
{}
|
||||
|
||||
RadioClockWebAPIAdapter::~RadioClockWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int RadioClockWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setRadioClockSettings(new SWGSDRangel::SWGRadioClockSettings());
|
||||
response.getRadioClockSettings()->init();
|
||||
RadioClock::webapiFormatChannelSettings(response, m_settings);
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
int RadioClockWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& channelSettingsKeys,
|
||||
SWGSDRangel::SWGChannelSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) force;
|
||||
(void) errorMessage;
|
||||
RadioClock::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
|
||||
|
||||
return 200;
|
||||
}
|
50
plugins/channelrx/radioclock/radioclockwebapiadapter.h
Normal file
50
plugins/channelrx/radioclock/radioclockwebapiadapter.h
Normal file
@ -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_RADIOCLOCK_WEBAPIADAPTER_H
|
||||
#define INCLUDE_RADIOCLOCK_WEBAPIADAPTER_H
|
||||
|
||||
#include "channel/channelwebapiadapter.h"
|
||||
#include "radioclocksettings.h"
|
||||
|
||||
/**
|
||||
* Standalone API adapter only for the settings
|
||||
*/
|
||||
class RadioClockWebAPIAdapter : public ChannelWebAPIAdapter {
|
||||
public:
|
||||
RadioClockWebAPIAdapter();
|
||||
virtual ~RadioClockWebAPIAdapter();
|
||||
|
||||
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:
|
||||
RadioClockSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_RADIOCLOCK_WEBAPIADAPTER_H
|
77
plugins/channelrx/radioclock/readme.md
Normal file
77
plugins/channelrx/radioclock/readme.md
Normal file
@ -0,0 +1,77 @@
|
||||
<h1>Radio clock plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This plugin can be used to receive the time and date as broadcast on Low Frequency (LF) by various transmitters that are tied to atomic clocks:
|
||||
|
||||
* [MSF](https://en.wikipedia.org/wiki/Time_from_NPL_(MSF)) - UK - 60kHz
|
||||
* [DCF77](https://en.wikipedia.org/wiki/DCF77) - Germany - 77.5kHz
|
||||
* [TDF](https://en.wikipedia.org/wiki/TDF_time_signal) - France - 162kHz
|
||||
|
||||
If you'd like other transmitters to be supported (such as WWVB), please upload a .sdriq file to SDRangel's [github issue tracker](https://github.com/f4exb/sdrangel/issues).
|
||||
|
||||
Typically, it will take two minutes before the time is able to be displayed (up to one minute to find the minute marker, then another minute to receive the timecode).
|
||||
|
||||
Although the atomic clocks used to transmit the timecode are extremely accurate, propagation, SDR data transfer and demodulation delays limit accuracy of the displayed time to around 1 second.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
![Radio clock plugin GUI](../../../doc/img/Radio_clock_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.
|
||||
|
||||
<h3>5: TH - Threshold</h3>
|
||||
|
||||
For MSF and DCF77, specifies the threshold in dB below the average carrier power level that determines a binary 0 or 1.
|
||||
|
||||
<h3>6: Modulation</h3>
|
||||
|
||||
Specifies the modulation and timecode encoding used:
|
||||
|
||||
* MSF - OOK (On-off keying)
|
||||
* DCF77 - OOK (On-off keying)
|
||||
* TDF - PM (Phase modulation)
|
||||
|
||||
<h3>7: Display Timezone</h3>
|
||||
|
||||
Specifies the timezone used to display the received time. This can be:
|
||||
|
||||
* Broadcast - the time is displayed as broadcast (which is typically the timezone of the country the signal is broadcast from, adjusted for summer time).
|
||||
* Local - the time is converted to the local time (as determined by your operating system's timezone).
|
||||
* UTC - the time is converted to Coordinated Universal Time.
|
||||
|
||||
<h3>8: Date</h3>
|
||||
|
||||
Displays the decoded date.
|
||||
|
||||
<h3>9: Time</h3>
|
||||
|
||||
Displays the decoded time, adjusted for the timezone set by (7).
|
||||
|
||||
<h3>10: Status</h3>
|
||||
|
||||
Displays the demodulator status. This can be:
|
||||
|
||||
* Looking for minute marker - Indicated at startup or after signal lost, while looking for the minute marker.
|
||||
* Got minute marker - Indicated after finding the minute marker and waiting for the first timecode to be received.
|
||||
* OK - Indicates timecodes are being received.
|
||||
|
||||
The date and time fields are only valid when the status indicates OK.
|
||||
|
||||
If while in the OK state several second markers are missed, the status will return to Acquiring Signal.
|
@ -3961,6 +3961,11 @@ bool WebAPIRequestMapper::getChannelSettings(
|
||||
channelSettings->setPacketModSettings(new SWGSDRangel::SWGPacketModSettings());
|
||||
channelSettings->getPacketModSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (channelSettingsKey == "RadioClockSettings")
|
||||
{
|
||||
channelSettings->setRadioClockSettings(new SWGSDRangel::SWGRadioClockSettings());
|
||||
channelSettings->getRadioClockSettings()->fromJsonObject(settingsJsonObject);
|
||||
}
|
||||
else if (channelSettingsKey == "RemoteSinkSettings")
|
||||
{
|
||||
channelSettings->setRemoteSinkSettings(new SWGSDRangel::SWGRemoteSinkSettings());
|
||||
|
@ -56,6 +56,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
|
||||
{"sdrangel.channel.packetdemod", "PacketDemodSettings"},
|
||||
{"sdrangel.channeltx.modpacket", "PacketModSettings"},
|
||||
{"sdrangel.channeltx.mod802.15.4", "IEEE_802_15_4_ModSettings"},
|
||||
{"sdrangel.channel.radioclock", "RadioClockSettings"},
|
||||
{"sdrangel.demod.remotesink", "RemoteSinkSettings"},
|
||||
{"sdrangel.channeltx.remotesource", "RemoteSourceSettings"},
|
||||
{"sdrangel.channeltx.modssb", "SSBModSettings"},
|
||||
@ -153,6 +154,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
|
||||
{"PacketMod", "PacketModSettings"},
|
||||
{"LocalSink", "LocalSinkSettings"},
|
||||
{"LocalSource", "LocalSourceSettings"},
|
||||
{"RadioClock", "RadioClockSettings"},
|
||||
{"RemoteSink", "RemoteSinkSettings"},
|
||||
{"RemoteSource", "RemoteSourceSettings"},
|
||||
{"SSBMod", "SSBModSettings"},
|
||||
|
@ -53,6 +53,8 @@ ChannelReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/NoiseFigure.yaml#/NoiseFigureReport"
|
||||
SSBDemodReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/SSBDemod.yaml#/SSBDemodReport"
|
||||
RadioClockReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioClock.yaml#/RadioClockReport"
|
||||
RemoteSourceReport:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RemoteSource.yaml#/RemoteSourceReport"
|
||||
PacketModReport:
|
||||
|
@ -79,6 +79,8 @@ ChannelSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/PacketDemod.yaml#/PacketDemodSettings"
|
||||
PacketModSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/PacketMod.yaml#/PacketModSettings"
|
||||
RadioClockSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RadioClock.yaml#/RadioClockSettings"
|
||||
RemoteSinkSettings:
|
||||
$ref: "http://swgserver:8081/api/swagger/include/RemoteSink.yaml#/RemoteSinkSettings"
|
||||
RemoteSourceSettings:
|
||||
|
52
swagger/sdrangel/api/swagger/include/RadioClock.yaml
Normal file
52
swagger/sdrangel/api/swagger/include/RadioClock.yaml
Normal file
@ -0,0 +1,52 @@
|
||||
RadioClockSettings:
|
||||
description: RadioClock
|
||||
properties:
|
||||
inputFrequencyOffset:
|
||||
description: channel center frequency shift from baseband center in Hz
|
||||
type: integer
|
||||
format: int64
|
||||
rfBandwidth:
|
||||
description: channel RF bandwidth in Hz
|
||||
type: number
|
||||
format: float
|
||||
threshold:
|
||||
type: number
|
||||
format: float
|
||||
modulation:
|
||||
description: 0 - MSF, 1 - DCF77, 2 - TDF
|
||||
type: integer
|
||||
timezone:
|
||||
description: 0 - Broadcast, 1 - Local, 2 - UTC
|
||||
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
|
||||
|
||||
RadioClockReport:
|
||||
description: RadioClock
|
||||
properties:
|
||||
channelPowerDB:
|
||||
description: power received in channel (dB)
|
||||
type: number
|
||||
format: float
|
||||
channelSampleRate:
|
||||
type: integer
|
||||
date:
|
||||
type: string
|
||||
time:
|
||||
type: string
|
@ -74,6 +74,8 @@ SWGChannelReport::SWGChannelReport() {
|
||||
m_noise_figure_report_isSet = false;
|
||||
ssb_demod_report = nullptr;
|
||||
m_ssb_demod_report_isSet = false;
|
||||
radio_clock_report = nullptr;
|
||||
m_radio_clock_report_isSet = false;
|
||||
remote_source_report = nullptr;
|
||||
m_remote_source_report_isSet = false;
|
||||
packet_mod_report = nullptr;
|
||||
@ -148,6 +150,8 @@ SWGChannelReport::init() {
|
||||
m_noise_figure_report_isSet = false;
|
||||
ssb_demod_report = new SWGSSBDemodReport();
|
||||
m_ssb_demod_report_isSet = false;
|
||||
radio_clock_report = new SWGRadioClockReport();
|
||||
m_radio_clock_report_isSet = false;
|
||||
remote_source_report = new SWGRemoteSourceReport();
|
||||
m_remote_source_report_isSet = false;
|
||||
packet_mod_report = new SWGPacketModReport();
|
||||
@ -239,6 +243,9 @@ SWGChannelReport::cleanup() {
|
||||
if(ssb_demod_report != nullptr) {
|
||||
delete ssb_demod_report;
|
||||
}
|
||||
if(radio_clock_report != nullptr) {
|
||||
delete radio_clock_report;
|
||||
}
|
||||
if(remote_source_report != nullptr) {
|
||||
delete remote_source_report;
|
||||
}
|
||||
@ -328,6 +335,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&ssb_demod_report, pJson["SSBDemodReport"], "SWGSSBDemodReport", "SWGSSBDemodReport");
|
||||
|
||||
::SWGSDRangel::setValue(&radio_clock_report, pJson["RadioClockReport"], "SWGRadioClockReport", "SWGRadioClockReport");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_source_report, pJson["RemoteSourceReport"], "SWGRemoteSourceReport", "SWGRemoteSourceReport");
|
||||
|
||||
::SWGSDRangel::setValue(&packet_mod_report, pJson["PacketModReport"], "SWGPacketModReport", "SWGPacketModReport");
|
||||
@ -433,6 +442,9 @@ SWGChannelReport::asJsonObject() {
|
||||
if((ssb_demod_report != nullptr) && (ssb_demod_report->isSet())){
|
||||
toJsonValue(QString("SSBDemodReport"), ssb_demod_report, obj, QString("SWGSSBDemodReport"));
|
||||
}
|
||||
if((radio_clock_report != nullptr) && (radio_clock_report->isSet())){
|
||||
toJsonValue(QString("RadioClockReport"), radio_clock_report, obj, QString("SWGRadioClockReport"));
|
||||
}
|
||||
if((remote_source_report != nullptr) && (remote_source_report->isSet())){
|
||||
toJsonValue(QString("RemoteSourceReport"), remote_source_report, obj, QString("SWGRemoteSourceReport"));
|
||||
}
|
||||
@ -697,6 +709,16 @@ SWGChannelReport::setSsbDemodReport(SWGSSBDemodReport* ssb_demod_report) {
|
||||
this->m_ssb_demod_report_isSet = true;
|
||||
}
|
||||
|
||||
SWGRadioClockReport*
|
||||
SWGChannelReport::getRadioClockReport() {
|
||||
return radio_clock_report;
|
||||
}
|
||||
void
|
||||
SWGChannelReport::setRadioClockReport(SWGRadioClockReport* radio_clock_report) {
|
||||
this->radio_clock_report = radio_clock_report;
|
||||
this->m_radio_clock_report_isSet = true;
|
||||
}
|
||||
|
||||
SWGRemoteSourceReport*
|
||||
SWGChannelReport::getRemoteSourceReport() {
|
||||
return remote_source_report;
|
||||
@ -871,6 +893,9 @@ SWGChannelReport::isSet(){
|
||||
if(ssb_demod_report && ssb_demod_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(radio_clock_report && radio_clock_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(remote_source_report && remote_source_report->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "SWGNFMModReport.h"
|
||||
#include "SWGNoiseFigureReport.h"
|
||||
#include "SWGPacketModReport.h"
|
||||
#include "SWGRadioClockReport.h"
|
||||
#include "SWGRemoteSourceReport.h"
|
||||
#include "SWGSSBDemodReport.h"
|
||||
#include "SWGSSBModReport.h"
|
||||
@ -142,6 +143,9 @@ public:
|
||||
SWGSSBDemodReport* getSsbDemodReport();
|
||||
void setSsbDemodReport(SWGSSBDemodReport* ssb_demod_report);
|
||||
|
||||
SWGRadioClockReport* getRadioClockReport();
|
||||
void setRadioClockReport(SWGRadioClockReport* radio_clock_report);
|
||||
|
||||
SWGRemoteSourceReport* getRemoteSourceReport();
|
||||
void setRemoteSourceReport(SWGRemoteSourceReport* remote_source_report);
|
||||
|
||||
@ -245,6 +249,9 @@ private:
|
||||
SWGSSBDemodReport* ssb_demod_report;
|
||||
bool m_ssb_demod_report_isSet;
|
||||
|
||||
SWGRadioClockReport* radio_clock_report;
|
||||
bool m_radio_clock_report_isSet;
|
||||
|
||||
SWGRemoteSourceReport* remote_source_report;
|
||||
bool m_remote_source_report_isSet;
|
||||
|
||||
|
@ -98,6 +98,8 @@ SWGChannelSettings::SWGChannelSettings() {
|
||||
m_packet_demod_settings_isSet = false;
|
||||
packet_mod_settings = nullptr;
|
||||
m_packet_mod_settings_isSet = false;
|
||||
radio_clock_settings = nullptr;
|
||||
m_radio_clock_settings_isSet = false;
|
||||
remote_sink_settings = nullptr;
|
||||
m_remote_sink_settings_isSet = false;
|
||||
remote_source_settings = nullptr;
|
||||
@ -198,6 +200,8 @@ SWGChannelSettings::init() {
|
||||
m_packet_demod_settings_isSet = false;
|
||||
packet_mod_settings = new SWGPacketModSettings();
|
||||
m_packet_mod_settings_isSet = false;
|
||||
radio_clock_settings = new SWGRadioClockSettings();
|
||||
m_radio_clock_settings_isSet = false;
|
||||
remote_sink_settings = new SWGRemoteSinkSettings();
|
||||
m_remote_sink_settings_isSet = false;
|
||||
remote_source_settings = new SWGRemoteSourceSettings();
|
||||
@ -323,6 +327,9 @@ SWGChannelSettings::cleanup() {
|
||||
if(packet_mod_settings != nullptr) {
|
||||
delete packet_mod_settings;
|
||||
}
|
||||
if(radio_clock_settings != nullptr) {
|
||||
delete radio_clock_settings;
|
||||
}
|
||||
if(remote_sink_settings != nullptr) {
|
||||
delete remote_sink_settings;
|
||||
}
|
||||
@ -439,6 +446,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&packet_mod_settings, pJson["PacketModSettings"], "SWGPacketModSettings", "SWGPacketModSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&radio_clock_settings, pJson["RadioClockSettings"], "SWGRadioClockSettings", "SWGRadioClockSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_sink_settings, pJson["RemoteSinkSettings"], "SWGRemoteSinkSettings", "SWGRemoteSinkSettings");
|
||||
|
||||
::SWGSDRangel::setValue(&remote_source_settings, pJson["RemoteSourceSettings"], "SWGRemoteSourceSettings", "SWGRemoteSourceSettings");
|
||||
@ -582,6 +591,9 @@ SWGChannelSettings::asJsonObject() {
|
||||
if((packet_mod_settings != nullptr) && (packet_mod_settings->isSet())){
|
||||
toJsonValue(QString("PacketModSettings"), packet_mod_settings, obj, QString("SWGPacketModSettings"));
|
||||
}
|
||||
if((radio_clock_settings != nullptr) && (radio_clock_settings->isSet())){
|
||||
toJsonValue(QString("RadioClockSettings"), radio_clock_settings, obj, QString("SWGRadioClockSettings"));
|
||||
}
|
||||
if((remote_sink_settings != nullptr) && (remote_sink_settings->isSet())){
|
||||
toJsonValue(QString("RemoteSinkSettings"), remote_sink_settings, obj, QString("SWGRemoteSinkSettings"));
|
||||
}
|
||||
@ -969,6 +981,16 @@ SWGChannelSettings::setPacketModSettings(SWGPacketModSettings* packet_mod_settin
|
||||
this->m_packet_mod_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRadioClockSettings*
|
||||
SWGChannelSettings::getRadioClockSettings() {
|
||||
return radio_clock_settings;
|
||||
}
|
||||
void
|
||||
SWGChannelSettings::setRadioClockSettings(SWGRadioClockSettings* radio_clock_settings) {
|
||||
this->radio_clock_settings = radio_clock_settings;
|
||||
this->m_radio_clock_settings_isSet = true;
|
||||
}
|
||||
|
||||
SWGRemoteSinkSettings*
|
||||
SWGChannelSettings::getRemoteSinkSettings() {
|
||||
return remote_sink_settings;
|
||||
@ -1189,6 +1211,9 @@ SWGChannelSettings::isSet(){
|
||||
if(packet_mod_settings && packet_mod_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(radio_clock_settings && radio_clock_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(remote_sink_settings && remote_sink_settings->isSet()){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "SWGNoiseFigureSettings.h"
|
||||
#include "SWGPacketDemodSettings.h"
|
||||
#include "SWGPacketModSettings.h"
|
||||
#include "SWGRadioClockSettings.h"
|
||||
#include "SWGRemoteSinkSettings.h"
|
||||
#include "SWGRemoteSourceSettings.h"
|
||||
#include "SWGSSBDemodSettings.h"
|
||||
@ -189,6 +190,9 @@ public:
|
||||
SWGPacketModSettings* getPacketModSettings();
|
||||
void setPacketModSettings(SWGPacketModSettings* packet_mod_settings);
|
||||
|
||||
SWGRadioClockSettings* getRadioClockSettings();
|
||||
void setRadioClockSettings(SWGRadioClockSettings* radio_clock_settings);
|
||||
|
||||
SWGRemoteSinkSettings* getRemoteSinkSettings();
|
||||
void setRemoteSinkSettings(SWGRemoteSinkSettings* remote_sink_settings);
|
||||
|
||||
@ -331,6 +335,9 @@ private:
|
||||
SWGPacketModSettings* packet_mod_settings;
|
||||
bool m_packet_mod_settings_isSet;
|
||||
|
||||
SWGRadioClockSettings* radio_clock_settings;
|
||||
bool m_radio_clock_settings_isSet;
|
||||
|
||||
SWGRemoteSinkSettings* remote_sink_settings;
|
||||
bool m_remote_sink_settings_isSet;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
181
swagger/sdrangel/code/qt5/client/SWGRadioClockReport.cpp
Normal file
181
swagger/sdrangel/code/qt5/client/SWGRadioClockReport.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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 "SWGRadioClockReport.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGRadioClockReport::SWGRadioClockReport(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGRadioClockReport::SWGRadioClockReport() {
|
||||
channel_power_db = 0.0f;
|
||||
m_channel_power_db_isSet = false;
|
||||
channel_sample_rate = 0;
|
||||
m_channel_sample_rate_isSet = false;
|
||||
date = nullptr;
|
||||
m_date_isSet = false;
|
||||
time = nullptr;
|
||||
m_time_isSet = false;
|
||||
}
|
||||
|
||||
SWGRadioClockReport::~SWGRadioClockReport() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockReport::init() {
|
||||
channel_power_db = 0.0f;
|
||||
m_channel_power_db_isSet = false;
|
||||
channel_sample_rate = 0;
|
||||
m_channel_sample_rate_isSet = false;
|
||||
date = new QString("");
|
||||
m_date_isSet = false;
|
||||
time = new QString("");
|
||||
m_time_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockReport::cleanup() {
|
||||
|
||||
|
||||
if(date != nullptr) {
|
||||
delete date;
|
||||
}
|
||||
if(time != nullptr) {
|
||||
delete time;
|
||||
}
|
||||
}
|
||||
|
||||
SWGRadioClockReport*
|
||||
SWGRadioClockReport::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockReport::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&date, pJson["date"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&time, pJson["time"], "QString", "QString");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
SWGRadioClockReport::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGRadioClockReport::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));
|
||||
}
|
||||
if(date != nullptr && *date != QString("")){
|
||||
toJsonValue(QString("date"), date, obj, QString("QString"));
|
||||
}
|
||||
if(time != nullptr && *time != QString("")){
|
||||
toJsonValue(QString("time"), time, obj, QString("QString"));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadioClockReport::getChannelPowerDb() {
|
||||
return channel_power_db;
|
||||
}
|
||||
void
|
||||
SWGRadioClockReport::setChannelPowerDb(float channel_power_db) {
|
||||
this->channel_power_db = channel_power_db;
|
||||
this->m_channel_power_db_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockReport::getChannelSampleRate() {
|
||||
return channel_sample_rate;
|
||||
}
|
||||
void
|
||||
SWGRadioClockReport::setChannelSampleRate(qint32 channel_sample_rate) {
|
||||
this->channel_sample_rate = channel_sample_rate;
|
||||
this->m_channel_sample_rate_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadioClockReport::getDate() {
|
||||
return date;
|
||||
}
|
||||
void
|
||||
SWGRadioClockReport::setDate(QString* date) {
|
||||
this->date = date;
|
||||
this->m_date_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadioClockReport::getTime() {
|
||||
return time;
|
||||
}
|
||||
void
|
||||
SWGRadioClockReport::setTime(QString* time) {
|
||||
this->time = time;
|
||||
this->m_time_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGRadioClockReport::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(m_channel_power_db_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_channel_sample_rate_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(date && *date != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(time && *time != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
}
|
||||
|
77
swagger/sdrangel/code/qt5/client/SWGRadioClockReport.h
Normal file
77
swagger/sdrangel/code/qt5/client/SWGRadioClockReport.h
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGRadioClockReport.h
|
||||
*
|
||||
* RadioClock
|
||||
*/
|
||||
|
||||
#ifndef SWGRadioClockReport_H_
|
||||
#define SWGRadioClockReport_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGRadioClockReport: public SWGObject {
|
||||
public:
|
||||
SWGRadioClockReport();
|
||||
SWGRadioClockReport(QString* json);
|
||||
virtual ~SWGRadioClockReport();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGRadioClockReport* fromJson(QString &jsonString) override;
|
||||
|
||||
float getChannelPowerDb();
|
||||
void setChannelPowerDb(float channel_power_db);
|
||||
|
||||
qint32 getChannelSampleRate();
|
||||
void setChannelSampleRate(qint32 channel_sample_rate);
|
||||
|
||||
QString* getDate();
|
||||
void setDate(QString* date);
|
||||
|
||||
QString* getTime();
|
||||
void setTime(QString* time);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
QString* date;
|
||||
bool m_date_isSet;
|
||||
|
||||
QString* time;
|
||||
bool m_time_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SWGRadioClockReport_H_ */
|
388
swagger/sdrangel/code/qt5/client/SWGRadioClockSettings.cpp
Normal file
388
swagger/sdrangel/code/qt5/client/SWGRadioClockSettings.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
/**
|
||||
* 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 "SWGRadioClockSettings.h"
|
||||
|
||||
#include "SWGHelpers.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
SWGRadioClockSettings::SWGRadioClockSettings(QString* json) {
|
||||
init();
|
||||
this->fromJson(*json);
|
||||
}
|
||||
|
||||
SWGRadioClockSettings::SWGRadioClockSettings() {
|
||||
input_frequency_offset = 0L;
|
||||
m_input_frequency_offset_isSet = false;
|
||||
rf_bandwidth = 0.0f;
|
||||
m_rf_bandwidth_isSet = false;
|
||||
threshold = 0.0f;
|
||||
m_threshold_isSet = false;
|
||||
modulation = 0;
|
||||
m_modulation_isSet = false;
|
||||
timezone = 0;
|
||||
m_timezone_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;
|
||||
}
|
||||
|
||||
SWGRadioClockSettings::~SWGRadioClockSettings() {
|
||||
this->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockSettings::init() {
|
||||
input_frequency_offset = 0L;
|
||||
m_input_frequency_offset_isSet = false;
|
||||
rf_bandwidth = 0.0f;
|
||||
m_rf_bandwidth_isSet = false;
|
||||
threshold = 0.0f;
|
||||
m_threshold_isSet = false;
|
||||
modulation = 0;
|
||||
m_modulation_isSet = false;
|
||||
timezone = 0;
|
||||
m_timezone_isSet = false;
|
||||
rgb_color = 0;
|
||||
m_rgb_color_isSet = false;
|
||||
title = new QString("");
|
||||
m_title_isSet = false;
|
||||
stream_index = 0;
|
||||
m_stream_index_isSet = false;
|
||||
use_reverse_api = 0;
|
||||
m_use_reverse_api_isSet = false;
|
||||
reverse_api_address = new QString("");
|
||||
m_reverse_api_address_isSet = false;
|
||||
reverse_api_port = 0;
|
||||
m_reverse_api_port_isSet = false;
|
||||
reverse_api_device_index = 0;
|
||||
m_reverse_api_device_index_isSet = false;
|
||||
reverse_api_channel_index = 0;
|
||||
m_reverse_api_channel_index_isSet = false;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockSettings::cleanup() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(title != nullptr) {
|
||||
delete title;
|
||||
}
|
||||
|
||||
|
||||
if(reverse_api_address != nullptr) {
|
||||
delete reverse_api_address;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
SWGRadioClockSettings*
|
||||
SWGRadioClockSettings::fromJson(QString &json) {
|
||||
QByteArray array (json.toStdString().c_str());
|
||||
QJsonDocument doc = QJsonDocument::fromJson(array);
|
||||
QJsonObject jsonObject = doc.object();
|
||||
this->fromJsonObject(jsonObject);
|
||||
return this;
|
||||
}
|
||||
|
||||
void
|
||||
SWGRadioClockSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
|
||||
|
||||
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&threshold, pJson["threshold"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&modulation, pJson["modulation"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&timezone, pJson["timezone"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
|
||||
|
||||
}
|
||||
|
||||
QString
|
||||
SWGRadioClockSettings::asJson ()
|
||||
{
|
||||
QJsonObject* obj = this->asJsonObject();
|
||||
|
||||
QJsonDocument doc(*obj);
|
||||
QByteArray bytes = doc.toJson();
|
||||
delete obj;
|
||||
return QString(bytes);
|
||||
}
|
||||
|
||||
QJsonObject*
|
||||
SWGRadioClockSettings::asJsonObject() {
|
||||
QJsonObject* obj = new QJsonObject();
|
||||
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_threshold_isSet){
|
||||
obj->insert("threshold", QJsonValue(threshold));
|
||||
}
|
||||
if(m_modulation_isSet){
|
||||
obj->insert("modulation", QJsonValue(modulation));
|
||||
}
|
||||
if(m_timezone_isSet){
|
||||
obj->insert("timezone", QJsonValue(timezone));
|
||||
}
|
||||
if(m_rgb_color_isSet){
|
||||
obj->insert("rgbColor", QJsonValue(rgb_color));
|
||||
}
|
||||
if(title != nullptr && *title != QString("")){
|
||||
toJsonValue(QString("title"), title, obj, QString("QString"));
|
||||
}
|
||||
if(m_stream_index_isSet){
|
||||
obj->insert("streamIndex", QJsonValue(stream_index));
|
||||
}
|
||||
if(m_use_reverse_api_isSet){
|
||||
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
|
||||
}
|
||||
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
|
||||
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
|
||||
}
|
||||
if(m_reverse_api_port_isSet){
|
||||
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
|
||||
}
|
||||
if(m_reverse_api_device_index_isSet){
|
||||
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
|
||||
}
|
||||
if(m_reverse_api_channel_index_isSet){
|
||||
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
qint64
|
||||
SWGRadioClockSettings::getInputFrequencyOffset() {
|
||||
return input_frequency_offset;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
|
||||
this->input_frequency_offset = input_frequency_offset;
|
||||
this->m_input_frequency_offset_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadioClockSettings::getRfBandwidth() {
|
||||
return rf_bandwidth;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setRfBandwidth(float rf_bandwidth) {
|
||||
this->rf_bandwidth = rf_bandwidth;
|
||||
this->m_rf_bandwidth_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGRadioClockSettings::getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setThreshold(float threshold) {
|
||||
this->threshold = threshold;
|
||||
this->m_threshold_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getModulation() {
|
||||
return modulation;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setModulation(qint32 modulation) {
|
||||
this->modulation = modulation;
|
||||
this->m_modulation_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getTimezone() {
|
||||
return timezone;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setTimezone(qint32 timezone) {
|
||||
this->timezone = timezone;
|
||||
this->m_timezone_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getRgbColor() {
|
||||
return rgb_color;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setRgbColor(qint32 rgb_color) {
|
||||
this->rgb_color = rgb_color;
|
||||
this->m_rgb_color_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadioClockSettings::getTitle() {
|
||||
return title;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setTitle(QString* title) {
|
||||
this->title = title;
|
||||
this->m_title_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getStreamIndex() {
|
||||
return stream_index;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setStreamIndex(qint32 stream_index) {
|
||||
this->stream_index = stream_index;
|
||||
this->m_stream_index_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getUseReverseApi() {
|
||||
return use_reverse_api;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setUseReverseApi(qint32 use_reverse_api) {
|
||||
this->use_reverse_api = use_reverse_api;
|
||||
this->m_use_reverse_api_isSet = true;
|
||||
}
|
||||
|
||||
QString*
|
||||
SWGRadioClockSettings::getReverseApiAddress() {
|
||||
return reverse_api_address;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setReverseApiAddress(QString* reverse_api_address) {
|
||||
this->reverse_api_address = reverse_api_address;
|
||||
this->m_reverse_api_address_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getReverseApiPort() {
|
||||
return reverse_api_port;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setReverseApiPort(qint32 reverse_api_port) {
|
||||
this->reverse_api_port = reverse_api_port;
|
||||
this->m_reverse_api_port_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getReverseApiDeviceIndex() {
|
||||
return reverse_api_device_index;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
|
||||
this->reverse_api_device_index = reverse_api_device_index;
|
||||
this->m_reverse_api_device_index_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGRadioClockSettings::getReverseApiChannelIndex() {
|
||||
return reverse_api_channel_index;
|
||||
}
|
||||
void
|
||||
SWGRadioClockSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
|
||||
this->reverse_api_channel_index = reverse_api_channel_index;
|
||||
this->m_reverse_api_channel_index_isSet = true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SWGRadioClockSettings::isSet(){
|
||||
bool isObjectUpdated = false;
|
||||
do{
|
||||
if(m_input_frequency_offset_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_rf_bandwidth_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_threshold_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_modulation_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_timezone_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_rgb_color_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(title && *title != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_stream_index_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_use_reverse_api_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(reverse_api_address && *reverse_api_address != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_reverse_api_port_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_reverse_api_device_index_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_reverse_api_channel_index_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
}while(false);
|
||||
return isObjectUpdated;
|
||||
}
|
||||
}
|
||||
|
131
swagger/sdrangel/code/qt5/client/SWGRadioClockSettings.h
Normal file
131
swagger/sdrangel/code/qt5/client/SWGRadioClockSettings.h
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SWGRadioClockSettings.h
|
||||
*
|
||||
* RadioClock
|
||||
*/
|
||||
|
||||
#ifndef SWGRadioClockSettings_H_
|
||||
#define SWGRadioClockSettings_H_
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "SWGObject.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace SWGSDRangel {
|
||||
|
||||
class SWG_API SWGRadioClockSettings: public SWGObject {
|
||||
public:
|
||||
SWGRadioClockSettings();
|
||||
SWGRadioClockSettings(QString* json);
|
||||
virtual ~SWGRadioClockSettings();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
virtual QString asJson () override;
|
||||
virtual QJsonObject* asJsonObject() override;
|
||||
virtual void fromJsonObject(QJsonObject &json) override;
|
||||
virtual SWGRadioClockSettings* fromJson(QString &jsonString) override;
|
||||
|
||||
qint64 getInputFrequencyOffset();
|
||||
void setInputFrequencyOffset(qint64 input_frequency_offset);
|
||||
|
||||
float getRfBandwidth();
|
||||
void setRfBandwidth(float rf_bandwidth);
|
||||
|
||||
float getThreshold();
|
||||
void setThreshold(float threshold);
|
||||
|
||||
qint32 getModulation();
|
||||
void setModulation(qint32 modulation);
|
||||
|
||||
qint32 getTimezone();
|
||||
void setTimezone(qint32 timezone);
|
||||
|
||||
qint32 getRgbColor();
|
||||
void setRgbColor(qint32 rgb_color);
|
||||
|
||||
QString* getTitle();
|
||||
void setTitle(QString* title);
|
||||
|
||||
qint32 getStreamIndex();
|
||||
void setStreamIndex(qint32 stream_index);
|
||||
|
||||
qint32 getUseReverseApi();
|
||||
void setUseReverseApi(qint32 use_reverse_api);
|
||||
|
||||
QString* getReverseApiAddress();
|
||||
void setReverseApiAddress(QString* reverse_api_address);
|
||||
|
||||
qint32 getReverseApiPort();
|
||||
void setReverseApiPort(qint32 reverse_api_port);
|
||||
|
||||
qint32 getReverseApiDeviceIndex();
|
||||
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
|
||||
|
||||
qint32 getReverseApiChannelIndex();
|
||||
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
|
||||
|
||||
|
||||
virtual bool isSet() override;
|
||||
|
||||
private:
|
||||
qint64 input_frequency_offset;
|
||||
bool m_input_frequency_offset_isSet;
|
||||
|
||||
float rf_bandwidth;
|
||||
bool m_rf_bandwidth_isSet;
|
||||
|
||||
float threshold;
|
||||
bool m_threshold_isSet;
|
||||
|
||||
qint32 modulation;
|
||||
bool m_modulation_isSet;
|
||||
|
||||
qint32 timezone;
|
||||
bool m_timezone_isSet;
|
||||
|
||||
qint32 rgb_color;
|
||||
bool m_rgb_color_isSet;
|
||||
|
||||
QString* title;
|
||||
bool m_title_isSet;
|
||||
|
||||
qint32 stream_index;
|
||||
bool m_stream_index_isSet;
|
||||
|
||||
qint32 use_reverse_api;
|
||||
bool m_use_reverse_api_isSet;
|
||||
|
||||
QString* reverse_api_address;
|
||||
bool m_reverse_api_address_isSet;
|
||||
|
||||
qint32 reverse_api_port;
|
||||
bool m_reverse_api_port_isSet;
|
||||
|
||||
qint32 reverse_api_device_index;
|
||||
bool m_reverse_api_device_index_isSet;
|
||||
|
||||
qint32 reverse_api_channel_index;
|
||||
bool m_reverse_api_channel_index_isSet;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SWGRadioClockSettings_H_ */
|
Loading…
Reference in New Issue
Block a user