Add radio clock plugin

pull/934/head
Jon Beniston 2021-06-22 17:38:56 +01:00
rodzic 4e449b397c
commit 0faaa8397d
33 zmienionych plików z 4851 dodań i 776 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Wyświetl plik

@ -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)

Wyświetl plik

@ -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})

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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());
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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++;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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>

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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.

Wyświetl plik

@ -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());

Wyświetl plik

@ -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"},

Wyświetl plik

@ -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:

Wyświetl plik

@ -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:

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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_ */