Add DATV Modulator

This commit is contained in:
Jon Beniston 2021-03-18 17:17:58 +00:00
parent 996e4d7816
commit 37a19dee04
47 changed files with 6639 additions and 42 deletions

BIN
doc/img/DATVMod_plugin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -19,6 +19,12 @@ if (OpenCV_FOUND)
add_subdirectory(modatv)
endif()
# Copied from channelrx/CMakeLists.txt - why not in top-level?
find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
if (FFMPEG_FOUND)
add_subdirectory(moddatv)
endif()
if (CODEC2_FOUND)
add_subdirectory(modfreedv)
endif(CODEC2_FOUND)

View File

@ -28,7 +28,7 @@ IEEE_802_15_4_ModSource::IEEE_802_15_4_ModSource() :
m_channelFrequencyOffset(0),
m_spectrumRate(0),
m_sinLUT(nullptr),
m_scrambler(0x108, 0x1fe, 0),
m_scrambler(0x108, 0x1fe, 1),
m_spectrumSink(nullptr),
m_scopeSink(nullptr),
m_magsq(0.0),

View File

@ -0,0 +1,75 @@
project(moddatv)
set(moddatv_SOURCES
datvmod.cpp
datvmodbaseband.cpp
datvmodreport.cpp
datvmodsource.cpp
datvmodplugin.cpp
datvmodsettings.cpp
datvmodwebapiadapter.cpp
dvb-s/dvb-s.cpp
)
set(moddatv_HEADERS
datvmod.h
datvmodbaseband.h
datvmodreport.h
datvmodsource.h
datvmodplugin.h
datvmodsettings.h
datvmodwebapiadapter.h
dvb-s/dvb-s.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${Boost_INCLUDE_DIRS}
${AVCODEC_INCLUDE_DIRS}
${AVFORMAT_INCLUDE_DIRS}
${AVUTIL_INCLUDE_DIRS}
${SWSCALE_INCLUDE_DIRS}
${SWRESAMPLE_INCLUDE_DIRS}
)
if(NOT SERVER_MODE)
set(moddatv_SOURCES
${moddatv_SOURCES}
datvmodgui.cpp
datvmodgui.ui
)
set(moddatv_HEADERS
${moddatv_HEADERS}
datvmodgui.h
)
set(TARGET_NAME moddatv)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME moddatvsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${moddatv_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Boost::disable_autolinking
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
${AVCODEC_LIBRARIES}
${AVFORMAT_LIBRARIES}
${AVUTIL_LIBRARIES}
${SWSCALE_LIBRARIES}
${SWRESAMPLE_LIBRARIES}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,644 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
#include <stdio.h>
#include <complex.h>
#include <QTime>
#include <QDebug>
#include <QMutexLocker>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include "SWGChannelSettings.h"
#include "SWGChannelReport.h"
//#include "SWGDATVModReport.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/devicesamplemimo.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "util/db.h"
#include "maincore.h"
#include "datvmodbaseband.h"
#include "datvmod.h"
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureDATVMod, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureChannelizer, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureSourceCenterFrequency, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileName, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileSourceSeek, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileSourceStreamTiming, Message)
MESSAGE_CLASS_DEFINITION(DATVMod::MsgGetUDPBitrate, Message)
const char* const DATVMod::m_channelIdURI = "sdrangel.channeltx.moddatv";
const char* const DATVMod::m_channelId = "DATVMod";
DATVMod::DATVMod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
m_deviceAPI(deviceAPI)
{
setObjectName(m_channelId);
m_thread = new QThread(this);
m_basebandSource = new DATVModBaseband();
m_basebandSource->moveToThread(m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSource(this);
m_deviceAPI->addChannelSourceAPI(this);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
DATVMod::~DATVMod()
{
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
m_deviceAPI->removeChannelSourceAPI(this);
m_deviceAPI->removeChannelSource(this);
delete m_basebandSource;
delete m_thread;
}
uint32_t DATVMod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSinkStreams();
}
void DATVMod::start()
{
qDebug("DATVMod::start");
m_basebandSource->reset();
m_thread->start();
}
void DATVMod::stop()
{
qDebug("DATVMod::stop");
m_thread->exit();
m_thread->wait();
}
void DATVMod::pull(SampleVector::iterator& begin, unsigned int nbSamples)
{
m_basebandSource->pull(begin, nbSamples);
}
bool DATVMod::handleMessage(const Message& cmd)
{
if (MsgConfigureChannelizer::match(cmd))
{
MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd;
qDebug() << "DATVMod::handleMessage: MsgConfigureChannelizer:"
<< " getSourceSampleRate: " << cfg.getSourceSampleRate()
<< " getSourceCenterFrequency: " << cfg.getSourceCenterFrequency();
MsgConfigureChannelizer *msg
= MsgConfigureChannelizer::create(cfg.getSourceSampleRate(), cfg.getSourceCenterFrequency());
m_basebandSource->getInputMessageQueue()->push(msg);
return true;
}
else if (MsgConfigureSourceCenterFrequency::match(cmd))
{
MsgConfigureSourceCenterFrequency& cfg = (MsgConfigureSourceCenterFrequency&) cmd;
qDebug() << "DATVMod::handleMessage: MsgConfigureSourceCenterFrequency:"
<< " getSourceCenterFrequency: " << cfg.getSourceCenterFrequency();
MsgConfigureChannelizer *msg
= MsgConfigureChannelizer::create(m_basebandSource->getChannelSampleRate(), cfg.getSourceCenterFrequency());
m_basebandSource->getInputMessageQueue()->push(msg);
return true;
}
else if (MsgConfigureDATVMod::match(cmd))
{
MsgConfigureDATVMod& cfg = (MsgConfigureDATVMod&) cmd;
qDebug() << "DATVMod::handleMessage: MsgConfigureDATVMod";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
// Forward to the source
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
DSPSignalNotification* rep = new DSPSignalNotification(notif);
qDebug() << "DATVMod::handleMessage: DSPSignalNotification";
m_basebandSource->getInputMessageQueue()->push(rep);
return true;
}
else if (MsgConfigureTsFileName::match(cmd))
{
MsgConfigureTsFileName& cfg = (MsgConfigureTsFileName&) cmd;
MsgConfigureTsFileName *msg = MsgConfigureTsFileName::create(
cfg.getFileName());
m_basebandSource->getInputMessageQueue()->push(msg);
return true;
}
else if (MsgConfigureTsFileSourceSeek::match(cmd))
{
MsgConfigureTsFileSourceSeek& cfg = (MsgConfigureTsFileSourceSeek&) cmd;
MsgConfigureTsFileSourceSeek *rep = MsgConfigureTsFileSourceSeek::create(cfg.getPercentage());
m_basebandSource->getInputMessageQueue()->push(rep);
return true;
}
else if (MsgConfigureTsFileSourceStreamTiming::match(cmd))
{
MsgConfigureTsFileSourceStreamTiming *rep = MsgConfigureTsFileSourceStreamTiming::create();
m_basebandSource->getInputMessageQueue()->push(rep);
return true;
}
else if (MsgGetUDPBitrate::match(cmd))
{
m_basebandSource->getInputMessageQueue()->push(DATVMod::MsgGetUDPBitrate::create());
return true;
}
else
{
return false;
}
}
void DATVMod::applySettings(const DATVModSettings& settings, bool force)
{
qDebug() << "DATVMod::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_standard: " << (int) settings.m_standard
<< " m_source: " << (int) settings.m_source
<< " m_modulation: " << (int) settings.m_modulation
<< " m_fec: " << (int) settings.m_fec
<< " m_symbolRate: " << settings.m_symbolRate
<< " m_source: " << settings.m_source
<< " m_tsFileName: " << settings.m_tsFileName
<< " m_tsFilePlayLoop: " << settings.m_tsFilePlayLoop
<< " m_tsFilePlay: " << settings.m_tsFilePlay
<< " m_udpAddress: " << settings.m_udpAddress
<< " m_udpPort: " << settings.m_udpPort
<< " m_channelMute: " << settings.m_channelMute
<< " 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_standard != m_settings.m_standard) || force) {
reverseAPIKeys.append("standard");
}
if ((settings.m_modulation != m_settings.m_modulation) || force) {
reverseAPIKeys.append("modulation");
}
if ((settings.m_modulation != m_settings.m_modulation) || force) {
reverseAPIKeys.append("modulation");
}
if ((settings.m_fec != m_settings.m_fec) || force) {
reverseAPIKeys.append("fec");
}
if ((settings.m_symbolRate != m_settings.m_symbolRate) || force) {
reverseAPIKeys.append("symbolRate");
}
if ((settings.m_rollOff != m_settings.m_rollOff) || force) {
reverseAPIKeys.append("rollOff");
}
if ((settings.m_tsFilePlayLoop != m_settings.m_tsFilePlayLoop) || force) {
reverseAPIKeys.append("tsSource");
}
if ((settings.m_tsFileName != m_settings.m_tsFileName) || force) {
reverseAPIKeys.append("tsFileName");
}
if ((settings.m_tsFilePlayLoop != m_settings.m_tsFilePlayLoop) || force) {
reverseAPIKeys.append("tsFilePlayLoop");
}
if ((settings.m_tsFilePlay != m_settings.m_tsFilePlay) || force) {
reverseAPIKeys.append("tsFilePlay");
}
if ((settings.m_udpAddress != m_settings.m_udpAddress) || force) {
reverseAPIKeys.append("udpAddress");
}
if ((settings.m_udpPort != m_settings.m_udpPort) || force) {
reverseAPIKeys.append("udpPort");
}
if ((settings.m_channelMute != m_settings.m_channelMute) || force) {
reverseAPIKeys.append("channelMute");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSourceAPI(this);
m_deviceAPI->removeChannelSource(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSource(this, settings.m_streamIndex);
m_deviceAPI->addChannelSourceAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
MsgConfigureDATVMod *msg = MsgConfigureDATVMod::create(settings, force);
m_basebandSource->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);
}
QList<MessageQueue*> *messageQueues = MainCore::instance()->getMessagePipes().getMessageQueues(this, "settings");
if (messageQueues) {
sendChannelSettings(messageQueues, reverseAPIKeys, settings, force);
}
m_settings = settings;
}
QByteArray DATVMod::serialize() const
{
return m_settings.serialize();
}
bool DATVMod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureDATVMod *msg = MsgConfigureDATVMod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureDATVMod *msg = MsgConfigureDATVMod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int DATVMod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDatvModSettings(new SWGSDRangel::SWGDATVModSettings());
response.getDatvModSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int DATVMod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
DATVModSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
if (m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset)
{
DATVMod::MsgConfigureChannelizer *msgChan = DATVMod::MsgConfigureChannelizer::create(
m_basebandSource->getChannelSampleRate(), settings.m_inputFrequencyOffset);
m_inputMessageQueue.push(msgChan);
}
MsgConfigureDATVMod *msg = MsgConfigureDATVMod::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue)
m_guiMessageQueue->push(MsgConfigureDATVMod::create(settings, force));
if (channelSettingsKeys.contains("tsFileName"))
{
m_basebandSource->getInputMessageQueue()->push(MsgConfigureTsFileName::create(
*response.getDatvModSettings()->getTsFileName()));
if (m_guiMessageQueue)
{
m_guiMessageQueue->push(MsgConfigureTsFileName::create(
*response.getDatvModSettings()->getTsFileName()));
}
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void DATVMod::webapiUpdateChannelSettings(
DATVModSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getDatvModSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getDatvModSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("standard")) {
settings.m_standard = (DATVModSettings::DVBStandard) response.getDatvModSettings()->getStandard();
}
if (channelSettingsKeys.contains("modulation")) {
settings.m_modulation = (DATVModSettings::DATVModulation) response.getDatvModSettings()->getModulation();
}
if (channelSettingsKeys.contains("fec")) {
settings.m_fec = (DATVModSettings::DATVCodeRate) response.getDatvModSettings()->getFec();
}
if (channelSettingsKeys.contains("symbolRate")) {
settings.m_symbolRate = response.getDatvModSettings()->getSymbolRate();
}
if (channelSettingsKeys.contains("rollOff")) {
settings.m_rollOff = response.getDatvModSettings()->getRollOff();
}
if (channelSettingsKeys.contains("tsSource")) {
settings.m_source = (DATVModSettings::DATVSource) response.getDatvModSettings()->getTsSource();
}
if (channelSettingsKeys.contains("tsFileName")) {
settings.m_tsFileName = *response.getDatvModSettings()->getTsFileName();
}
if (channelSettingsKeys.contains("tsFilePlayLoop")) {
settings.m_tsFilePlayLoop = response.getDatvModSettings()->getTsFilePlayLoop() != 0;
}
if (channelSettingsKeys.contains("tsFilePlay")) {
settings.m_tsFilePlay = response.getDatvModSettings()->getTsFilePlay() != 0;
}
if (channelSettingsKeys.contains("udpAddress")) {
settings.m_udpAddress = *response.getDatvModSettings()->getUdpAddress();
}
if (channelSettingsKeys.contains("udpPort")) {
settings.m_udpPort = response.getDatvModSettings()->getUdpPort();
}
if (channelSettingsKeys.contains("channelMute")) {
settings.m_channelMute = response.getDatvModSettings()->getChannelMute() != 0;
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getDatvModSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getDatvModSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getDatvModSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getDatvModSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getDatvModSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getDatvModSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getDatvModSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getDatvModSettings()->getReverseApiChannelIndex();
}
}
int DATVMod::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDatvModReport(new SWGSDRangel::SWGDATVModReport());
response.getDatvModReport()->init();
webapiFormatChannelReport(response);
return 200;
}
void DATVMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DATVModSettings& settings)
{
response.getDatvModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getDatvModSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getDatvModSettings()->setStandard((int)settings.m_standard);
response.getDatvModSettings()->setModulation((int)settings.m_modulation);
response.getDatvModSettings()->setFec((int)settings.m_fec);
response.getDatvModSettings()->setSymbolRate(settings.m_symbolRate);
response.getDatvModSettings()->setRollOff(settings.m_rollOff);
response.getDatvModSettings()->setTsSource(settings.m_source);
response.getDatvModSettings()->setTsFileName(new QString(settings.m_tsFileName));
response.getDatvModSettings()->setTsFilePlayLoop(settings.m_tsFilePlayLoop ? 1 : 0);
response.getDatvModSettings()->setTsFilePlay(settings.m_tsFilePlay ? 1 : 0);
response.getDatvModSettings()->setUdpAddress(new QString(settings.m_udpAddress));
response.getDatvModSettings()->setUdpPort(settings.m_udpPort);
response.getDatvModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0);
response.getDatvModSettings()->setRgbColor(settings.m_rgbColor);
if (response.getDatvModSettings()->getTitle()) {
*response.getDatvModSettings()->getTitle() = settings.m_title;
} else {
response.getDatvModSettings()->setTitle(new QString(settings.m_title));
}
response.getDatvModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getDatvModSettings()->getReverseApiAddress()) {
*response.getDatvModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getDatvModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getDatvModSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getDatvModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getDatvModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
}
void DATVMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
response.getDatvModReport()->setChannelPowerDb(CalcDb::dbPower(getMagSq()));
response.getDatvModReport()->setChannelSampleRate(m_basebandSource->getChannelSampleRate());
}
void DATVMod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DATVModSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
buffer->setParent(reply);
delete swgChannelSettings;
}
void DATVMod::sendChannelSettings(
QList<MessageQueue*> *messageQueues,
QList<QString>& channelSettingsKeys,
const DATVModSettings& settings,
bool force)
{
QList<MessageQueue*>::iterator it = messageQueues->begin();
for (; it != messageQueues->end(); ++it)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
MainCore::MsgChannelSettings *msg = MainCore::MsgChannelSettings::create(
this,
channelSettingsKeys,
swgChannelSettings,
force
);
(*it)->push(msg);
}
}
void DATVMod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DATVModSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(1); // single source (Tx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString(m_channelId));
swgChannelSettings->setDatvModSettings(new SWGSDRangel::SWGDATVModSettings());
SWGSDRangel::SWGDATVModSettings *swgDATVModSettings = swgChannelSettings->getDatvModSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgDATVModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgDATVModSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("standard") || force) {
swgDATVModSettings->setStandard((int) settings.m_standard);
}
if (channelSettingsKeys.contains("modulation") || force) {
swgDATVModSettings->setModulation((int) settings.m_modulation);
}
if (channelSettingsKeys.contains("fec") || force) {
swgDATVModSettings->setFec((int) settings.m_fec);
}
if (channelSettingsKeys.contains("symbolRate") || force) {
swgDATVModSettings->setSymbolRate((int) settings.m_symbolRate);
}
if (channelSettingsKeys.contains("tsSource") || force) {
swgDATVModSettings->setTsSource((int) settings.m_source);
}
if (channelSettingsKeys.contains("tsFileName") || force) {
swgDATVModSettings->setTsFileName(new QString(settings.m_tsFileName));
}
if (channelSettingsKeys.contains("tsFilePlayLoop") || force) {
swgDATVModSettings->setTsFilePlayLoop(settings.m_tsFilePlayLoop ? 1 : 0);
}
if (channelSettingsKeys.contains("tsFilePlay") || force) {
swgDATVModSettings->setTsFilePlay(settings.m_tsFilePlay ? 1 : 0);
}
if (channelSettingsKeys.contains("udpAddress") || force) {
swgDATVModSettings->setUdpAddress(new QString(settings.m_udpAddress));
}
if (channelSettingsKeys.contains("udpPort") || force) {
swgDATVModSettings->setUdpPort(settings.m_udpPort);
}
if (channelSettingsKeys.contains("channelMute") || force) {
swgDATVModSettings->setChannelMute(settings.m_channelMute ? 1 : 0);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgDATVModSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgDATVModSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgDATVModSettings->setStreamIndex(settings.m_streamIndex);
}
}
void DATVMod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "DATVMod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("DATVMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
double DATVMod::getMagSq() const
{
return m_basebandSource->getMagSq();
}
int DATVMod::getEffectiveSampleRate() const
{
return m_basebandSource->getEffectiveSampleRate();
}
void DATVMod::setMessageQueueToGUI(MessageQueue* queue) {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSource->setMessageQueueToGUI(queue);
}

View File

@ -0,0 +1,273 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMOD_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMOD_H_
#include <vector>
#include <iostream>
#include <fstream>
#include <QMutex>
#include <QNetworkRequest>
#include "dsp/basebandsamplesource.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "datvmodsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DATVModBaseband;
class DeviceAPI;
class DATVMod : public BasebandSampleSource, public ChannelAPI {
Q_OBJECT
public:
class MsgConfigureDATVMod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DATVModSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureDATVMod* create(const DATVModSettings& settings, bool force)
{
return new MsgConfigureDATVMod(settings, force);
}
private:
DATVModSettings m_settings;
bool m_force;
MsgConfigureDATVMod(const DATVModSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgConfigureChannelizer : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSourceSampleRate() const { return m_sourceSampleRate; }
int getSourceCenterFrequency() const { return m_sourceCenterFrequency; }
static MsgConfigureChannelizer* create(int sourceSampleRate, int sourceCenterFrequency) {
return new MsgConfigureChannelizer(sourceSampleRate, sourceCenterFrequency);
}
private:
int m_sourceSampleRate;
int m_sourceCenterFrequency;
MsgConfigureChannelizer(int sourceSampleRate, int sourceCenterFrequency) :
Message(),
m_sourceSampleRate(sourceSampleRate),
m_sourceCenterFrequency(sourceCenterFrequency)
{ }
};
class MsgConfigureSourceCenterFrequency : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSourceCenterFrequency() const { return m_sourceCenterFrequency; }
static MsgConfigureSourceCenterFrequency *create(int sourceCenterFrequency) {
return new MsgConfigureSourceCenterFrequency(sourceCenterFrequency);
}
private:
int m_sourceCenterFrequency;
MsgConfigureSourceCenterFrequency(int sourceCenterFrequency) :
Message(),
m_sourceCenterFrequency(sourceCenterFrequency)
{ }
};
class MsgConfigureTsFileName : public Message
{
MESSAGE_CLASS_DECLARATION
public:
const QString& getFileName() const { return m_fileName; }
static MsgConfigureTsFileName* create(const QString& fileName)
{
return new MsgConfigureTsFileName(fileName);
}
private:
QString m_fileName;
MsgConfigureTsFileName(const QString& fileName) :
Message(),
m_fileName(fileName)
{ }
};
class MsgConfigureTsFileSourceSeek : public Message
{
MESSAGE_CLASS_DECLARATION
public:
int getPercentage() const { return m_seekPercentage; }
static MsgConfigureTsFileSourceSeek* create(int seekPercentage)
{
return new MsgConfigureTsFileSourceSeek(seekPercentage);
}
protected:
int m_seekPercentage; //!< percentage of seek position from the beginning 0..100
MsgConfigureTsFileSourceSeek(int seekPercentage) :
Message(),
m_seekPercentage(seekPercentage)
{ }
};
class MsgConfigureTsFileSourceStreamTiming : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgConfigureTsFileSourceStreamTiming* create()
{
return new MsgConfigureTsFileSourceStreamTiming();
}
private:
MsgConfigureTsFileSourceStreamTiming() :
Message()
{ }
};
class MsgGetUDPBitrate : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgGetUDPBitrate* create()
{
return new MsgGetUDPBitrate();
}
private:
MsgGetUDPBitrate() :
Message()
{ }
};
//=================================================================
DATVMod(DeviceAPI *deviceAPI);
virtual ~DATVMod();
virtual void destroy() { delete this; }
virtual void start();
virtual void stop();
virtual void pull(SampleVector::iterator& begin, unsigned int nbSamples);
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; }
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 m_settings.m_inputFrequencyOffset;
}
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 DATVModSettings& settings);
static void webapiUpdateChannelSettings(
DATVModSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
uint32_t getNumberOfDeviceStreams() const;
double getMagSq() const;
int getEffectiveSampleRate() const;
void setMessageQueueToGUI(MessageQueue* queue) override;
static const char* const m_channelIdURI;
static const char* const m_channelId;
private:
DeviceAPI* m_deviceAPI;
QThread *m_thread;
DATVModBaseband* m_basebandSource;
DATVModSettings m_settings;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void applySettings(const DATVModSettings& settings, bool force = false);
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DATVModSettings& settings, bool force);
void sendChannelSettings(
QList<MessageQueue*> *messageQueues,
QList<QString>& channelSettingsKeys,
const DATVModSettings& settings,
bool force
);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DATVModSettings& settings,
bool force
);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif /* PLUGINS_CHANNELTX_MODDATV_DATVMOD_H_ */

View File

@ -0,0 +1,210 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/upchannelizer.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "datvmodbaseband.h"
#include "datvmod.h"
DATVModBaseband::DATVModBaseband() :
m_mutex(QMutex::Recursive)
{
m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(48000));
m_channelizer = new UpChannelizer(&m_source);
QObject::connect(
&m_sampleFifo,
&SampleSourceFifo::dataRead,
this,
&DATVModBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
}
DATVModBaseband::~DATVModBaseband()
{
delete m_channelizer;
}
void DATVModBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_sampleFifo.reset();
}
void DATVModBaseband::pull(const SampleVector::iterator& begin, unsigned int nbSamples)
{
unsigned int part1Begin, part1End, part2Begin, part2End;
m_sampleFifo.read(nbSamples, part1Begin, part1End, part2Begin, part2End);
SampleVector& data = m_sampleFifo.getData();
if (part1Begin != part1End)
{
std::copy(
data.begin() + part1Begin,
data.begin() + part1End,
begin
);
}
unsigned int shift = part1End - part1Begin;
if (part2Begin != part2End)
{
std::copy(
data.begin() + part2Begin,
data.begin() + part2End,
begin + shift
);
}
}
void DATVModBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
SampleVector& data = m_sampleFifo.getData();
unsigned int ipart1begin;
unsigned int ipart1end;
unsigned int ipart2begin;
unsigned int ipart2end;
qreal rmsLevel, peakLevel;
int numSamples;
unsigned int remainder = m_sampleFifo.remainder();
while ((remainder > 0) && (m_inputMessageQueue.size() == 0))
{
m_sampleFifo.write(remainder, ipart1begin, ipart1end, ipart2begin, ipart2end);
if (ipart1begin != ipart1end) { // first part of FIFO data
processFifo(data, ipart1begin, ipart1end);
}
if (ipart2begin != ipart2end) { // second part of FIFO data (used when block wraps around)
processFifo(data, ipart2begin, ipart2end);
}
remainder = m_sampleFifo.remainder();
}
m_source.getLevels(rmsLevel, peakLevel, numSamples);
emit levelChanged(rmsLevel, peakLevel, numSamples);
}
void DATVModBaseband::processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd)
{
m_channelizer->prefetch(iEnd - iBegin);
m_channelizer->pull(data.begin() + iBegin, iEnd - iBegin);
}
void DATVModBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool DATVModBaseband::handleMessage(const Message& cmd)
{
if (DATVMod::MsgConfigureDATVMod::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DATVMod::MsgConfigureDATVMod& cfg = (DATVMod::MsgConfigureDATVMod&) cmd;
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DATVMod::MsgConfigureChannelizer::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DATVMod::MsgConfigureChannelizer& cfg = (DATVMod::MsgConfigureChannelizer&) cmd;
qDebug() << "DATVModBaseband::handleMessage: MsgConfigureChannelizer"
<< "(requested) sourceSampleRate: " << cfg.getSourceSampleRate()
<< "(requested) sourceCenterFrequency: " << cfg.getSourceCenterFrequency();
m_channelizer->setChannelization(cfg.getSourceSampleRate(), cfg.getSourceCenterFrequency());
m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "DATVModBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
m_sampleFifo.resize(4*SampleSourceFifo::getSizePolicy(notif.getSampleRate()));
m_channelizer->setBasebandSampleRate(notif.getSampleRate());
m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
return true;
}
else if (DATVMod::MsgConfigureTsFileName::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DATVMod::MsgConfigureTsFileName& cfg = (DATVMod::MsgConfigureTsFileName&) cmd;
m_source.openTsFile(cfg.getFileName());
return true;
}
else if (DATVMod::MsgConfigureTsFileSourceSeek::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DATVMod::MsgConfigureTsFileSourceSeek& cfg = (DATVMod::MsgConfigureTsFileSourceSeek&) cmd;
m_source.seekTsFileStream(cfg.getPercentage());
return true;
}
else if (DATVMod::MsgConfigureTsFileSourceStreamTiming::match(cmd))
{
m_source.reportTsFileSourceStreamTiming();
return true;
}
else if (DATVMod::MsgGetUDPBitrate::match(cmd))
{
m_source.reportUDPBitrate();
return true;
}
else
{
return false;
}
}
void DATVModBaseband::applySettings(const DATVModSettings& settings, bool force)
{
m_source.applySettings(settings, force);
m_settings = settings;
}
int DATVModBaseband::getChannelSampleRate() const
{
return m_channelizer->getChannelSampleRate();
}

View File

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODBASEBAND_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODBASEBAND_H_
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
#include <QObject>
#include <QMutex>
#include "dsp/samplesourcefifo.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "datvmodsource.h"
class UpChannelizer;
class DATVModBaseband : public QObject
{
Q_OBJECT
public:
DATVModBaseband();
~DATVModBaseband();
void reset();
void pull(const SampleVector::iterator& begin, unsigned int nbSamples);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_source.setMessageQueueToGUI(messageQueue); }
double getMagSq() const { return m_source.getMagSq(); }
int getChannelSampleRate() const;
int getEffectiveSampleRate() const { return m_source.getEffectiveSampleRate(); }
signals:
/**
* Level changed
* \param rmsLevel RMS level in range 0.0 - 1.0
* \param peakLevel Peak level in range 0.0 - 1.0
* \param numSamples Number of audio samples analyzed
*/
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
private:
SampleSourceFifo m_sampleFifo;
UpChannelizer *m_channelizer;
DATVModSource m_source;
MessageQueue m_inputMessageQueue;
DATVModSettings m_settings;
QMutex m_mutex;
void processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd);
bool handleMessage(const Message& cmd);
void applySettings(const DATVModSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // PLUGINS_CHANNELTX_MODDATV_DATVMODBASEBAND_H_

View File

@ -0,0 +1,579 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDockWidget>
#include <QMainWindow>
#include <QFileDialog>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
#include <cmath>
#include "device/deviceuiset.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "dsp/dspengine.h"
#include "util/db.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "maincore.h"
#include "ui_datvmodgui.h"
#include "datvmodgui.h"
#include "datvmodreport.h"
DATVModGUI* DATVModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx)
{
DATVModGUI* gui = new DATVModGUI(pluginAPI, deviceUISet, channelTx);
return gui;
}
void DATVModGUI::destroy()
{
delete this;
}
DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::DATVModGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_doApplySettings(true),
m_tickMsgOutstanding(false),
m_streamLength(0),
m_bitrate(1),
m_frameCount(0),
m_tickCount(0),
m_enableNavTime(false)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_datvMod = (DATVMod*) channelTx;
m_datvMod->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 8, -99999999, 99999999);
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(m_settings.m_rgbColor);
m_channelMarker.setBandwidth(5000);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle("DATV Modulator");
m_channelMarker.setSourceOrSinkStream(false);
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_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
resetToDefaults();
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
displaySettings();
applySettings(true);
if (!m_settings.m_tsFileName.isEmpty())
configureTsFileName();
}
DATVModGUI::~DATVModGUI()
{
delete ui;
}
void DATVModGUI::resetToDefaults()
{
m_settings.resetToDefaults();
}
QByteArray DATVModGUI::serialize() const
{
return m_settings.serialize();
}
bool DATVModGUI::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
displaySettings();
applySettings(true);
if (!m_settings.m_tsFileName.isEmpty())
configureTsFileName();
return true;
}
else
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
return false;
}
}
bool DATVModGUI::handleMessage(const Message& message)
{
if (DATVModReport::MsgReportTsFileSourceStreamData::match(message))
{
m_bitrate = ((DATVModReport::MsgReportTsFileSourceStreamData&)message).getBitrate();
m_streamLength = ((DATVModReport::MsgReportTsFileSourceStreamData&)message).getStreamLength();
m_frameCount = 0;
ui->tsFileBitrate->setText(QString("%1kb/s").arg(m_bitrate/1000, 0, 'f', 2));
updateWithStreamData();
return true;
}
else if (DATVModReport::MsgReportTsFileSourceStreamTiming::match(message))
{
m_frameCount = ((DATVModReport::MsgReportTsFileSourceStreamTiming&)message).getFrameCount();
updateWithStreamTime();
m_tickMsgOutstanding = false;
return true;
}
else if (DATVModReport::MsgReportRates::match(message))
{
DATVModReport::MsgReportRates& report = (DATVModReport::MsgReportRates&)message;
m_channelSampleRate = report.getChannelSampleRate();
m_sampleRate = report.getSampleRate();
ui->channelSampleRateText->setText(tr("%1k").arg(m_sampleRate/1000.0f, 0, 'f', 2));
int dataRate = report.getDataRate();
ui->dataRateText->setText(tr("%1kb/s").arg(dataRate/1000.0f, 0, 'f', 2));
setChannelMarkerBandwidth();
return true;
}
else if (DATVModReport::MsgReportUDPBitrate::match(message))
{
DATVModReport::MsgReportUDPBitrate& report = (DATVModReport::MsgReportUDPBitrate&)message;
ui->udpBitrate->setText(tr("%1kb/s").arg(report.getBitrate()/1000.0f, 0, 'f', 2));
m_tickMsgOutstanding = false;
return true;
}
else if (DATVMod::MsgConfigureDATVMod::match(message))
{
const DATVMod::MsgConfigureDATVMod& cfg = (DATVMod::MsgConfigureDATVMod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (DATVMod::MsgConfigureTsFileName::match(message))
{
const DATVMod::MsgConfigureTsFileName& cfg = (DATVMod::MsgConfigureTsFileName&) message;
m_settings.m_tsFileName = cfg.getFileName();
ui->tsFileText->setText(m_settings.m_tsFileName);
return true;
}
else
{
return false;
}
}
void DATVModGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void DATVModGUI::handleSourceMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void DATVModGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = value;
applySettings();
}
void DATVModGUI::on_dvbStandard_currentIndexChanged(int index)
{
m_settings.m_standard = (DATVModSettings::DVBStandard) index;
ui->fec->blockSignals(true);
ui->rollOff->blockSignals(true);
ui->modulation->blockSignals(true);
ui->fec->clear();
ui->rollOff->clear();
ui->modulation->clear();
if (m_settings.m_standard == DATVModSettings::DVB_S)
{
ui->fec->addItem("1/2");
ui->fec->addItem("2/3");
ui->fec->addItem("3/4");
ui->fec->addItem("5/6");
ui->fec->addItem("7/8");
ui->rollOff->addItem("0.35");
ui->modulation->addItem("BPSK");
ui->modulation->addItem("QPSK");
}
else
{
ui->fec->addItem("1/4");
ui->fec->addItem("1/3");
ui->fec->addItem("2/5");
ui->fec->addItem("1/2");
ui->fec->addItem("3/5");
ui->fec->addItem("2/3");
ui->fec->addItem("3/4");
ui->fec->addItem("4/5");
ui->fec->addItem("5/6");
ui->fec->addItem("8/9");
ui->fec->addItem("9/10");
ui->rollOff->addItem("0.20");
ui->rollOff->addItem("0.25");
ui->rollOff->addItem("0.35");
ui->modulation->addItem("QPSK");
ui->modulation->addItem("8PSK");
ui->modulation->addItem("16APSK");
ui->modulation->addItem("32APSK");
}
ui->fec->setCurrentIndex(ui->fec->findText(DATVModSettings::mapCodeRate(m_settings.m_fec)));
ui->rollOff->setCurrentIndex(ui->rollOff->findText(QString("%1").arg(m_settings.m_rollOff, 0, 'f', 2)));
ui->modulation->setCurrentIndex(ui->modulation->findText(DATVModSettings::mapModulation(m_settings.m_modulation)));
ui->fec->blockSignals(false);
ui->rollOff->blockSignals(false);
ui->modulation->blockSignals(false);
applySettings();
}
void DATVModGUI::on_modulation_currentIndexChanged(int index)
{
m_settings.m_modulation = (DATVModSettings::DATVModulation) index;
applySettings();
}
void DATVModGUI::on_rollOff_currentIndexChanged(int index)
{
m_settings.m_rollOff = ui->rollOff->currentText().toFloat();
applySettings();
}
void DATVModGUI::on_fec_currentIndexChanged(int index)
{
m_settings.m_fec = DATVModSettings::mapCodeRate(ui->fec->currentText());
applySettings();
}
void DATVModGUI::on_symbolRate_valueChanged(int value)
{
m_settings.m_symbolRate = value;
applySettings();
}
void DATVModGUI::on_rfBW_valueChanged(int value)
{
m_settings.m_rfBandwidth = value * 100000;
ui->rfBWText->setText(QString("%1M").arg(m_settings.m_rfBandwidth / 1e6, 0, 'f', 1));
setChannelMarkerBandwidth();
applySettings();
}
void DATVModGUI::setChannelMarkerBandwidth()
{
if (m_channelSampleRate == m_sampleRate)
m_channelMarker.setBandwidth(m_channelSampleRate);
else
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setSidebands(ChannelMarker::dsb);
}
void DATVModGUI::on_inputSelect_currentIndexChanged(int index)
{
m_settings.m_source = (DATVModSettings::DATVSource) index;
applySettings();
}
void DATVModGUI::on_channelMute_toggled(bool checked)
{
m_settings.m_channelMute = checked;
applySettings();
}
void DATVModGUI::on_tsFileDialog_clicked(bool checked)
{
(void) checked;
QString fileName = QFileDialog::getOpenFileName(this,
tr("Open MPEG transport stream file"), m_settings.m_tsFileName, tr("MPEG Transport Stream Files (*.ts)"),
nullptr, QFileDialog::DontUseNativeDialog);
if (fileName != "")
{
m_settings.m_tsFileName = fileName;
ui->tsFileText->setText(m_settings.m_tsFileName);
configureTsFileName();
}
}
void DATVModGUI::on_playLoop_toggled(bool checked)
{
m_settings.m_tsFilePlayLoop = checked;
applySettings();
}
void DATVModGUI::on_playFile_toggled(bool checked)
{
m_settings.m_tsFilePlay = checked;
ui->navTimeSlider->setEnabled(!checked);
m_enableNavTime = !checked;
applySettings();
}
void DATVModGUI::on_navTimeSlider_valueChanged(int value)
{
if (m_enableNavTime && ((value >= 0) && (value <= 100)))
{
DATVMod::MsgConfigureTsFileSourceSeek* message = DATVMod::MsgConfigureTsFileSourceSeek::create(value);
m_datvMod->getInputMessageQueue()->push(message);
}
}
void DATVModGUI::configureTsFileName()
{
DATVMod::MsgConfigureTsFileName* message = DATVMod::MsgConfigureTsFileName::create(m_settings.m_tsFileName);
m_datvMod->getInputMessageQueue()->push(message);
}
void DATVModGUI::on_udpAddress_editingFinished()
{
m_settings.m_udpAddress = ui->udpAddress->text();
applySettings();
}
void DATVModGUI::on_udpPort_valueChanged(int value)
{
m_settings.m_udpPort = value;
applySettings();
}
void DATVModGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
void DATVModGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.move(p);
dialog.exec();
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine))
{
DeviceStreamSelectionDialog dialog(this);
dialog.setNumberOfStreams(m_datvMod->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();
}
void DATVModGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void DATVModGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
DATVMod::MsgConfigureSourceCenterFrequency *msgChan = DATVMod::MsgConfigureSourceCenterFrequency::create(
m_channelMarker.getCenterFrequency());
m_datvMod->getInputMessageQueue()->push(msgChan);
DATVMod::MsgConfigureDATVMod *msg = DATVMod::MsgConfigureDATVMod::create(m_settings, force);
m_datvMod->getInputMessageQueue()->push(msg);
}
}
void DATVModGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
setChannelMarkerBandwidth();
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());
displayStreamIndex();
blockApplySettings(true);
ui->deltaFrequency->setValue(m_settings.m_inputFrequencyOffset);
ui->dvbStandard->setCurrentIndex((int) m_settings.m_standard);
ui->fec->setCurrentIndex(ui->fec->findText(DATVModSettings::mapCodeRate(m_settings.m_fec)));
ui->symbolRate->setValue(m_settings.m_symbolRate);
ui->rollOff->setCurrentIndex(ui->rollOff->findText(QString("%1").arg(m_settings.m_rollOff, 0, 'f', 2)));
ui->modulation->setCurrentIndex(ui->modulation->findText(DATVModSettings::mapModulation(m_settings.m_modulation)));
ui->rfBW->setValue(m_settings.m_rfBandwidth/100000);
ui->rfBWText->setText(QString("%1M").arg(m_settings.m_rfBandwidth / 1e6, 0, 'f', 1));
ui->channelMute->setChecked(m_settings.m_channelMute);
ui->inputSelect->setCurrentIndex((int) m_settings.m_source);
if (m_settings.m_tsFileName.isEmpty())
ui->tsFileText->setText("...");
else
ui->tsFileText->setText(m_settings.m_tsFileName);
ui->playFile->setChecked(m_settings.m_tsFilePlay);
ui->playLoop->setChecked(m_settings.m_tsFilePlayLoop);
ui->udpAddress->setText(m_settings.m_udpAddress);
ui->udpPort->setValue(m_settings.m_udpPort);
blockApplySettings(false);
}
void DATVModGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
} else {
setStreamIndicator("S"); // single channel indicator
}
}
void DATVModGUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);
}
void DATVModGUI::enterEvent(QEvent*)
{
m_channelMarker.setHighlighted(true);
}
void DATVModGUI::tick()
{
double powDb = CalcDb::dbPower(m_datvMod->getMagSq());
m_channelPowerDbAvg(powDb);
ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1));
// Use m_tickMsgOutstanding to prevent queuing lots of messsages while stopped/paused
if (((++m_tickCount & 0xf) == 0) && !m_tickMsgOutstanding)
{
if (ui->inputSelect->currentIndex() == (int) DATVModSettings::SourceFile)
{
m_tickMsgOutstanding = true;
m_datvMod->getInputMessageQueue()->push(DATVMod::MsgConfigureTsFileSourceStreamTiming::create());
}
else if (ui->inputSelect->currentIndex() == (int) DATVModSettings::SourceUDP)
{
m_tickMsgOutstanding = true;
m_datvMod->getInputMessageQueue()->push(DATVMod::MsgGetUDPBitrate::create());
}
}
}
void DATVModGUI::updateWithStreamData()
{
QTime recordLength(0, 0, 0, 0);
recordLength = recordLength.addSecs(m_streamLength * 8 / m_bitrate);
QString s_time = recordLength.toString("HH:mm:ss");
ui->recordLengthText->setText(s_time);
updateWithStreamTime();
}
void DATVModGUI::updateWithStreamTime()
{
int t_sec = 0;
int t_msec = 0;
if (m_bitrate > 0.0f)
{
float secs = m_frameCount * 188 * 8 / m_bitrate;
t_sec = (int) secs;
t_msec = (int) ((secs - t_sec) * 1000.0f);
}
QTime t(0, 0, 0, 0);
t = t.addSecs(t_sec);
t = t.addMSecs(t_msec);
QString s_timems = t.toString("HH:mm:ss.zzz");
QString s_time = t.toString("HH:mm:ss");
ui->relTimeText->setText(s_timems);
if (!m_enableNavTime)
{
float posRatio = (t_sec * m_bitrate) / (m_streamLength * 8);
ui->navTimeSlider->setValue((int) (posRatio * 100.0));
}
}

View File

@ -0,0 +1,120 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODGUI_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODGUI_H_
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "util/movingaverage.h"
#include "util/messagequeue.h"
#include "datvmod.h"
#include "datvmodsettings.h"
class PluginAPI;
class DeviceUISet;
class BasebandSampleSource;
class QMessageBox;
namespace Ui {
class DATVModGUI;
}
class DATVModGUI : public ChannelGUI {
Q_OBJECT
public:
static DATVModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
public slots:
void channelMarkerChangedByCursor();
private:
Ui::DATVModGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
DATVModSettings m_settings;
bool m_doApplySettings;
bool m_tickMsgOutstanding;
DATVMod* m_datvMod;
MovingAverageUtil<double, double, 20> m_channelPowerDbAvg;
int m_channelSampleRate;
int m_sampleRate;
quint32 m_streamLength; //!< TS file length in seconds
float m_bitrate; //!< TS file bitrate
int m_frameCount; //!< TS frame count (E.g. 188 byte TS frames, not video frames)
std::size_t m_tickCount;
bool m_enableNavTime;
MessageQueue m_inputMessageQueue;
explicit DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent = 0);
virtual ~DATVModGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayStreamIndex();
void updateWithStreamData();
void updateWithStreamTime();
void setChannelMarkerBandwidth();
bool handleMessage(const Message& message);
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
private slots:
void handleSourceMessages();
void on_deltaFrequency_changed(qint64 value);
void on_channelMute_toggled(bool checked);
void on_dvbStandard_currentIndexChanged(int index);
void on_symbolRate_valueChanged(int value);
void on_rfBW_valueChanged(int value);
void on_fec_currentIndexChanged(int index);
void on_modulation_currentIndexChanged(int index);
void on_rollOff_currentIndexChanged(int index);
void on_inputSelect_currentIndexChanged(int index);
void on_tsFileDialog_clicked(bool checked);
void on_playFile_toggled(bool checked);
void on_playLoop_toggled(bool checked);
void on_navTimeSlider_valueChanged(int value);
void on_udpAddress_editingFinished();
void on_udpPort_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void configureTsFileName();
void tick();
};
#endif /* PLUGINS_CHANNELTX_MODDATV_DATVMODGUI_H_ */

View File

@ -0,0 +1,790 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DATVModGUI</class>
<widget class="RollupWidget" name="DATVModGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>463</width>
<height>235</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>450</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>DATV Modulator</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>451</width>
<height>221</height>
</rect>
</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="deltaFreqPowLayout">
<item>
<layout class="QHBoxLayout" name="deltaFrequencyLayout">
<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_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="channelSampleRateText">
<property name="minimumSize">
<size>
<width>66</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Sample rate</string>
</property>
<property name="text">
<string>00000.00k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dataRateText">
<property name="toolTip">
<string>Data rate of DVB stream</string>
</property>
<property name="text">
<string>0kb/s</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_12">
<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>
<widget class="QLabel" name="channelPower">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Channel power</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>-100.0 dB</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QToolButton" name="channelMute">
<property name="toolTip">
<string>Mute/Unmute channel</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/txon.png</normaloff>
<normalon>:/txoff.png</normalon>:/txon.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="rfBandwidthLayout">
<item>
<widget class="QComboBox" name="dvbStandard">
<property name="toolTip">
<string>DVB standard</string>
</property>
<item>
<property name="text">
<string>DVB-S</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line_15">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Symbols/s</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="symbolRate">
<property name="toolTip">
<string>Symbol rate</string>
</property>
<property name="maximum">
<number>10000000</number>
</property>
<property name="value">
<number>250000</number>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWLabel">
<property name="text">
<string>BW</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="rfBW">
<property name="toolTip">
<string>Modulator (RF) bandwidth when interpolation takes place</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>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="volumeLayout_2">
<item>
<widget class="QLabel" name="inputSelectLabel">
<property name="text">
<string>Source</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inputSelect">
<property name="toolTip">
<string>Source of MPEG transport stream to transmit</string>
</property>
<item>
<property name="text">
<string>File</string>
</property>
</item>
<item>
<property name="text">
<string>UDP</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fecLabel">
<property name="text">
<string>FEC</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fec">
<property name="toolTip">
<string>Forward error correction code rate</string>
</property>
<item>
<property name="text">
<string>1/2</string>
</property>
</item>
<item>
<property name="text">
<string>2/3</string>
</property>
</item>
<item>
<property name="text">
<string>3/4</string>
</property>
</item>
<item>
<property name="text">
<string>5/6</string>
</property>
</item>
<item>
<property name="text">
<string>7/8</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line_13">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="modulationLabel">
<property name="text">
<string>Modulation</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="modulation">
<property name="toolTip">
<string>Modulation type</string>
</property>
<item>
<property name="text">
<string>BPSK</string>
</property>
</item>
<item>
<property name="text">
<string>QPSK</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line_14">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rollOffLabel">
<property name="text">
<string>Roll off</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="rollOff">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Root raised cosine filter roll-off</string>
</property>
<item>
<property name="text">
<string>0.35</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>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_nav_2">
<item>
<widget class="QLabel" name="udpPortLabel">
<property name="text">
<string>UDP</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="udpAddress">
<property name="toolTip">
<string>IP address to bind UDP socket to</string>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="udpPort">
<property name="toolTip">
<string>UDP port number to receive MPEG transport stream on</string>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>5004</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<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>
<widget class="Line" name="line_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="udpBitrate">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>UDP bitrate</string>
</property>
<property name="text">
<string>0kb/s</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="tsFileLayout">
<item>
<widget class="QPushButton" name="tsFileDialog">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Open MPEG transport stream file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/film_reel.png</normaloff>:/film_reel.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tsFileText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tsFileBitrate">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>File bitrate</string>
</property>
<property name="text">
<string>0kb/s</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="playControllLayout">
<item>
<widget class="ButtonSwitch" name="playLoop">
<property name="toolTip">
<string>Play file in a loop</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/playloop.png</normaloff>:/playloop.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="playFile">
<property name="toolTip">
<string>File play/pause</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/pause.png</normalon>
<disabledoff>:/play.png</disabledoff>
<disabledon>:/play.png</disabledon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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>
<widget class="Line" name="linePlay1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="relTimeText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Time from start</string>
</property>
<property name="text">
<string>00:00:00.000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="linePlay2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="recordLengthText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Total time</string>
</property>
<property name="text">
<string>00:00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_nav">
<item>
<widget class="QSlider" name="navTimeSlider">
<property name="toolTip">
<string>File time navigator</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</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>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "datvmodgui.h"
#endif
#include "datvmod.h"
#include "datvmodwebapiadapter.h"
#include "datvmodplugin.h"
const PluginDescriptor DATVModPlugin::m_pluginDescriptor = {
DATVMod::m_channelId,
QStringLiteral("DATV Modulator"),
QStringLiteral("6.6.3"),
QStringLiteral("(c) Jon Beniston, M7RCE and Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
DATVModPlugin::DATVModPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& DATVModPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void DATVModPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerTxChannel(DATVMod::m_channelIdURI, DATVMod::m_channelId, this);
}
void DATVModPlugin::createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
DATVMod *instance = new DATVMod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* DATVModPlugin::createTxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSource *txChannel) const
{
(void) deviceUISet;
(void) txChannel;
return nullptr;
}
#else
ChannelGUI* DATVModPlugin::createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) const
{
return DATVModGUI::create(m_pluginAPI, deviceUISet, txChannel);
}
#endif
ChannelWebAPIAdapter* DATVModPlugin::createChannelWebAPIAdapter() const
{
return new DATVModWebAPIAdapter();
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODPLUGIN_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODPLUGIN_H_
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceAPI;
class BasebandSampleSource;
class DATVModPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channeltx.moddatv")
public:
explicit DATVModPlugin(QObject* parent = 0);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif /* PLUGINS_CHANNELTX_MODDATV_DATVMODPLUGIN_H_ */

View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "datvmodreport.h"
MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportTsFileSourceStreamTiming, Message)
MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportTsFileSourceStreamData, Message)
MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportRates, Message)
MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportUDPBitrate, Message)
DATVModReport::DATVModReport()
{ }
DATVModReport::~DATVModReport()
{ }

View File

@ -0,0 +1,133 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODREPORT_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODREPORT_H_
#include <vector>
#include <QObject>
#include "util/message.h"
class DATVModReport : public QObject
{
Q_OBJECT
public:
class MsgReportTsFileSourceStreamTiming : public Message
{
MESSAGE_CLASS_DECLARATION
public:
int getFrameCount() const { return m_frameCount; }
static MsgReportTsFileSourceStreamTiming* create(int frameCount)
{
return new MsgReportTsFileSourceStreamTiming(frameCount);
}
protected:
int m_frameCount;
MsgReportTsFileSourceStreamTiming(int frameCount) :
Message(),
m_frameCount(frameCount)
{ }
};
class MsgReportTsFileSourceStreamData : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getBitrate() const { return m_bitrate; }
quint64 getStreamLength() const { return m_streamLength; }
static MsgReportTsFileSourceStreamData* create(int bitrate,
quint64 streamLength)
{
return new MsgReportTsFileSourceStreamData(bitrate, streamLength);
}
protected:
int m_bitrate;
int m_streamLength; //!< TS length in bytes
MsgReportTsFileSourceStreamData(int bitrate,
int streamLength) :
Message(),
m_bitrate(bitrate),
m_streamLength(streamLength)
{ }
};
class MsgReportRates : public Message
{
MESSAGE_CLASS_DECLARATION
public:
int getChannelSampleRate() const { return m_channelSampleRate; }
int getSampleRate() const { return m_sampleRate; }
int getDataRate() const { return m_dataRate; }
static MsgReportRates* create(int channelSampleRate, int sampleRate, int dataRate)
{
return new MsgReportRates(channelSampleRate, sampleRate, dataRate);
}
protected:
int m_channelSampleRate;
int m_sampleRate;
int m_dataRate;
MsgReportRates(
int channelSampleRate,
int sampleRate,
int dataRate) :
Message(),
m_channelSampleRate(channelSampleRate),
m_sampleRate(sampleRate),
m_dataRate(dataRate)
{ }
};
class MsgReportUDPBitrate : public Message
{
MESSAGE_CLASS_DECLARATION
public:
int getBitrate() const { return m_bitrate; }
static MsgReportUDPBitrate* create(int bitrate)
{
return new MsgReportUDPBitrate(bitrate);
}
protected:
int m_bitrate;
MsgReportUDPBitrate(
int bitrate) :
Message(),
m_bitrate(bitrate)
{ }
};
public:
DATVModReport();
~DATVModReport();
};
#endif // PLUGINS_CHANNELTX_MODDATV_DATVMODREPORT_H_

View File

@ -0,0 +1,190 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "datvmodsettings.h"
const QStringList DATVModSettings::m_codeRateStrings = {"1/2", "2/3", "3/4", "5/6", "7/8", "4/5", "8/9", "9/10", "1/4", "1/3", "2/5", "3/5"};
const QStringList DATVModSettings::m_modulationStrings = {"BPSK", "QPSK", "8PSK", "16APSK", "32APSK"};
DATVModSettings::DATVModSettings() :
m_channelMarker(0)
{
resetToDefaults();
}
void DATVModSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 1000000;
m_standard = DVB_S;
m_modulation = QPSK;
m_fec = FEC12;
m_symbolRate = 250000;
m_rollOff = 0.35f;
m_source = SourceFile;
m_tsFileName = "";
m_tsFilePlayLoop = false;
m_tsFilePlay = false;
m_udpAddress = "127.0.0.1";
m_udpPort = 5004;
m_channelMute = false;
m_rgbColor = QColor(Qt::magenta).rgb();
m_title = "DATV Modulator";
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
}
QByteArray DATVModSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeReal(2, m_rfBandwidth);
s.writeS32(3, (int) m_standard);
s.writeS32(4, (int) m_modulation);
s.writeS32(5, (int) m_fec);
s.writeS32(6, m_symbolRate);
s.writeReal(7, m_rollOff);
s.writeS32(10, (int) m_source);
s.writeString(11, m_tsFileName);
s.writeBool(12, m_tsFilePlayLoop);
s.writeString(13, m_udpAddress);
s.writeU32(14, m_udpPort);
s.writeString(20, m_title);
s.writeU32(21, m_rgbColor);
if (m_channelMarker) {
s.writeBlob(22, m_channelMarker->serialize());
}
s.writeBool(23, m_useReverseAPI);
s.writeString(24, m_reverseAPIAddress);
s.writeU32(25, m_reverseAPIPort);
s.writeU32(26, m_reverseAPIDeviceIndex);
s.writeU32(27, m_reverseAPIChannelIndex);
s.writeS32(28, m_streamIndex);
return s.final();
}
bool DATVModSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
qint32 tmp;
uint32_t utmp;
d.readS32(1, &tmp, 0);
m_inputFrequencyOffset = tmp;
d.readReal(2, &m_rfBandwidth, 1000000);
d.readS32(3, (int *) &m_standard, (int)DVB_S);
d.readS32(4, (int *) &m_modulation, (int)QPSK);
d.readS32(5, (int *) &m_fec, (int)FEC12);
d.readS32(6, &m_symbolRate, 250000);
d.readReal(7, &m_rollOff, 0.35f);
d.readS32(10, (int *)&m_source, (int)SourceFile);
d.readString(11, &m_tsFileName);
d.readBool(12, &m_tsFilePlayLoop, false);
d.readString(13, &m_udpAddress, "127.0.0.1");
d.readU32(14, &utmp, 0);
if ((utmp > 1023) && (utmp < 65536)) {
m_udpPort = utmp;
} else {
m_udpPort = 5004;
}
d.readString(20, &m_title, "DATV Modulator");
d.readU32(21, &m_rgbColor, QColor(Qt::magenta).rgb());
if (m_channelMarker)
{
d.readBlob(22, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
d.readBool(23, &m_useReverseAPI, false);
d.readString(24, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(25, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(26, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(27, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readS32(28, &m_streamIndex, 0);
return true;
}
else
{
resetToDefaults();
return false;
}
}
DATVModSettings::DATVCodeRate DATVModSettings::mapCodeRate(const QString& string)
{
for (int i = 0; i < m_codeRateStrings.size(); i++)
{
if (string == m_codeRateStrings[i])
return (DATVCodeRate)i;
}
return FEC12;
}
QString DATVModSettings::mapCodeRate(DATVCodeRate codeRate)
{
return m_codeRateStrings[codeRate];
}
DATVModSettings::DATVModulation DATVModSettings::mapModulation(const QString& string)
{
for (int i = 0; i < m_modulationStrings.size(); i++)
{
if (string == m_modulationStrings[i])
return (DATVModulation)i;
}
return QPSK;
}
QString DATVModSettings::mapModulation(DATVModulation modulation)
{
return m_modulationStrings[modulation];
}

View File

@ -0,0 +1,113 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODSETTINGS_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <stdint.h>
class Serializable;
struct DATVModSettings
{
enum DATVSource
{
SourceFile,
SourceUDP
};
enum DVBStandard
{
DVB_S,
DVB_S2
};
enum DATVModulation
{
BPSK, // DVB-S
QPSK,
PSK8, // DVB-S2
APSK16, // DVB-S2
APSK32 // DVB-S2
};
static const QStringList m_modulationStrings;
enum DATVCodeRate
{
FEC12,
FEC23,
FEC34,
FEC56,
FEC78, // DVB-S
FEC45, // DVB-S2
FEC89, // DVB-S2
FEC910, // DVB-S2
FEC14, // DVB-S2
FEC13, // DVB-S2
FEC25, // DVB-S2
FEC35 // DVB-S2
};
static const QStringList m_codeRateStrings;
qint64 m_inputFrequencyOffset; //!< Offset from baseband center frequency
Real m_rfBandwidth; //!< Bandwidth of modulated signal
DVBStandard m_standard;
DATVModulation m_modulation; //!< RF modulation type
DATVCodeRate m_fec; //!< FEC code rate
int m_symbolRate; //!< Symbol rate in symbols per second
float m_rollOff; //!< 0.35, 0.25, 0.2
DATVSource m_source; //!< Source of transport stream
QString m_tsFileName;
bool m_tsFilePlayLoop; //!< Play TS file in a loop
bool m_tsFilePlay; //!< True to play TS file and false to pause
QString m_udpAddress; //!< UDP to receive transport stream packets on
int m_udpPort; //!< UDP port number
bool m_channelMute; //!< Mute channel baseband output
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
int m_streamIndex;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
DATVModSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
static DATVCodeRate mapCodeRate(const QString& string);
static QString mapCodeRate(DATVCodeRate codeRate);
static DATVModulation mapModulation(const QString& string);
static QString mapModulation(DATVModulation modulation);
};
#endif /* PLUGINS_CHANNELTX_MODDATV_DATVMODSETTINGS_H_ */

View File

@ -0,0 +1,613 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/chrono.hpp>
//#include <time.h>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QUdpSocket>
#include <QHostAddress>
#include <QNetworkDatagram>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "util/db.h"
#include "util/messagequeue.h"
#include "datvmodreport.h"
#include "datvmodsource.h"
const int DATVModSource::m_levelNbSamples = 10000; // every 10ms
// Get transport stream bitrate from file
int DATVModSource::getTSBitrate(const QString& filename)
{
AVFormatContext *fmtCtx = nullptr;
QByteArray ba = filename.toLocal8Bit();
const char *filenameChars = ba.data();
if (avformat_open_input(&fmtCtx, filenameChars, nullptr, nullptr) < 0)
{
qCritical() << "DATVModSource: Could not open source file " << filename;
return -1;
}
if (avformat_find_stream_info(fmtCtx, nullptr) < 0)
{
qCritical() << "DATVModSource: Could not find stream information for " << filename;
avformat_close_input(&fmtCtx);
return -1;
}
int bitrate = fmtCtx->bit_rate;
avformat_close_input(&fmtCtx);
return bitrate;
}
// Get data bitrate (i.e. excluding FEC overhead)
int DATVModSource::getDVBSDataBitrate(const DATVModSettings& settings)
{
float rsFactor = DVBS::tsPacketLen/(float)DVBS::rsPacketLen;
float convFactor;
float bitsPerSymbol;
switch (settings.m_fec)
{
case DATVModSettings::FEC12:
convFactor = 1.0f/2.0f;
break;
case DATVModSettings::FEC23:
convFactor = 2.0f/3.0f;
break;
case DATVModSettings::FEC34:
convFactor = 3.0f/4.0f;
break;
case DATVModSettings::FEC56:
convFactor = 5.0f/6.0f;
break;
case DATVModSettings::FEC78:
convFactor = 7.0f/8.0f;
break;
}
switch (settings.m_modulation)
{
case DATVModSettings::BPSK:
bitsPerSymbol = 1.0f;
break;
case DATVModSettings::QPSK:
bitsPerSymbol = 2.0f;
break;
case DATVModSettings::PSK8:
bitsPerSymbol = 3.0f;
break;
}
return std::round(settings.m_symbolRate * bitsPerSymbol * rsFactor * convFactor);
}
void DATVModSource::checkBitrates()
{
int dataBitrate = getDVBSDataBitrate(m_settings);
qDebug() << "MPEG-TS bitrate: " << m_mpegTSBitrate;
qDebug() << "DVB-S data bitrate: " << dataBitrate;
if (dataBitrate < m_mpegTSBitrate)
qWarning() << "DVB-S data bitrate is lower than the bitrate of the MPEG transport stream";
m_tsRatio = m_mpegTSBitrate/(float)dataBitrate;
}
DATVModSource::DATVModSource() :
m_sampleIdx(0),
m_frameIdx(0),
m_frameCount(0),
m_tsRatio(0.0f),
m_symbolCount(0),
m_symbolSel(0),
m_symbolIdx(0),
m_samplesPerSymbol(1),
m_udpSocket(nullptr),
m_udpByteCount(0),
m_udpBufferIdx(0),
m_udpBufferCount(0),
m_sampleRate(0),
m_channelSampleRate(1000000),
m_channelFrequencyOffset(0),
m_tsFileOK(false),
m_messageQueueToGUI(nullptr)
{
m_interpolatorDistanceRemain = 0.0f;
m_interpolatorDistance = 1.0f;
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
applySettings(m_settings, true);
}
DATVModSource::~DATVModSource()
{
}
void DATVModSource::pull(SampleVector::iterator begin, unsigned int nbSamples)
{
std::for_each(
begin,
begin + nbSamples,
[this](Sample& s) {
pullOne(s);
}
);
}
void DATVModSource::prefetch(unsigned int nbSamples)
{
(void) nbSamples;
}
void DATVModSource::pullOne(Sample& sample)
{
if (m_settings.m_channelMute)
{
sample.m_real = 0.0f;
sample.m_imag = 0.0f;
return;
}
Complex ci;
if (m_sampleRate == m_channelSampleRate) // no interpolation nor decimation
{
modulateSample();
pullFinalize(m_modSample, sample);
}
else
{
if (m_interpolatorDistance > 1.0f) // decimate
{
modulateSample();
while (!m_interpolator.decimate(&m_interpolatorDistanceRemain, m_modSample, &ci))
modulateSample();
}
else
{
if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, m_modSample, &ci))
modulateSample();
}
m_interpolatorDistanceRemain += m_interpolatorDistance;
pullFinalize(ci, sample);
}
}
void DATVModSource::pullFinalize(Complex& ci, Sample& sample)
{
ci *= m_carrierNco.nextIQ(); // shift to carrier frequency
double magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
m_movingAverage(magsq);
sample.m_real = (FixReal) (ci.real() * SDR_TX_SCALEF);
sample.m_imag = (FixReal) (ci.imag() * SDR_TX_SCALEF);
}
void DATVModSource::modulateSample()
{
Real i, q;
if (m_sampleIdx == 0)
{
while (m_symbolCount == 0)
{
bool tsFileReady = (m_settings.m_source == DATVModSettings::SourceFile)
&& m_settings.m_tsFilePlay
&& m_tsFileOK
&& !m_mpegTSStream.eof();
if ( tsFileReady
&& (m_frameIdx/(m_frameCount+1.0f) < m_tsRatio) // Insert null packets if file rate is lower than DVB-S data rate
)
{
// Read transport stream packet from file
m_mpegTSStream.read((char *)m_mpegTS, sizeof(m_mpegTS));
m_frameIdx++;
m_frameCount++;
}
else if ( (m_settings.m_source == DATVModSettings::SourceUDP)
&& (m_udpBufferIdx < m_udpBufferCount)
)
{
// Copy transport stream packet from UDP buffer
memcpy(m_mpegTS, &m_udpBuffer[m_udpBufferIdx*sizeof(m_mpegTS)], sizeof(m_mpegTS));
m_udpBufferIdx++;
}
else if ( (m_settings.m_source == DATVModSettings::SourceUDP)
&& ((m_udpSocket != nullptr) && m_udpSocket->hasPendingDatagrams())
)
{
// Get transport stream packets from UDP - buffer if more than one
QNetworkDatagram datagram = m_udpSocket->receiveDatagram();
QByteArray ba = datagram.data();
int size = ba.size();
char *data = ba.data();
if (size <= sizeof(m_udpBuffer))
{
memcpy(m_mpegTS, data, sizeof(m_mpegTS));
if (size >= sizeof(m_mpegTS))
memcpy(&m_udpBuffer[0], &data[sizeof(m_mpegTS)], size - sizeof(m_mpegTS));
m_udpBufferIdx = 0;
m_udpBufferCount = (size / sizeof(m_mpegTS)) - 1;
if (size % sizeof(m_mpegTS) != 0)
qWarning() << "DATVModSource::modulateSample: UDP packet size (" << size << ") is not a multiple of " << sizeof(m_mpegTS);
}
else
qWarning() << "DATVModSource::modulateSample: UDP packet size (" << size << ") exceeds buffer size " << sizeof(m_udpBuffer) << ")";
m_udpByteCount += ba.size();
}
else
{
// Insert null packet. PID=0x1fff
memset(m_mpegTS, 0, sizeof(m_mpegTS));
m_mpegTS[0] = 0x47; // Sync byte
m_mpegTS[1] = 0x01;
m_mpegTS[2] = 0xff;
m_mpegTS[3] = 0xff;
m_mpegTS[4] = 0x10;
if (tsFileReady)
m_frameCount++;
//qDebug() << "null " << tsFileReady << " " << (m_frameIdx/(m_frameCount+1.0f)) << " " << m_tsRatio;
}
if (m_settings.m_standard == DATVModSettings::DVB_S)
{
// Encode using DVB-S
m_symbolCount = m_dvbs.encode(m_mpegTS, m_iqSymbols);
}
// Loop file if we reach the end
if ((m_frameIdx*DVBS::tsPacketLen >= m_mpegTSSize) && m_settings.m_tsFilePlayLoop)
{
m_frameIdx = 0;
m_frameCount = 0;
}
m_symbolIdx = 0;
}
if (m_settings.m_modulation == DATVModSettings::BPSK)
{
// BPSK
i = m_pulseShapeI.filter(m_iqSymbols[m_symbolIdx*2+m_symbolSel] ? -1.0f : 1.0f);
q = 0.0f;
if (m_symbolSel == 1)
{
m_symbolIdx++;
m_symbolCount--;
m_symbolSel = 0;
}
else
m_symbolSel++;
}
else
{
// QPSK
if (m_settings.m_standard == DATVModSettings::DVB_S)
{
// Does the 45-degree rotation matter?
// Makes a little difference to amplitude of filter output, but we could scale that
i = m_pulseShapeI.filter(m_iqSymbols[m_symbolIdx*2] ? -1.0f : 1.0f);
q = m_pulseShapeQ.filter(m_iqSymbols[m_symbolIdx*2+1] ? -1.0f : 1.0f);
/*
int sym = (m_iqSymbols[m_symbolIdx*2] << 1) | m_iqSymbols[m_symbolIdx*2+1];
if (sym == 0)
{
i = m_pulseShapeI.filter(cos(M_PI/4));
q = m_pulseShapeQ.filter(sin(M_PI/4));
}
else if (sym == 1)
{
i = m_pulseShapeI.filter(cos(7*M_PI/4));
q = m_pulseShapeQ.filter(sin(7*M_PI/4));
}
else if (sym == 2)
{
i = m_pulseShapeI.filter(cos(3*M_PI/4));
q = m_pulseShapeQ.filter(sin(3*M_PI/4));
}
else if (sym == 3)
{
i = m_pulseShapeI.filter(cos(5*M_PI/4));
q = m_pulseShapeQ.filter(sin(5*M_PI/4));
}
*/
}
m_symbolIdx++;
m_symbolCount--;
}
}
else
{
i = m_pulseShapeI.filter(0.0f);
q = m_pulseShapeQ.filter(0.0f);
}
m_sampleIdx++;
if (m_sampleIdx >= m_samplesPerSymbol)
m_sampleIdx = 0;
m_modSample.real(i);
m_modSample.imag(q);
// These levels aren't currently used in the GUI
Real t = std::abs(m_modSample);
calculateLevel(t);
}
void DATVModSource::calculateLevel(Real& sample)
{
if (m_levelCalcCount < m_levelNbSamples)
{
m_peakLevel = std::max(std::fabs(m_peakLevel), sample);
m_levelSum += sample * sample;
m_levelCalcCount++;
}
else
{
m_rmsLevel = std::sqrt(m_levelSum / m_levelNbSamples);
m_peakLevelOut = m_peakLevel;
m_peakLevel = 0.0f;
m_levelSum = 0.0f;
m_levelCalcCount = 0;
}
}
void DATVModSource::openTsFile(const QString& fileName)
{
m_tsFileOK = false;
m_mpegTSBitrate = getTSBitrate(fileName);
if (m_mpegTSBitrate > 0)
{
m_mpegTSStream.open(qPrintable(fileName), std::ifstream::binary);
if (m_mpegTSStream.is_open())
{
m_mpegTSStream.seekg (0, m_mpegTSStream.end);
m_mpegTSSize = m_mpegTSStream.tellg();
m_mpegTSStream.seekg (0, m_mpegTSStream.beg);
m_frameIdx = 0;
m_frameCount = 0;
m_tsFileOK = true;
}
checkBitrates();
}
else
qDebug() << "DATVModSource::openTsFile: Failed to get bitrate for transport stream file: " << fileName;
if (m_tsFileOK)
{
m_settings.m_tsFileName = fileName;
if (getMessageQueueToGUI())
{
DATVModReport::MsgReportTsFileSourceStreamData *report;
report = DATVModReport::MsgReportTsFileSourceStreamData::create(m_mpegTSBitrate, m_mpegTSSize);
getMessageQueueToGUI()->push(report);
}
}
else
{
m_settings.m_tsFileName.clear();
qDebug() << "DATVModSource::openTsFile: Cannot open file: " << fileName;
}
}
void DATVModSource::seekTsFileStream(int seekPercentage)
{
if (m_tsFileOK)
{
m_frameIdx = ((m_mpegTSSize / DVBS::tsPacketLen) * seekPercentage) / 100;
m_mpegTSStream.seekg (m_frameIdx * (std::streampos)DVBS::tsPacketLen, m_mpegTSStream.beg);
m_frameCount = (int)(m_frameIdx / m_tsRatio);
}
}
void DATVModSource::reportTsFileSourceStreamTiming()
{
int framesCount = m_tsFileOK ? m_frameIdx : 0;
if (getMessageQueueToGUI())
getMessageQueueToGUI()->push(DATVModReport::MsgReportTsFileSourceStreamTiming::create(framesCount));
}
void DATVModSource::reportUDPBitrate()
{
boost::chrono::duration<double> sec = boost::chrono::steady_clock::now() - m_udpTimingStart;
double seconds = sec.count();
int bitrate = seconds > 0.0 ? m_udpByteCount * 8 / seconds : 0;
if (getMessageQueueToGUI())
getMessageQueueToGUI()->push(DATVModReport::MsgReportUDPBitrate::create(bitrate));
m_udpTimingStart = boost::chrono::steady_clock::now();
m_udpByteCount = 0;
}
void DATVModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "DATVModSource::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((channelFrequencyOffset != m_channelFrequencyOffset) ||
(channelSampleRate != m_channelSampleRate) || force)
{
m_carrierNco.setFreq(channelFrequencyOffset, channelSampleRate);
}
if ((channelSampleRate != m_channelSampleRate) || force)
{
m_sampleRate = (channelSampleRate/m_settings.m_symbolRate)*m_settings.m_symbolRate;
// Create interpolator if not integer multiple
if (m_sampleRate != channelSampleRate)
{
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_sampleRate / (Real) channelSampleRate;
m_interpolator.create(32, m_sampleRate, m_settings.m_rfBandwidth / 2.2f, 3.0);
}
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(DATVModReport::MsgReportRates::create(
channelSampleRate, m_sampleRate,
getDVBSDataBitrate(m_settings)));
}
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
m_samplesPerSymbol = m_channelSampleRate/m_settings.m_symbolRate;
m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false);
m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false);
}
void DATVModSource::applySettings(const DATVModSettings& settings, bool force)
{
qDebug() << "DATVModSource::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_rfBandwidth: " << settings.m_rfBandwidth
<< " m_source: " << (int) settings.m_source
<< " m_standard: " << (int) settings.m_standard
<< " m_modulation: " << (int) settings.m_modulation
<< " m_fec: " << (int) settings.m_fec
<< " m_symbolRate: " << (int) settings.m_symbolRate
<< " m_rollOff: " << (int) settings.m_rollOff
<< " m_tsFilePlayLoop: " << settings.m_tsFilePlayLoop
<< " m_tsFilePlay: " << settings.m_tsFilePlay
<< " m_udpAddress: " << settings.m_udpAddress
<< " m_udpPort: " << settings.m_udpPort
<< " m_channelMute: " << settings.m_channelMute
<< " force: " << force;
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth)
|| (settings.m_modulation != m_settings.m_modulation)
|| (settings.m_symbolRate != m_settings.m_symbolRate)
|| force)
{
m_sampleRate = (m_channelSampleRate/settings.m_symbolRate)*settings.m_symbolRate;
if (m_sampleRate != m_channelSampleRate)
{
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_sampleRate / (Real) m_channelSampleRate;
m_interpolator.create(32, m_sampleRate, settings.m_rfBandwidth / 2.2f, 3.0);
}
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(DATVModReport::MsgReportRates::create(
m_channelSampleRate, m_sampleRate,
getDVBSDataBitrate(settings)));
}
}
if ((settings.m_source != m_settings.m_source)
|| (settings.m_udpAddress != m_settings.m_udpAddress)
|| (settings.m_udpPort != m_settings.m_udpPort)
|| force)
{
if (m_udpSocket)
{
m_udpSocket->close();
delete m_udpSocket;
m_udpSocket = nullptr;
}
if (settings.m_source == DATVModSettings::SourceUDP)
{
m_udpSocket = new QUdpSocket();
m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort);
m_udpSocket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 1000000);
m_udpTimingStart = boost::chrono::steady_clock::now();
m_udpByteCount = 0;
}
}
if ((settings.m_standard != m_settings.m_standard)
|| (settings.m_modulation != m_settings.m_modulation)
|| force)
{
m_symbolSel = 0;
m_symbolIdx = 0;
m_symbolCount = 0;
m_sampleIdx = 0;
}
if ((settings.m_standard != m_settings.m_standard)
|| (settings.m_modulation != m_settings.m_modulation)
|| (settings.m_fec != m_settings.m_fec)
|| (settings.m_rollOff != m_settings.m_rollOff)
|| force)
{
if (settings.m_standard == DATVModSettings::DVB_S)
{
switch (settings.m_fec)
{
case DATVModSettings::FEC12:
m_dvbs.setCodeRate(DVBS::RATE_1_2);
break;
case DATVModSettings::FEC23:
m_dvbs.setCodeRate(DVBS::RATE_2_3);
break;
case DATVModSettings::FEC34:
m_dvbs.setCodeRate(DVBS::RATE_3_4);
break;
case DATVModSettings::FEC56:
m_dvbs.setCodeRate(DVBS::RATE_5_6);
break;
case DATVModSettings::FEC78:
m_dvbs.setCodeRate(DVBS::RATE_7_8);
break;
default:
qCritical() << "DATVModSource::applySettings: Unsupported FEC code rate for DVB-S: " << settings.m_fec;
break;
}
}
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(DATVModReport::MsgReportRates::create(
m_channelSampleRate, m_sampleRate,
getDVBSDataBitrate(settings)));
}
}
m_settings = settings;
m_samplesPerSymbol = m_channelSampleRate/m_settings.m_symbolRate;
m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false);
m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false);
checkBitrates();
}

View File

@ -0,0 +1,135 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODDATV_DATVMODSOURCE_H_
#define PLUGINS_CHANNELTX_MODDATV_DATVMODSOURCE_H_
#include <vector>
#include <fstream>
#include <iostream>
#include <cstdint>
#include <QObject>
#include <QMutex>
#include "dsp/channelsamplesource.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "dsp/rootraisedcosine.h"
#include "util/movingaverage.h"
#include "dsp/fftfilt.h"
#include "util/message.h"
#include "datvmodsettings.h"
#include "dvb-s/dvb-s.h"
class MessageQueue;
class QUdpSocket;
class DATVModSource : public ChannelSampleSource
{
public:
DATVModSource();
~DATVModSource();
virtual void pull(SampleVector::iterator begin, unsigned int nbSamples);
virtual void pullOne(Sample& sample);
virtual void prefetch(unsigned int nbSamples);
int getEffectiveSampleRate() const { return m_channelSampleRate; };
double getMagSq() const { return m_movingAverage.asDouble(); }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; }
void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const
{
rmsLevel = m_rmsLevel;
peakLevel = m_peakLevelOut;
numSamples = m_levelNbSamples;
}
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const DATVModSettings& settings, bool force = false);
void openTsFile(const QString& fileName);
void seekTsFileStream(int seekPercentage);
void reportTsFileSourceStreamTiming();
void reportUDPBitrate();
private:
uint8_t m_mpegTS[188]; //!< MPEG transport stream packet
std::ifstream m_mpegTSStream; //!< File to read transport stream from
int m_mpegTSBitrate; //!< Bitrate of transport stream file
std::streampos m_mpegTSSize; //!< Size in bytes of transport stream file
RootRaisedCosine<Real> m_pulseShapeI; //!< Square root raised cosine filter for I samples
RootRaisedCosine<Real> m_pulseShapeQ; //!< Square root raised cosine filter for Q samples
DVBS m_dvbs; //!< DVB-S encoder
int m_sampleIdx;
int m_frameIdx; //!< Frame index in to TS file
int m_frameCount; //!< Count of frames transmitted, including null frames
float m_tsRatio; //!< DVB data rate to TS file bitrate ratio
int m_symbolCount; //!< Number of symbols returned from DVB-S encoder
int m_symbolSel; //!< I or Q symbol for BPSK modulation only
int m_symbolIdx;
int m_samplesPerSymbol;
uint8_t m_iqSymbols[DVBS::m_maxIQSymbols*2];
QUdpSocket *m_udpSocket; //!< UDP socket to receive MPEG transport stream via
int m_udpByteCount; //!< Count of bytes received via UDP for bitrate calculation
boost::chrono::steady_clock::time_point m_udpTimingStart; //!< When we last started counting UDP bytes
uint8_t m_udpBuffer[188*10];
int m_udpBufferIdx; //!< TS frame index into buffer
int m_udpBufferCount; //!< Number of TS frames in buffer
int m_sampleRate;
int m_channelSampleRate;
int m_channelFrequencyOffset;
DATVModSettings m_settings;
NCO m_carrierNco;
Complex m_modSample;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
MovingAverageUtil<double, double, 16> m_movingAverage;
quint32 m_levelCalcCount;
qreal m_rmsLevel;
qreal m_peakLevelOut;
Real m_peakLevel;
Real m_levelSum;
bool m_tsFileOK;
MessageQueue *m_messageQueueToGUI;
static const int m_levelNbSamples;
void pullFinalize(Complex& ci, Sample& sample);
void calculateLevel(Real& sample);
void modulateSample();
int getTSBitrate(const QString& filename);
int getDVBSDataBitrate(const DATVModSettings& settings);
void checkBitrates();
MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; }
};
#endif /* PLUGINS_CHANNELTX_MODATV_DATVMOD_H_ */

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGChannelSettings.h"
#include "datvmod.h"
#include "datvmodwebapiadapter.h"
DATVModWebAPIAdapter::DATVModWebAPIAdapter()
{}
DATVModWebAPIAdapter::~DATVModWebAPIAdapter()
{}
int DATVModWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDatvModSettings(new SWGSDRangel::SWGDATVModSettings());
response.getDatvModSettings()->init();
DATVMod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int DATVModWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
DATVMod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_ATVMOD_WEBAPIADAPTER_H
#define INCLUDE_ATVMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "datvmodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class DATVModWebAPIAdapter : public ChannelWebAPIAdapter {
public:
DATVModWebAPIAdapter();
virtual ~DATVModWebAPIAdapter();
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:
DATVModSettings m_settings;
};
#endif // INCLUDE_ATVMOD_WEBAPIADAPTER_H

View File

@ -0,0 +1,650 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "dvb-s.h"
#include "util/popcount.h"
#include <string.h>
DVBS::DVBS() :
m_prbsPacketCount(0),
m_delayLine(0),
m_punctureState(0),
m_prevIQValid(false)
{
// Allocate memory for packet buffer
m_packet = new uint8_t[rsPacketLen];
// Allocate memory for interleaver FIFOs
int unitSize = rsPacketLen/interleaveDepth;
m_interleaveFIFO = new uint8_t *[interleaveDepth];
m_interleaveLen = new int[interleaveDepth];
m_interleaveIdx = new int[interleaveDepth];
for (int i = 1; i < interleaveDepth; i++)
{
m_interleaveFIFO[i] = new uint8_t[unitSize*i]();
m_interleaveLen[i] = unitSize*i;
m_interleaveIdx[i] = 0;
}
}
DVBS::~DVBS()
{
// Free interleaver FIFO memory
for (int i = 1; i < interleaveDepth; i++)
delete m_interleaveFIFO[i];
delete m_interleaveIdx;
delete m_interleaveLen;
delete m_interleaveFIFO;
// Free packet buffer
delete m_packet;
}
// Scramble input packet (except for sync bytes) with psuedo random binary sequence
// Initiliase PRBS sequence every 8 packets and invert sync byte
void DVBS::scramble(const uint8_t *packetIn, uint8_t *packetOut)
{
if (m_prbsPacketCount == 0)
{
// Init
m_prbsIdx = 0;
// Invert sync byte to reset receiver
packetOut[0] = ~tsSync;
}
else
{
// PRBS continues, but isn't applied to sync bytes 1-7
m_prbsIdx++;
packetOut[0] = tsSync;
}
m_prbsPacketCount++;
if (m_prbsPacketCount == 8)
m_prbsPacketCount = 0;
// Apply PRBS to data
for (int i = 1; i < tsPacketLen; i++)
packetOut[i] = packetIn[i] ^ m_prbsLUT[m_prbsIdx++];
}
// RS(204,188,t=8) shortened code from RS(255,239,t=8) code
// Using GF(2^8=256) so 1 symbol is 1 byte
// Code generator polynomial: (x+l^0)(x+l^1)..(x+l^15) l=0x02
// Field generator polynomial: x^8+x^4+x^3+x^2+1 (a primitive polynomial)
// Add 51 zero bytes before 188-byte packet to pad to 239 bytes, which are discarded after RS encoding
// n=255 k=239
// RS adds 2t = 16 bytes (n-k) after original data (so is systematic)
// t=8 means we can correct 8 bytes if we don't know where the errors are
// or 2t=16 errors if we do no where the errors are
// https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders
// Galois field multiply
uint8_t DVBS::gfMul(uint8_t a, uint8_t b)
{
if ((a == 0) || (b == 0))
return 0;
return m_gfExp[m_gfLog[a] + m_gfLog[b]];
}
// Reed Solomon encoder
void DVBS::reedSolomon(uint8_t *packet)
{
uint8_t tmp[rsN];
const int zeroPadding = rsK - tsPacketLen;
const int partityBytesIdx = zeroPadding + tsPacketLen;
// Zero pad from 188 to 239 bytes
memset(tmp, 0, zeroPadding);
// Copy in packet
memcpy(&tmp[zeroPadding], packet, tsPacketLen);
// Zero parity bytes
memset(&tmp[partityBytesIdx], 0, rs2T);
// Divide generator polynomial by input to compute parity bytes
for (int i = 0; i < rsK; i++)
{
uint8_t coef = tmp[i];
if (coef != 0)
{
for (int j = 0; j < rs2T; j++)
tmp[i+j+1] ^= gfMul(rsPoly[j], coef);
}
}
// Append parity bytes to message
memcpy(&packet[tsPacketLen], &tmp[partityBytesIdx], rs2T);
}
// Forney interleaver
// We have interleaveDepth FIFOs, each of a different length, that the data is split between
// FIFO n's length is n*rsPacketLen/interleaveDepth elements
void DVBS::interleave(uint8_t *packet)
{
uint8_t *p = packet;
for (int j = 0; j < rsPacketLen; j += interleaveDepth)
{
p++;
for (int i = 1; i < interleaveDepth; i++)
{
uint8_t tmp = m_interleaveFIFO[i][m_interleaveIdx[i]];
m_interleaveFIFO[i][m_interleaveIdx[i]] = *p;
*p = tmp;
m_interleaveIdx[i] = (m_interleaveIdx[i] + 1) % m_interleaveLen[i];
p++;
}
}
}
// Convolution encoder
// Rate 1/2 with puncturing
// Constraint length K=7 G1=171 G2=133
// Similar to CCSDS (without inversion of C2)
// Using popcount is faster than state/output LUTs
int DVBS::convolution(const uint8_t *packet, uint8_t *iq)
{
const int g1 = 0x79;
const int g2 = 0x5b;
const int k = 7;
uint8_t *iqStart = iq;
int bit;
// Output byte left over from last frame
if (m_prevIQValid)
*iq++ = m_prevIQ;
switch (m_codeRate)
{
case RATE_1_2:
for (int i = 0; i < rsPacketLen; i++)
{
for (int j = 7; j >= 0; j--)
{
bit = (packet[i] >> j) & 1;
m_delayLine = m_delayLine | (bit << (k-1));
*iq++ = popcount(m_delayLine & g1) & 1;
*iq++ = popcount(m_delayLine & g2) & 1;
m_delayLine >>= 1;
}
}
break;
case RATE_2_3:
for (int i = 0; i < rsPacketLen; i++)
{
for (int j = 7; j >= 0; j--)
{
bit = (packet[i] >> j) & 1;
m_delayLine = m_delayLine | (bit << (k-1));
switch (m_punctureState)
{
case 0:
*iq++ = popcount(m_delayLine & g1) & 1;
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 1:
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState = 0;
break;
}
m_delayLine >>= 1;
}
}
break;
case RATE_3_4:
for (int i = 0; i < rsPacketLen; i++)
{
for (int j = 7; j >= 0; j--)
{
bit = (packet[i] >> j) & 1;
m_delayLine = m_delayLine | (bit << (k-1));
switch (m_punctureState)
{
case 0:
*iq++ = popcount(m_delayLine & g1) & 1;
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 1:
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 2:
*iq++ = popcount(m_delayLine & g1) & 1;
m_punctureState = 0;
break;
}
m_delayLine >>= 1;
}
}
break;
case RATE_5_6:
for (int i = 0; i < rsPacketLen; i++)
{
for (int j = 7; j >= 0; j--)
{
bit = (packet[i] >> j) & 1;
m_delayLine = m_delayLine | (bit << (k-1));
switch (m_punctureState)
{
case 0:
*iq++ = popcount(m_delayLine & g1) & 1;
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 1:
case 3:
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 2:
*iq++ = popcount(m_delayLine & g1) & 1;
m_punctureState++;
break;
case 4:
*iq++ = popcount(m_delayLine & g1) & 1;
m_punctureState = 0;
break;
}
m_delayLine >>= 1;
}
}
break;
case RATE_7_8:
for (int i = 0; i < rsPacketLen; i++)
{
for (int j = 7; j >= 0; j--)
{
bit = (packet[i] >> j) & 1;
m_delayLine = m_delayLine | (bit << (k-1));
switch (m_punctureState)
{
case 0:
*iq++ = popcount(m_delayLine & g1) & 1;
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 1:
case 2:
case 3:
case 5:
*iq++ = popcount(m_delayLine & g2) & 1;
m_punctureState++;
break;
case 4:
*iq++ = popcount(m_delayLine & g1) & 1;
m_punctureState++;
break;
case 6:
*iq++ = popcount(m_delayLine & g1) & 1;
m_punctureState = 0;
break;
}
m_delayLine >>= 1;
}
}
break;
}
int iqBytes = iq - iqStart;
// If puncturing has left half a symbol, save it for next packet
if (iqBytes & 1)
{
m_prevIQ = iq[-1];
m_prevIQValid = true;
}
else
m_prevIQValid = false;
return iqBytes >> 1;
}
// Set convolution encoder forward error correction (FEC) code rate
// Rate 1/2 = 1 input bit produces 2 output bits
void DVBS::setCodeRate(CodeRate codeRate)
{
m_codeRate = codeRate;
m_delayLine = 0;
m_punctureState = 0;
m_prevIQValid = false;
}
// DVB-S packet encoder.
// Encode 188 byte MPEG transport stream to IQ symbols
// IQ symbols are 0 or 1 and interleaved (I0, Q0, I1, Q1...)
// Number of IQ symbols is returned from function and depends on FEC rate
// Maximum number is (188+16)*8=1632 when FEC rate is 1/2
// (This is IQ symbols, so 3264 elements needed in iq array)
int DVBS::encode(const uint8_t *tsStreamPacket, uint8_t *iq)
{
scramble(tsStreamPacket, m_packet);
reedSolomon(m_packet);
interleave(m_packet);
return convolution(m_packet, iq);
}
// Pseudo random sequence look-up table
// Generated with RandomizeDVBS in util/lfsr.h
const uint8_t DVBS::m_prbsLUT[tsPacketLen*8-1] =
{
0x03, 0xf6, 0x08, 0x34, 0x30, 0xb8, 0xa3, 0x93,
0xc9, 0x68, 0xb7, 0x73, 0xb3, 0x29, 0xaa, 0xf5,
0xfe, 0x3c, 0x04, 0x88, 0x1b, 0x30, 0x5a, 0xa1,
0xdf, 0xc4, 0xc0, 0x9a, 0x83, 0x5f, 0x0b, 0xc2,
0x38, 0x8c, 0x93, 0x2b, 0x6a, 0xfb, 0x7e, 0x1b,
0x04, 0x5a, 0x19, 0xdc, 0x54, 0xc9, 0xfa, 0xb4,
0x1f, 0xb8, 0x41, 0x91, 0x85, 0x65, 0x1f, 0x5e,
0x43, 0xc5, 0x88, 0x9d, 0x33, 0x4e, 0xab, 0xa7,
0xf9, 0xd0, 0x14, 0xe0, 0x7a, 0x41, 0x1d, 0x86,
0x4d, 0x15, 0xae, 0x7d, 0xe5, 0x0c, 0x5e, 0x29,
0xc4, 0xf4, 0x9a, 0x3b, 0x5c, 0x9b, 0xcb, 0x58,
0xbb, 0xd3, 0x98, 0xe9, 0x52, 0x77, 0xed, 0x30,
0x6e, 0xa1, 0x67, 0xc7, 0x50, 0x93, 0xe3, 0x68,
0x4b, 0x71, 0xbb, 0x25, 0x9a, 0xdd, 0x5e, 0xcf,
0xc6, 0xa0, 0x97, 0xc3, 0x70, 0x8b, 0x23, 0x3a,
0xca, 0x9e, 0xbf, 0x47, 0x83, 0x91, 0x09, 0x66,
0x37, 0x54, 0xb3, 0xfb, 0xa8, 0x19, 0xf0, 0x54,
0x21, 0xf8, 0xc4, 0x12, 0x98, 0x6f, 0x51, 0x63,
0xe7, 0x48, 0x53, 0xb1, 0xe9, 0xa4, 0x75, 0xd9,
0x3c, 0xd6, 0x8a, 0xf7, 0x3e, 0x32, 0x84, 0xaf,
0x1b, 0xe2, 0x58, 0x4d, 0xd1, 0xac, 0xe5, 0xea,
0x5c, 0x7d, 0xc9, 0x0c, 0xb6, 0x2b, 0xb4, 0xf9,
0xba, 0x15, 0x9c, 0x7d, 0x49, 0x0f, 0xb6, 0x21,
0xb4, 0xc5, 0xba, 0x9d, 0x9f, 0x4d, 0x43, 0xaf,
0x89, 0xe1, 0x34, 0x46, 0xb9, 0x97, 0x95, 0x71,
0x7f, 0x27, 0x02, 0xd2, 0x0e, 0xec, 0x26, 0x68,
0xd5, 0x72, 0xff, 0x2e, 0x02, 0xe4, 0x0e, 0x58,
0x25, 0xd0, 0xdc, 0xe2, 0xca, 0x4e, 0xbd, 0xa7,
0x8d, 0xd1, 0x2c, 0xe6, 0xea, 0x56, 0x7d, 0xf5,
0x0c, 0x3e, 0x28, 0x84, 0xf3, 0x1a, 0x2a, 0x5c,
0xfd, 0xca, 0x0c, 0xbc, 0x2b, 0x88, 0xf9, 0x32,
0x16, 0xac, 0x77, 0xe9, 0x30, 0x76, 0xa1, 0x37,
0xc6, 0xb0, 0x97, 0xa3, 0x71, 0xcb, 0x24, 0xba,
0xdb, 0x9e, 0xd9, 0x46, 0xd7, 0x96, 0xf1, 0x76,
0x27, 0x34, 0xd2, 0xba, 0xef, 0x9e, 0x61, 0x45,
0x47, 0x9f, 0x91, 0x41, 0x67, 0x87, 0x51, 0x13,
0xe6, 0x68, 0x55, 0x71, 0xff, 0x24, 0x02, 0xd8,
0x0e, 0xd0, 0x26, 0xe0, 0xd6, 0x42, 0xf5, 0x8e,
0x3d, 0x24, 0x8e, 0xdb, 0x26, 0xda, 0xd6, 0xde,
0xf6, 0xc6, 0x36, 0x94, 0xb7, 0x7b, 0xb3, 0x19,
0xaa, 0x55, 0xfd, 0xfc, 0x0c, 0x08, 0x28, 0x30,
0xf0, 0xa2, 0x23, 0xcc, 0xc8, 0xaa, 0xb3, 0xff,
0xa8, 0x01, 0xf0, 0x04, 0x20, 0x18, 0xc0, 0x52,
0x81, 0xef, 0x04, 0x62, 0x19, 0x4c, 0x57, 0xa9,
0xf1, 0xf4, 0x24, 0x38, 0xd8, 0x92, 0xd3, 0x6e,
0xeb, 0x66, 0x7b, 0x55, 0x1b, 0xfe, 0x58, 0x05,
0xd0, 0x1c, 0xe0, 0x4a, 0x41, 0xbd, 0x85, 0x8d,
0x1d, 0x2e, 0x4e, 0xe5, 0xa6, 0x5d, 0xd5, 0xcc,
0xfc, 0xaa, 0x0b, 0xfc, 0x38, 0x08, 0x90, 0x33,
0x60, 0xab, 0x43, 0xfb, 0x88, 0x19, 0x30, 0x56,
0xa1, 0xf7, 0xc4, 0x30, 0x98, 0xa3, 0x53, 0xcb,
0xe8, 0xb8, 0x73, 0x91, 0x29, 0x66, 0xf7, 0x56,
0x33, 0xf4, 0xa8, 0x3b, 0xf0, 0x98, 0x23, 0x50,
0xcb, 0xe2, 0xb8, 0x4f, 0x91, 0xa1, 0x65, 0xc7,
0x5c, 0x93, 0xcb, 0x68, 0xbb, 0x73, 0x9b, 0x29,
0x5a, 0xf7, 0xde, 0x30, 0xc4, 0xa2, 0x9b, 0xcf,
0x58, 0xa3, 0xd3, 0xc8, 0xe8, 0xb2, 0x73, 0xad,
0x29, 0xee, 0xf4, 0x66, 0x39, 0x54, 0x97, 0xfb,
0x70, 0x1b, 0x20, 0x5a, 0xc1, 0xde, 0x84, 0xc7,
0x1a, 0x92, 0x5f, 0x6d, 0xc3, 0x6c, 0x8b, 0x6b,
0x3b, 0x7a, 0x9b, 0x1f, 0x5a, 0x43, 0xdd, 0x88,
0xcd, 0x32, 0xae, 0xaf, 0xe7, 0xe0, 0x50, 0x41,
0xe1, 0x84, 0x45, 0x19, 0x9e, 0x55, 0x45, 0xff,
0x9c, 0x01, 0x48, 0x07, 0xb0, 0x11, 0xa0, 0x65,
0xc1, 0x5c, 0x87, 0xcb, 0x10, 0xba, 0x63, 0x9d,
0x49, 0x4f, 0xb7, 0xa1, 0xb1, 0xc5, 0xa4, 0x9d,
0xdb, 0x4c, 0xdb, 0xaa, 0xd9, 0xfe, 0xd4, 0x06,
0xf8, 0x16, 0x10, 0x74, 0x61, 0x39, 0x46, 0x97,
0x97, 0x71, 0x73, 0x27, 0x2a, 0xd2, 0xfe, 0xee,
0x06, 0x64, 0x15, 0x58, 0x7f, 0xd1, 0x00, 0xe6,
0x02, 0x54, 0x0d, 0xf8, 0x2c, 0x10, 0xe8, 0x62,
0x71, 0x4d, 0x27, 0xae, 0xd1, 0xe6, 0xe4, 0x56,
0x59, 0xf5, 0xd4, 0x3c, 0xf8, 0x8a, 0x13, 0x3c,
0x6a, 0x89, 0x7f, 0x37, 0x02, 0xb2, 0x0f, 0xac,
0x21, 0xe8, 0xc4, 0x72, 0x99, 0x2f, 0x56, 0xe3,
0xf6, 0x48, 0x35, 0xb0, 0xbd, 0xa3, 0x8d, 0xc9,
0x2c, 0xb6, 0xeb, 0xb6, 0x79, 0xb5, 0x15, 0xbe,
0x7d, 0x85, 0x0d, 0x1e, 0x2e, 0x44, 0xe5, 0x9a,
0x5d, 0x5d, 0xcf, 0xcc, 0xa0, 0xab, 0xc3, 0xf8,
0x88, 0x13, 0x30, 0x6a, 0xa1, 0x7f, 0xc7, 0x00,
0x92, 0x03, 0x6c, 0x0b, 0x68, 0x3b, 0x70, 0x9b,
0x23, 0x5a, 0xcb, 0xde, 0xb8, 0xc7, 0x92, 0x91,
0x6f, 0x67, 0x63, 0x53, 0x4b, 0xeb, 0xb8, 0x79,
0x91, 0x15, 0x66, 0x7f, 0x55, 0x03, 0xfe, 0x08,
0x04, 0x30, 0x18, 0xa0, 0x53, 0xc1, 0xe8, 0x84,
0x73, 0x19, 0x2a, 0x56, 0xfd, 0xf6, 0x0c, 0x34,
0x28, 0xb8, 0xf3, 0x92, 0x29, 0x6c, 0xf7, 0x6a,
0x33, 0x7c, 0xab, 0x0b, 0xfa, 0x38, 0x1c, 0x90,
0x4b, 0x61, 0xbb, 0x45, 0x9b, 0x9d, 0x59, 0x4f,
0xd7, 0xa0, 0xf1, 0xc2, 0x24, 0x8c, 0xdb, 0x2a,
0xda, 0xfe, 0xde, 0x06, 0xc4, 0x16, 0x98, 0x77,
0x51, 0x33, 0xe6, 0xa8, 0x57, 0xf1, 0xf0, 0x24,
0x20, 0xd8, 0xc2, 0xd2, 0x8e, 0xef, 0x26, 0x62,
0xd5, 0x4e, 0xff, 0xa6, 0x01, 0xd4, 0x04, 0xf8,
0x1a, 0x10, 0x5c, 0x61, 0xc9, 0x44, 0xb7, 0x9b,
0xb1, 0x59, 0xa7, 0xd5, 0xd0, 0xfc, 0xe2, 0x0a,
0x4c, 0x3d, 0xa8, 0x8d, 0xf3, 0x2c, 0x2a, 0xe8,
0xfe, 0x72, 0x05, 0x2c, 0x1e, 0xe8, 0x46, 0x71,
0x95, 0x25, 0x7e, 0xdf, 0x06, 0xc2, 0x16, 0x8c,
0x77, 0x29, 0x32, 0xf6, 0xae, 0x37, 0xe4, 0xb0,
0x5b, 0xa1, 0xd9, 0xc4, 0xd4, 0x9a, 0xfb, 0x5e,
0x1b, 0xc4, 0x58, 0x99, 0xd3, 0x54, 0xeb, 0xfa,
0x78, 0x1d, 0x10, 0x4e, 0x61, 0xa5, 0x45, 0xdf,
0x9c, 0xc1, 0x4a, 0x87, 0xbf, 0x11, 0x82, 0x65,
0x0d, 0x5e, 0x2f, 0xc4, 0xe0, 0x9a, 0x43, 0x5d,
0x8b, 0xcd, 0x38, 0xae, 0x93, 0xe7, 0x68, 0x53,
0x71, 0xeb, 0x24, 0x7a, 0xd9, 0x1e, 0xd6, 0x46,
0xf5, 0x96, 0x3d, 0x74, 0x8f, 0x3b, 0x22, 0x9a,
0xcf, 0x5e, 0xa3, 0xc7, 0xc8, 0x90, 0xb3, 0x63,
0xab, 0x49, 0xfb, 0xb4, 0x19, 0xb8, 0x55, 0x91,
0xfd, 0x64, 0x0f, 0x58, 0x23, 0xd0, 0xc8, 0xe2,
0xb2, 0x4f, 0xad, 0xa1, 0xed, 0xc4, 0x6c, 0x99,
0x6b, 0x57, 0x7b, 0xf3, 0x18, 0x2a, 0x50, 0xfd,
0xe2, 0x0c, 0x4c, 0x29, 0xa8, 0xf5, 0xf2, 0x3c,
0x2c, 0x88, 0xeb, 0x32, 0x7a, 0xad, 0x1f, 0xee,
0x40, 0x65, 0x81, 0x5d, 0x07, 0xce, 0x10, 0xa4,
0x63, 0xd9, 0x48, 0xd7, 0xb2, 0xf1, 0xae, 0x25,
0xe4, 0xdc, 0x5a, 0xc9, 0xde, 0xb4, 0xc7, 0xba,
0x91, 0x9f, 0x65, 0x43, 0x5f, 0x8b, 0xc1, 0x38,
0x86, 0x93, 0x17, 0x6a, 0x73, 0x7d, 0x2b, 0x0e,
0xfa, 0x26, 0x1c, 0xd4, 0x4a, 0xf9, 0xbe, 0x15,
0x84, 0x7d, 0x19, 0x0e, 0x56, 0x25, 0xf4, 0xdc,
0x3a, 0xc8, 0x9e, 0xb3, 0x47, 0xab, 0x91, 0xf9,
0x64, 0x17, 0x58, 0x73, 0xd1, 0x28, 0xe6, 0xf2,
0x56, 0x2d, 0xf4, 0xec, 0x3a, 0x68, 0x9d, 0x73,
0x4f, 0x2b, 0xa2, 0xf9, 0xce, 0x14, 0xa4, 0x7b,
0xd9, 0x18, 0xd6, 0x52, 0xf5, 0xee, 0x3c, 0x64,
0x89, 0x5b, 0x37, 0xda, 0xb0, 0xdf, 0xa2, 0xc1,
0xce, 0x84, 0xa7, 0x1b, 0xd2, 0x58, 0xed, 0xd2,
0x6c, 0xed, 0x6a, 0x6f, 0x7d, 0x63, 0x0f, 0x4a,
0x23, 0xbc, 0xc9, 0x8a, 0xb5, 0x3f, 0xbe, 0x81,
0x87, 0x05, 0x12, 0x1e, 0x6c, 0x45, 0x69, 0x9f,
0x75, 0x43, 0x3f, 0x8a, 0x81, 0x3f, 0x06, 0x82,
0x17, 0x0c, 0x72, 0x29, 0x2c, 0xf6, 0xea, 0x36,
0x7c, 0xb5, 0x0b, 0xbe, 0x39, 0x84, 0x95, 0x1b,
0x7e, 0x5b, 0x05, 0xda, 0x1c, 0xdc, 0x4a, 0xc9,
0xbe, 0xb5, 0x87, 0xbd, 0x11, 0x8e, 0x65, 0x25,
0x5e, 0xdf, 0xc6, 0xc0, 0x96, 0x83, 0x77, 0x0b,
0x32, 0x3a, 0xac, 0x9f, 0xeb, 0x40, 0x7b, 0x81,
0x19, 0x06, 0x56, 0x15, 0xf4, 0x7c, 0x39, 0x08,
0x96, 0x33, 0x74, 0xab, 0x3b, 0xfa, 0x98, 0x1f,
0x50, 0x43, 0xe1, 0x88, 0x45, 0x31, 0x9e, 0xa5,
0x47, 0xdf, 0x90, 0xc1, 0x62, 0x87, 0x4f, 0x13,
0xa2, 0x69, 0xcd, 0x74, 0xaf, 0x3b, 0xe2, 0x98,
0x4f, 0x51, 0xa3, 0xe5, 0xc8, 0x5c, 0xb1, 0xcb,
0xa4, 0xb9, 0xdb, 0x94, 0xd9, 0x7a, 0xd7, 0x1e,
0xf2, 0x46, 0x2d, 0x94, 0xed, 0x7a, 0x6f, 0x1d,
0x62, 0x4f, 0x4d, 0xa3, 0xad, 0xc9, 0xec, 0xb4,
0x6b, 0xb9, 0x79, 0x97, 0x15, 0x72, 0x7f, 0x2d,
0x02, 0xee, 0x0e, 0x64, 0x25, 0x58, 0xdf, 0xd2,
0xc0, 0xee, 0x82, 0x67, 0x0d, 0x52, 0x2f, 0xec,
0xe0, 0x6a, 0x41, 0x7d, 0x87, 0x0d, 0x12, 0x2e,
0x6c, 0xe5, 0x6a, 0x5f, 0x7d, 0xc3, 0x0c, 0x8a,
0x2b, 0x3c, 0xfa, 0x8a, 0x1f, 0x3c, 0x42, 0x89,
0x8f, 0x35, 0x22, 0xbe, 0xcf, 0x86, 0xa1, 0x17,
0xc6, 0x70, 0x95, 0x23, 0x7e, 0xcb, 0x06, 0xba,
0x17, 0x9c, 0x71, 0x49, 0x27, 0xb6, 0xd1, 0xb6,
0xe5, 0xb6, 0x5d, 0xb5, 0xcd, 0xbc, 0xad, 0x8b,
0xed, 0x38, 0x6e, 0x91, 0x67, 0x67, 0x53, 0x53,
0xeb, 0xe8, 0x78, 0x71, 0x11, 0x26, 0x66, 0xd5,
0x56, 0xff, 0xf6, 0x00, 0x34, 0x00, 0xb8, 0x03,
0x90, 0x09, 0x60, 0x37, 0x40, 0xb3, 0x83, 0xa9,
0x09, 0xf6, 0x34, 0x34, 0xb8, 0xbb, 0x93, 0x99,
0x69, 0x57, 0x77, 0xf3, 0x30, 0x2a, 0xa0, 0xff,
0xc2, 0x00, 0x8c, 0x03, 0x28, 0x0a, 0xf0, 0x3e,
0x20, 0x84, 0xc3, 0x1a, 0x8a, 0x5f, 0x3d, 0xc2,
0x8c, 0x8f, 0x2b, 0x22, 0xfa, 0xce, 0x1e, 0xa4,
0x47, 0xd9, 0x90, 0xd5, 0x62, 0xff, 0x4e, 0x03,
0xa4, 0x09, 0xd8, 0x34, 0xd0, 0xba, 0xe3, 0x9e,
0x49, 0x45, 0xb7, 0x9d, 0xb1, 0x4d, 0xa7, 0xad,
0xd1, 0xec, 0xe4, 0x6a, 0x59, 0x7d, 0xd7, 0x0c,
0xf2, 0x2a, 0x2c, 0xfc, 0xea, 0x0a, 0x7c, 0x3d,
0x08, 0x8e, 0x33, 0x24, 0xaa, 0xdb, 0xfe, 0xd8,
0x06, 0xd0, 0x16, 0xe0, 0x76, 0x41, 0x35, 0x86,
0xbd, 0x17, 0x8e, 0x71, 0x25, 0x26, 0xde, 0xd6,
0xc6, 0xf6, 0x96, 0x37, 0x74, 0xb3, 0x3b, 0xaa,
0x99, 0xff, 0x54, 0x03, 0xf8, 0x08, 0x10, 0x30,
0x60, 0xa1, 0x43, 0xc7, 0x88, 0x91, 0x33, 0x66,
0xab, 0x57, 0xfb, 0xf0, 0x18, 0x20, 0x50, 0xc1,
0xe2, 0x84, 0x4f, 0x19, 0xa2, 0x55, 0xcd, 0xfc,
0xac, 0x0b, 0xe8, 0x38, 0x70, 0x91, 0x23, 0x66,
0xcb, 0x56, 0xbb, 0xf7, 0x98, 0x31, 0x50, 0xa7,
0xe3, 0xd0, 0x48, 0xe1, 0xb2, 0x45, 0xad, 0x9d,
0xed, 0x4c, 0x6f, 0xa9, 0x61, 0xf7, 0x44, 0x33,
0x98, 0xa9, 0x53, 0xf7, 0xe8, 0x30, 0x70, 0xa1,
0x23, 0xc6, 0xc8, 0x96, 0xb3, 0x77, 0xab, 0x31,
0xfa, 0xa4, 0x1f, 0xd8, 0x40, 0xd1, 0x82, 0xe5,
0x0e, 0x5e, 0x25, 0xc4, 0xdc, 0x9a, 0xcb,
};
// Generator polynmoial
const uint8_t DVBS::rsPoly[DVBS::rs2T] =
{
59, 13, 104, 189, 68, 209, 30, 8, 163, 65, 41, 229, 98, 50, 36, 59
};
// Galios Field logarithm
const uint8_t DVBS::m_gfLog[256] =
{
0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
};
// Galios Field exponent
const uint8_t DVBS::m_gfExp[512] =
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01,
0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d,
0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c,
0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f,
0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x9d,
0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a,
0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46,
0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d,
0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f,
0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65,
0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0xfd,
0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe,
0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9,
0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d,
0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81,
0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b,
0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85,
0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f,
0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8,
0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49,
0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6,
0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc,
0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3,
0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95,
0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82,
0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c,
0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51,
0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3,
0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 0x12,
0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7,
0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 0x2c,
0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b,
0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01, 0x02
};

View File

@ -0,0 +1,77 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 <cstdint>
// DVB-S coder - See ETSI EN 300 421 V1.1.2
class DVBS {
static const uint8_t tsSync = 0x47; //!< Transport stream sync byte - should be at start of every TS packet
static const int interleaveDepth = 12; //!< Interleaving depth
static const int rsK = 239; //!< Reed-Solomon input length
static const int rs2T = 16; //!< Reed-Solomon number check/parity symbols
static const int rsN = rsK + rs2T; //!< Reed-Solomon block length
public:
static const int tsPacketLen = 188; //!< Length of transport stream packets
static const int rsPacketLen = 204; //!< Length of packet after adding RS parity bytes
static const int m_maxIQSymbols = 204*8; //!< Maximum number of IQ symbols per packet
enum CodeRate {RATE_1_2, RATE_2_3, RATE_3_4, RATE_5_6, RATE_7_8};
DVBS();
~DVBS();
int encode(const uint8_t *ts, uint8_t *iq);
void setCodeRate(CodeRate codeRate);
private:
uint8_t *m_packet; //!< Buffer to hold input data + partiy bytes
int m_prbsPacketCount; //!< Counts 0-7
int m_prbsIdx; //!< Index in to m_prbsLUT
uint8_t **m_interleaveFIFO; //!< FIFOs for convolutional interleaving
int *m_interleaveLen; //!< Length of each FIFO
int *m_interleaveIdx; //!< Write index in to FIFOs
CodeRate m_codeRate; //!< Convolution coding rate
unsigned m_delayLine; //!< Delay line for convolution encoder
int m_punctureState; //!< Puncturing state
uint8_t m_prevIQ; //!< Saved bit from previous packet
bool m_prevIQValid; //!< Whether there is a saved bit
protected:
static const uint8_t m_prbsLUT[tsPacketLen*8-1]; //!< PRBS lookup table
static const uint8_t rsPoly[rs2T]; //!< Reed-Solomon generator polynomial
static const uint8_t m_gfLog[256]; //!< GF(2^8) log lookup table
static const uint8_t m_gfExp[512]; //!< GF(2^8) exponent lookup table
static uint8_t gfMul(uint8_t a, uint8_t b); //!< GF(2^8) multiplication
void scramble(const uint8_t *packetIn, uint8_t *packetOut);
void reedSolomon(uint8_t *packet);
void interleave(uint8_t *packet);
int convolution(const uint8_t *packet, uint8_t *iq);
};

View File

@ -0,0 +1,132 @@
<h1>DATV modulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to transmit a digital amateur TV signal in the DVB-S standard. The plugin requires the video and audio to be transmitted to be in an MPEG transport stream.
The MPEG transport stream can either be read from a file or streamed via UDP.
The MPEG transport stream must (for now) be created outside of SDRangel, using software such as ffmpeg. The MPEG transport stream can contain video compressed using codecs such as MPEG-2, h264 or h265 (HEVC).
Similarly, audio can be MPEG-2, MP3 or Opus. Settings such as the video resolution are determined by the software used to create the MPEG transport stream.
The DATV modulator plugin just performs channel coding and modulation of the transport stream according to the DVB-S standard.
DVB-S includes: scrambling, Reed-Solomon RS(204,188,T=8) coding, convolutional interleaving (I=12), convolutional encoding (code rate=1/2, with optional puncturing to rates of 2/3, 3/4, 5/6 and 7/8) and BPSK or QPSK modulation.
<h2>Interface</h2>
![DATV Modulator plugin GUI](../../../doc/img/DATVMod_plugin.png)
<h3>1: Frequency shift from center frequency of transmission</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of transmission. 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: Sample rate</h3>
This figure is the sample rate in kS/s used by the modulator. The modulator requires a sample rate that is an integer multiple of the symbol rate.
If this rate differs from the baseband sample rate, a rational interpolator will be used to upsample.
<h3>3: Data rate</h3>
This figure is the data rate supported by the DVB-S channel, as determined by the settings below.
This is the maximum rate a transport stream can be transmitted at, so this value should be greater or equal to the bitrate required for the transport stream, if real-time decoding at the receiver is required.
If this rate is greater than the bitrate required for a transport stream file, the modulator will transmit null packets to try to transmit the stream at the required rate.
If the transport stream is via UDP, null packets will be transmitted when no data is available on the UDP port.
<h3>4: Channel power</h3>
Average total power in dB relative to a &#177;1.0 amplitude signal generated in the pass band.
<h3>5: Channel mute</h3>
Use this button to toggle mute for this channel. The radio waves on the icon are toggled on (active) and off (muted) accordingly. Default is channel active.
<h3>6: Standard</h3>
Select the DVB standard to use for channel coding and modulation. Currenty only DVB-S is supported.
<h3>7: Symbol rate</h3>
Specifies the symbol rate in symbols per second. Higher symbol rates allow for higher bitrate transport streams to be transmitted, but require a greater bandwidth.
<h3>8: Bandwidth</h3>
Specifies the bandwidth of a filter applied to the modulated output signal when interpolation takes place (i.e. when the sample rate (2) is not equal to the baseband sample rate). Otherwise the full baseband bandwidth is used.
<h3>9: Transport Stream Source</h3>
This combo box lets you choose the source of the MPEG transport stream:
- File: transport stream file read from the file selected with button (16).
- UDP: transport stream received via UDP port (14).
When using UDP, the packet size should be an integer multiple of the MPEG transport stream packet size, which is 188 bytes. 1316 bytes is a common value.
<h3>10: FEC</h3>
Forward error correction code rate. This controls the number of bits sent to help the receiver to correct errors.
A code rate of 1/2 has the highest overhead (corresponding to a lower data rate), but allows the most amount of errors to be correct.
7/8 has the least overhead (corresponding to higher data rates), but will allow the fewest amount of errors to be corrected.
<h3>11: Modulation</h3>
Select the modulation to be used. This can either be BPSK or QPSK. BPSK transmits a single bit per symbol, whereas QPSK transmits two bits per symbol, so has twice the bitrate.
<h3>12: Roll off</h3>
Roll-off for the root raised cosine filter. For DVB-S, this should be 0.35.
<h3>13: UDP IP address</h3>
Set the IP address of the network interface/adaptor to bind the UDP socket to.
<h3>14: UDP port</h3>
Set the UDP port number the UDP socket will be opened on. This is the port the transport stream will need to be sent to.
<h3>15: UDP bitrate</h3>
This displays the bitrate at which data is being received via the UDP port.
<h3>16: Transport stream file select</h3>
Clicking on this button will open a file dialog to let you choose an MPEG transport stream file to transmit. When the dialog is closed and the choice is validated the name of the file will appear on the space at the right of the button.
<h3>17: Play loop</h3>
Use this button to toggle on/off transmitting of the transport stream file in a loop.
<h3>18: Play/Pause</h3>
Use this button to play or pause the transport stream file.
<h3>19: Current transport stream file position</h3>
This is the current transport stream file position in time units relative to the start.
<h3>20: Transport stream file bitrate</h3>
This is the bitrate in kb/s of the transport stream file. This should be less or equal to the DVB data rate (3).
<h3>21: Transport stream file length</h3>
This is the length of the transport stream file in time units
<h3>22: Transport stream file position slider</h3>
This slider can be used to randomly set the current position in the file when file play is in pause state (button 18). When the transport stream is transmitted, the slider moves according to the current position.
<h2>Creating an MPEG transport stream</h2>
An MPEG transport stream file can be created from a video file using ffpmeg:
ffmpeg -i input.avi -pix_fmt yuv420p -r 25 -s 720x576 -aspect 4:3 -c:v hevc -c:a libopus -b:v 500k -b:a 64k -maxrate 600k -bufsize 50k -f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="SDRangel" -metadata service_name="SDRangel TV" -y mpeg.ts
To stream from a video camera via UDP (on Windows):
ffmpeg -f dshow -i video="c922 Pro Stream Webcam":audio="Microphone (C922 Pro Stream Webcam)" -pix_fmt yuv420p -r 25 -s 720x576 -aspect 4:3 -c:v hevc -c:a libopus -b:v 500k -b:a 64k -maxrate 600k -bufsize 50k -f mpegts -mpegts_original_network_id 1 -mpegts_transport_stream_id 1 -mpegts_service_id 1 -mpegts_pmt_start_pid 4096 -streamid 0:289 -streamid 1:337 -metadata service_provider="SDRangel" -metadata service_name="SDRangel TV" -flush_packets 0 "udp://127.0.0.1:5004?pkt_size=1316&bitrate=600000"
You can list camera devices with:
ffmpeg -list_devices true -f dshow -i dummy

View File

@ -322,6 +322,7 @@ set(sdrbase_HEADERS
dsp/projector.h
dsp/raisedcosine.h
dsp/recursivefilters.h
dsp/rootraisedcosine.h
dsp/samplemififo.h
dsp/samplemofifo.h
dsp/samplesinkfifo.h

View File

@ -109,45 +109,24 @@ public:
Type filter(Type sample)
{
Type acc = 0;
int a = m_ptr;
int b = a - 1;
int i, n_taps, size;
unsigned int n_samples = m_samples.size();
unsigned int n_taps = m_taps.size() - 1;
unsigned int a = m_ptr;
unsigned int b = a == n_samples - 1 ? 0 : a + 1;
m_samples[m_ptr] = sample;
size = m_samples.size(); // Valgrind optim (2)
while (b < 0)
{
b += size;
}
n_taps = m_taps.size() - 1; // Valgrind optim
for (i = 0; i < n_taps; i++)
for (unsigned int i = 0; i < n_taps; ++i)
{
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while (a >= size)
{
a -= size;
}
b--;
while(b < 0)
{
b += size;
}
a = (a == 0) ? n_samples - 1 : a - 1;
b = (b == n_samples - 1) ? 0 : b + 1;
}
acc += m_samples[a] * m_taps[i];
m_ptr++;
acc += m_samples[a] * m_taps[n_taps];
while(m_ptr >= size)
{
m_ptr -= size;
}
m_ptr = (m_ptr == n_samples - 1) ? 0 : m_ptr + 1;
return acc;
}

View File

@ -0,0 +1,142 @@
///////////////////////////////////////////////////////////////////////////////////
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_ROOTRAISEDCOSINE_H
#define INCLUDE_ROOTRAISEDCOSINE_H
#include <cmath>
#include <vector>
#include "dsp/dsptypes.h"
// Root-raised-cosine low-pass filter for pulse shaping, without intersymbol interference (ISI)
// https://en.wikipedia.org/wiki/Root-raised-cosine_filter
// This could be optimised in to a polyphase filter, as samplesPerSymbol-1 inputs
// to filter() should be zero, as the data is upsampled to the sample rate
template <class Type> class RootRaisedCosine {
public:
RootRaisedCosine() : m_ptr(0) { }
// beta - roll-off factor
// symbolSpan - number of symbols over which the filter is spread
// samplesPerSymbol - number of samples per symbol
// normaliseUpsampledAmplitude - when true, scale the filter such that an upsampled
// (by samplesPerSymbol) bipolar sequence (E.g. [1 0 0 -1 0 0..]) has maximum
// output values close to (1,-1)
void create(double beta, int symbolSpan, int samplesPerSymbol, bool normaliseUpsampledAmplitude = false)
{
int nTaps = symbolSpan * samplesPerSymbol + 1;
int i, j;
// check constraints
if(!(nTaps & 1)) {
qDebug("Root raised cosine filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
m_samples[i] = 0;
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
// calculate filter taps
double Ne = (nTaps-1) / 2;
for(i = 0; i < nTaps / 2 + 1; i++)
{
double t = (i - (nTaps / 2)) / (double)samplesPerSymbol;
double Ts = 1.0;
double numerator = 1.0/Ts * (sin(M_PI * t / Ts * (1.0-beta)) + 4.0*beta*t/Ts*cos(M_PI*t/Ts*(1+beta)));
double b = (4.0 * beta * t / Ts);
double denominator = M_PI * t / Ts * (1-b*b);
if ((numerator == 0.0) && (denominator == 0.0))
m_taps[i] = 1.0/Ts * (1.0+beta*(4.0/M_PI-1.0));
else if (denominator == 0.0)
m_taps[i] = beta/(Ts*sqrt(2.0)) * ((1+2.0/M_PI)*sin(M_PI/(4.0*beta)) + (1.0-2.0/M_PI)*cos(M_PI/(4.0*beta)));
else
m_taps[i] = numerator/denominator;
}
// normalize
if (!normaliseUpsampledAmplitude)
{
// normalize energy
double sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++)
sum += std::pow(m_taps[i], 2.0) * 2;
sum += std::pow(m_taps[i], 2.0);
sum = std::sqrt(sum);
for(i = 0; i < (int)m_taps.size(); i++)
m_taps[i] /= sum;
}
else
{
// Calculate maximum output of filter, assuming upsampled bipolar input E.g. [1 0 0 -1 0 0..]
// This doesn't necessarily include the centre tap, so we try each offset
double maxGain = 0.0;
for (i = 0; i < samplesPerSymbol; i++)
{
double g = 0.0;
for (j = 0; j < (int)m_taps.size() - 1; j += samplesPerSymbol)
g += std::fabs(2.0 * m_taps[j]);
if ((i & 1) == 0)
g += std::fabs(m_taps[j]);
if (g > maxGain)
maxGain = g;
}
// Scale up so maximum out is 1
for(i = 0; i < (int)m_taps.size(); i++)
m_taps[i] /= maxGain;
}
}
Type filter(Type sample)
{
Type acc = 0;
unsigned int n_samples = m_samples.size();
unsigned int n_taps = m_taps.size() - 1;
unsigned int a = m_ptr;
unsigned int b = a == n_samples - 1 ? 0 : a + 1;
m_samples[m_ptr] = sample;
for (unsigned int i = 0; i < n_taps; ++i)
{
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a = (a == 0) ? n_samples - 1 : a - 1;
b = (b == n_samples - 1) ? 0 : b + 1;
}
acc += m_samples[a] * m_taps[n_taps];
m_ptr = (m_ptr == n_samples - 1) ? 0 : m_ptr + 1;
return acc;
}
private:
std::vector<Real> m_taps;
std::vector<Type> m_samples;
int m_ptr;
};
#endif // INCLUDE_ROOTRAISEDCOSINE_H

View File

@ -2707,6 +2707,9 @@ margin-bottom: 20px;
"ChirpChatModReport" : {
"$ref" : "#/definitions/ChirpChatModReport"
},
"DATVModReport" : {
"$ref" : "#/definitions/DATVModReport"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -2823,6 +2826,9 @@ margin-bottom: 20px;
"ChirpChatModSettings" : {
"$ref" : "#/definitions/ChirpChatModSettings"
},
"DATVModSettings" : {
"$ref" : "#/definitions/DATVModSettings"
},
"DATVDemodSettings" : {
"$ref" : "#/definitions/DATVDemodSettings"
},
@ -3409,6 +3415,103 @@ margin-bottom: 20px;
}
},
"description" : "DATVDemod"
};
defs.DATVModReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "power transmitted in channel (dB)"
},
"channelSampleRate" : {
"type" : "integer"
}
},
"description" : "DATVMod"
};
defs.DATVModSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"rfBandwidth" : {
"type" : "number",
"format" : "float"
},
"standard" : {
"type" : "integer",
"description" : "Broadcast standard (DVB-S=0)"
},
"modulation" : {
"type" : "integer",
"description" : "Modulation (BPSK=0 QPSK=1)"
},
"fec" : {
"type" : "integer",
"description" : "Forward error correct code rate (1/2=0 2/3=1 3/4=2 5/6=3 7/8=4)"
},
"symbolRate" : {
"type" : "integer",
"description" : "Symbol rate in symbols per second"
},
"rollOff" : {
"type" : "number",
"format" : "float",
"description" : "Root raised cosine filter bandwidth (0.35)"
},
"tsSource" : {
"type" : "integer",
"description" : "Transport stream source (File=0 UDP=1)"
},
"tsFileName" : {
"type" : "string"
},
"tsFilePlayLoop" : {
"type" : "integer"
},
"tsFilePlay" : {
"type" : "integer",
"description" : "(Pause=0 Play=1)"
},
"udpAddress" : {
"type" : "string"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port number to receive MPEG transport stream on"
},
"channelMute" : {
"type" : "integer"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "DATVMod"
};
defs.DSDDemodReport = {
"properties" : {
@ -45639,7 +45742,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-03-05T14:04:36.302+01:00
Generated 2021-03-18T18:13:23.857+01:00
</div>
</div>
</div>

View File

@ -25,6 +25,8 @@ ChannelReport:
$ref: "/doc/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodReport"
ChirpChatModReport:
$ref: "/doc/swagger/include/ChirpChatMod.yaml#/ChirpChatModReport"
DATVModReport:
$ref: "/doc/swagger/include/DATVMod.yaml#/DATVModReport"
DSDDemodReport:
$ref: "/doc/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

View File

@ -39,6 +39,8 @@ ChannelSettings:
$ref: "/doc/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodSettings"
ChirpChatModSettings:
$ref: "/doc/swagger/include/ChirpChatMod.yaml#/ChirpChatModSettings"
DATVModSettings:
$ref: "/doc/swagger/include/DATVMod.yaml#/DATVModSettings"
DATVDemodSettings:
$ref: "/doc/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DSDDemodSettings:

View File

@ -37,7 +37,6 @@ int LFSR::scramble(int bit_in)
m_sr = (m_sr << 1) | bit_out;
return bit_out;
}
#include <stdio.h>
// Scramble data using LFSR - LSB first
void LFSR::scramble(uint8_t *data, int length)
@ -81,7 +80,7 @@ void LFSR::descramble(uint8_t *data, int length)
}
}
// XOR data with rand_bit of LFSR - LSB first
// XOR data with rand_bits of LFSR - LSB first
void LFSR::randomize(uint8_t *data, int length)
{
uint8_t byte_in, byte_out;
@ -95,7 +94,8 @@ void LFSR::randomize(uint8_t *data, int length)
{
// XOR input bit with specified bit from SR
bit_in = (byte_in >> j) & 1;
bit_out = ((m_sr >> m_rand_bit) & 1) ^ bit_in;
//bit_out = ((m_sr >> m_rand_bit) & 1) ^ bit_in;
bit_out = (popcount(m_sr & m_rand_bits) & 1) ^ bit_in;
byte_out = byte_out | (bit_out << j);
// Update LFSR
bit = popcount(m_sr & m_polynomial) & 1;
@ -104,3 +104,27 @@ void LFSR::randomize(uint8_t *data, int length)
data[i] = byte_out;
}
}
// XOR data with rand_bits of LFSR - MSB first
void LFSR::randomizeMSB(const uint8_t *dataIn, uint8_t *dataOut, int length)
{
uint8_t byte_in, byte_out;
int bit_in, bit_out, bit;
for(int i = 0; i < length; i++)
{
byte_in = dataIn[i];
byte_out = 0;
for (int j = 7; j >= 0; j--)
{
// XOR input bit with selected bits from SR
bit_in = (byte_in >> j) & 1;
bit_out = (popcount(m_sr & m_rand_bits) & 1) ^ bit_in;
byte_out = byte_out | (bit_out << j);
// Update LFSR
bit = popcount(m_sr & m_polynomial) & 1;
m_sr = (m_sr << 1) | bit;
}
dataOut[i] = byte_out;
}
}

View File

@ -30,8 +30,8 @@ public:
// Create and initialise LFSR with specified number of bits, polynomial and
// initial state (which must be non-zero, unless a multiplicative scrambler).
// The +1 is implicit in the polynomial so x^1 + 1 should be passed as 0x01
LFSR(uint32_t polynomial, uint32_t init_value = ~0U, int rand_bit = -1) :
m_rand_bit(rand_bit),
LFSR(uint32_t polynomial, uint32_t init_value = ~0U, int rand_bits = 1) :
m_rand_bits(rand_bits),
m_polynomial(polynomial),
m_init_value(init_value)
{
@ -55,8 +55,10 @@ public:
// Descramble data using LFSR - LSB first
void descramble(uint8_t *data, int length);
// XOR data with rand_bit from LFSR generating pseudo noise (PN) sequence - LSB first
// XOR data with rand_bits from LFSR generating pseudo noise (PN) sequence - LSB first
void randomize(uint8_t *data, int length);
// XOR data with rand_bits from LFSR generating pseudo noise (PN) sequence - MSB first
void randomizeMSB(const uint8_t *dataIn, uint8_t *dataOut, int length);
// Get current shift-register value
uint32_t getSR()
@ -77,7 +79,7 @@ public:
}
private:
int m_rand_bit; // Which bit from the SR to use in randomize()
int m_rand_bits; // Which bits from the SR to use in randomize()
uint32_t m_polynomial; // Polynomial coefficients (+1 is implicit)
uint32_t m_init_value; // Value to initialise SR to when init() is called
uint32_t m_sr; // Shift register
@ -95,19 +97,32 @@ public:
// https://public.ccsds.org/Pubs/131x0b3e1.pdf
// x^8+x^7+x^5+x^3+1
// In Matlab: comm.PNSequence('Polynomial', 'x^8+x^7+x^5+x^3+1', 'InitialConditions', [1 1 1 1 1 1 1 1])
// randomize() shifts to right, so polynomial below is reversed with x^8 implicit
// Call randomize()
class SDRBASE_API RandomizeCCSDS : public LFSR
{
public:
RandomizeCCSDS() : LFSR(0x95, 0xff, 7) {}
RandomizeCCSDS() : LFSR(0x95, 0xff, 1<<7) {}
};
// DVB-S
// https://www.etsi.org/deliver/etsi_en/300400_300499/300421/01.01.02_60/en_300421v010102p.pdf
// x^15+x^14+1
// In Matlab: comm.PNSequence('Polynomial', 'z^15+z^1+1', 'InitialConditions', [1 0 0 1 0 1 0 1 0 0 0 0 0 0 0], 'Mask', [0 0 0 0 0 0 0 0 0 0 0 0 0 1 1])
// Call randomizeMSB()
class SDRBASE_API RandomizeDVBS : public LFSR
{
public:
RandomizeDVBS() : LFSR(0x6000, 0x00a9, 0x6000) {}
};
// 802.15.4 GFSK PHY
// In Matlab: comm.PNSequence('Polynomial', [1 0 0 0 1 0 0 0 0 1], 'InitialConditions', [0 1 1 1 1 1 1 1 1], 'Mask', [1 0 0 0 0 0 0 0 0])
// Call randomize()
class SDRBASE_API Randomize_802_15_4_PN9 : public LFSR
{
public:
Randomize_802_15_4_PN9() : LFSR(0x108, 0x1fe, 0) {}
Randomize_802_15_4_PN9() : LFSR(0x108, 0x1fe, 1) {}
};
#endif

View File

@ -3866,6 +3866,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setDatvDemodSettings(new SWGSDRangel::SWGDATVDemodSettings());
channelSettings->getDatvDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DATVModSettings")
{
channelSettings->setDatvModSettings(new SWGSDRangel::SWGDATVModSettings());
channelSettings->getDatvModSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DSDDemodSettings")
{
channelSettings->setDsdDemodSettings(new SWGSDRangel::SWGDSDDemodSettings());
@ -4577,6 +4582,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setAptDemodSettings(nullptr);
channelSettings.setAtvModSettings(nullptr);
channelSettings.setBfmDemodSettings(nullptr);
channelSettings.setDatvModSettings(nullptr);
channelSettings.setDsdDemodSettings(nullptr);
channelSettings.setIeee802154ModSettings(nullptr);
channelSettings.setNfmDemodSettings(nullptr);
@ -4603,6 +4609,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
channelReport.setAmModReport(nullptr);
channelReport.setAtvModReport(nullptr);
channelReport.setBfmDemodReport(nullptr);
channelReport.setDatvModReport(nullptr);
channelReport.setDsdDemodReport(nullptr);
channelReport.setNfmDemodReport(nullptr);
channelReport.setNfmModReport(nullptr);

View File

@ -28,6 +28,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"de.maintech.sdrangelove.channel.am", "AMDemodSettings"}, // remap
{"sdrangel.channeltx.modam", "AMModSettings"},
{"sdrangel.channeltx.modatv", "ATVModSettings"},
{"sdrangel.channeltx.moddatv", "DATVModSettings"},
{"sdrangel.channel.bfm", "BFMDemodSettings"},
{"sdrangel.channel.chanalyzer", "ChannelAnalyzerSettings"},
{"sdrangel.channel.chanalyzerng", "ChannelAnalyzerSettings"}, // remap
@ -129,6 +130,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"ChirpChatDemod", "ChirpChatDemodSettings"},
{"ChirpChatMod", "ChirpChatModSettings"},
{"DATVDemod", "DATVDemodSettings"},
{"DATVMod", "DATVModSettings"},
{"DSDDemod", "DSDDemodSettings"},
{"FileSink", "FileSinkSettings"},
{"FileSource", "FileSourceSettings"},

View File

@ -25,6 +25,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodReport"
ChirpChatModReport:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatMod.yaml#/ChirpChatModReport"
DATVModReport:
$ref: "http://swgserver:8081/api/swagger/include/DATVMod.yaml#/DATVModReport"
DSDDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

View File

@ -39,6 +39,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatDemod.yaml#/ChirpChatDemodSettings"
ChirpChatModSettings:
$ref: "http://swgserver:8081/api/swagger/include/ChirpChatMod.yaml#/ChirpChatModSettings"
DATVModSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVMod.yaml#/DATVModSettings"
DATVDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DSDDemodSettings:

View File

@ -0,0 +1,70 @@
DATVModSettings:
description: DATVMod
properties:
inputFrequencyOffset:
type: integer
format: int64
rfBandwidth:
type: number
format: float
standard:
description: "Broadcast standard (DVB-S=0)"
type: integer
modulation:
description: "Modulation (BPSK=0 QPSK=1)"
type: integer
fec:
description: "Forward error correct code rate (1/2=0 2/3=1 3/4=2 5/6=3 7/8=4)"
type: integer
symbolRate:
description: "Symbol rate in symbols per second"
type: integer
rollOff:
description: "Root raised cosine filter bandwidth (0.35)"
type: number
format: float
tsSource:
description: "Transport stream source (File=0 UDP=1)"
type: integer
tsFileName:
type: string
tsFilePlayLoop:
type: integer
tsFilePlay:
description: "(Pause=0 Play=1)"
type: integer
udpAddress:
type: string
udpPort:
description: "UDP port number to receive MPEG transport stream on"
type: integer
channelMute:
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
DATVModReport:
description: DATVMod
properties:
channelPowerDB:
description: power transmitted in channel (dB)
type: number
format: float
channelSampleRate:
type: integer

View File

@ -2707,6 +2707,9 @@ margin-bottom: 20px;
"ChirpChatModReport" : {
"$ref" : "#/definitions/ChirpChatModReport"
},
"DATVModReport" : {
"$ref" : "#/definitions/DATVModReport"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -2823,6 +2826,9 @@ margin-bottom: 20px;
"ChirpChatModSettings" : {
"$ref" : "#/definitions/ChirpChatModSettings"
},
"DATVModSettings" : {
"$ref" : "#/definitions/DATVModSettings"
},
"DATVDemodSettings" : {
"$ref" : "#/definitions/DATVDemodSettings"
},
@ -3409,6 +3415,103 @@ margin-bottom: 20px;
}
},
"description" : "DATVDemod"
};
defs.DATVModReport = {
"properties" : {
"channelPowerDB" : {
"type" : "number",
"format" : "float",
"description" : "power transmitted in channel (dB)"
},
"channelSampleRate" : {
"type" : "integer"
}
},
"description" : "DATVMod"
};
defs.DATVModSettings = {
"properties" : {
"inputFrequencyOffset" : {
"type" : "integer",
"format" : "int64"
},
"rfBandwidth" : {
"type" : "number",
"format" : "float"
},
"standard" : {
"type" : "integer",
"description" : "Broadcast standard (DVB-S=0)"
},
"modulation" : {
"type" : "integer",
"description" : "Modulation (BPSK=0 QPSK=1)"
},
"fec" : {
"type" : "integer",
"description" : "Forward error correct code rate (1/2=0 2/3=1 3/4=2 5/6=3 7/8=4)"
},
"symbolRate" : {
"type" : "integer",
"description" : "Symbol rate in symbols per second"
},
"rollOff" : {
"type" : "number",
"format" : "float",
"description" : "Root raised cosine filter bandwidth (0.35)"
},
"tsSource" : {
"type" : "integer",
"description" : "Transport stream source (File=0 UDP=1)"
},
"tsFileName" : {
"type" : "string"
},
"tsFilePlayLoop" : {
"type" : "integer"
},
"tsFilePlay" : {
"type" : "integer",
"description" : "(Pause=0 Play=1)"
},
"udpAddress" : {
"type" : "string"
},
"udpPort" : {
"type" : "integer",
"description" : "UDP port number to receive MPEG transport stream on"
},
"channelMute" : {
"type" : "integer"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"streamIndex" : {
"type" : "integer",
"description" : "MIMO channel. Not relevant when connected to SI (single Rx)."
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
}
},
"description" : "DATVMod"
};
defs.DSDDemodReport = {
"properties" : {
@ -45639,7 +45742,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2021-03-05T14:04:36.302+01:00
Generated 2021-03-18T18:13:23.857+01:00
</div>
</div>
</div>

View File

@ -46,6 +46,8 @@ SWGChannelReport::SWGChannelReport() {
m_chirp_chat_demod_report_isSet = false;
chirp_chat_mod_report = nullptr;
m_chirp_chat_mod_report_isSet = false;
datv_mod_report = nullptr;
m_datv_mod_report_isSet = false;
dsd_demod_report = nullptr;
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = nullptr;
@ -112,6 +114,8 @@ SWGChannelReport::init() {
m_chirp_chat_demod_report_isSet = false;
chirp_chat_mod_report = new SWGChirpChatModReport();
m_chirp_chat_mod_report_isSet = false;
datv_mod_report = new SWGDATVModReport();
m_datv_mod_report_isSet = false;
dsd_demod_report = new SWGDSDDemodReport();
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = new SWGIEEE_802_15_4_ModReport();
@ -181,6 +185,9 @@ SWGChannelReport::cleanup() {
if(chirp_chat_mod_report != nullptr) {
delete chirp_chat_mod_report;
}
if(datv_mod_report != nullptr) {
delete datv_mod_report;
}
if(dsd_demod_report != nullptr) {
delete dsd_demod_report;
}
@ -272,6 +279,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&chirp_chat_mod_report, pJson["ChirpChatModReport"], "SWGChirpChatModReport", "SWGChirpChatModReport");
::SWGSDRangel::setValue(&datv_mod_report, pJson["DATVModReport"], "SWGDATVModReport", "SWGDATVModReport");
::SWGSDRangel::setValue(&dsd_demod_report, pJson["DSDDemodReport"], "SWGDSDDemodReport", "SWGDSDDemodReport");
::SWGSDRangel::setValue(&ieee_802_15_4_mod_report, pJson["IEEE_802_15_4_ModReport"], "SWGIEEE_802_15_4_ModReport", "SWGIEEE_802_15_4_ModReport");
@ -355,6 +364,9 @@ SWGChannelReport::asJsonObject() {
if((chirp_chat_mod_report != nullptr) && (chirp_chat_mod_report->isSet())){
toJsonValue(QString("ChirpChatModReport"), chirp_chat_mod_report, obj, QString("SWGChirpChatModReport"));
}
if((datv_mod_report != nullptr) && (datv_mod_report->isSet())){
toJsonValue(QString("DATVModReport"), datv_mod_report, obj, QString("SWGDATVModReport"));
}
if((dsd_demod_report != nullptr) && (dsd_demod_report->isSet())){
toJsonValue(QString("DSDDemodReport"), dsd_demod_report, obj, QString("SWGDSDDemodReport"));
}
@ -509,6 +521,16 @@ SWGChannelReport::setChirpChatModReport(SWGChirpChatModReport* chirp_chat_mod_re
this->m_chirp_chat_mod_report_isSet = true;
}
SWGDATVModReport*
SWGChannelReport::getDatvModReport() {
return datv_mod_report;
}
void
SWGChannelReport::setDatvModReport(SWGDATVModReport* datv_mod_report) {
this->datv_mod_report = datv_mod_report;
this->m_datv_mod_report_isSet = true;
}
SWGDSDDemodReport*
SWGChannelReport::getDsdDemodReport() {
return dsd_demod_report;
@ -741,6 +763,9 @@ SWGChannelReport::isSet(){
if(chirp_chat_mod_report && chirp_chat_mod_report->isSet()){
isObjectUpdated = true; break;
}
if(datv_mod_report && datv_mod_report->isSet()){
isObjectUpdated = true; break;
}
if(dsd_demod_report && dsd_demod_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -29,6 +29,7 @@
#include "SWGBFMDemodReport.h"
#include "SWGChirpChatDemodReport.h"
#include "SWGChirpChatModReport.h"
#include "SWGDATVModReport.h"
#include "SWGDSDDemodReport.h"
#include "SWGFileSinkReport.h"
#include "SWGFileSourceReport.h"
@ -96,6 +97,9 @@ public:
SWGChirpChatModReport* getChirpChatModReport();
void setChirpChatModReport(SWGChirpChatModReport* chirp_chat_mod_report);
SWGDATVModReport* getDatvModReport();
void setDatvModReport(SWGDATVModReport* datv_mod_report);
SWGDSDDemodReport* getDsdDemodReport();
void setDsdDemodReport(SWGDSDDemodReport* dsd_demod_report);
@ -187,6 +191,9 @@ private:
SWGChirpChatModReport* chirp_chat_mod_report;
bool m_chirp_chat_mod_report_isSet;
SWGDATVModReport* datv_mod_report;
bool m_datv_mod_report_isSet;
SWGDSDDemodReport* dsd_demod_report;
bool m_dsd_demod_report_isSet;

View File

@ -58,6 +58,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_chirp_chat_demod_settings_isSet = false;
chirp_chat_mod_settings = nullptr;
m_chirp_chat_mod_settings_isSet = false;
datv_mod_settings = nullptr;
m_datv_mod_settings_isSet = false;
datv_demod_settings = nullptr;
m_datv_demod_settings_isSet = false;
dsd_demod_settings = nullptr;
@ -148,6 +150,8 @@ SWGChannelSettings::init() {
m_chirp_chat_demod_settings_isSet = false;
chirp_chat_mod_settings = new SWGChirpChatModSettings();
m_chirp_chat_mod_settings_isSet = false;
datv_mod_settings = new SWGDATVModSettings();
m_datv_mod_settings_isSet = false;
datv_demod_settings = new SWGDATVDemodSettings();
m_datv_demod_settings_isSet = false;
dsd_demod_settings = new SWGDSDDemodSettings();
@ -243,6 +247,9 @@ SWGChannelSettings::cleanup() {
if(chirp_chat_mod_settings != nullptr) {
delete chirp_chat_mod_settings;
}
if(datv_mod_settings != nullptr) {
delete datv_mod_settings;
}
if(datv_demod_settings != nullptr) {
delete datv_demod_settings;
}
@ -364,6 +371,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&chirp_chat_mod_settings, pJson["ChirpChatModSettings"], "SWGChirpChatModSettings", "SWGChirpChatModSettings");
::SWGSDRangel::setValue(&datv_mod_settings, pJson["DATVModSettings"], "SWGDATVModSettings", "SWGDATVModSettings");
::SWGSDRangel::setValue(&datv_demod_settings, pJson["DATVDemodSettings"], "SWGDATVDemodSettings", "SWGDATVDemodSettings");
::SWGSDRangel::setValue(&dsd_demod_settings, pJson["DSDDemodSettings"], "SWGDSDDemodSettings", "SWGDSDDemodSettings");
@ -477,6 +486,9 @@ SWGChannelSettings::asJsonObject() {
if((chirp_chat_mod_settings != nullptr) && (chirp_chat_mod_settings->isSet())){
toJsonValue(QString("ChirpChatModSettings"), chirp_chat_mod_settings, obj, QString("SWGChirpChatModSettings"));
}
if((datv_mod_settings != nullptr) && (datv_mod_settings->isSet())){
toJsonValue(QString("DATVModSettings"), datv_mod_settings, obj, QString("SWGDATVModSettings"));
}
if((datv_demod_settings != nullptr) && (datv_demod_settings->isSet())){
toJsonValue(QString("DATVDemodSettings"), datv_demod_settings, obj, QString("SWGDATVDemodSettings"));
}
@ -709,6 +721,16 @@ SWGChannelSettings::setChirpChatModSettings(SWGChirpChatModSettings* chirp_chat_
this->m_chirp_chat_mod_settings_isSet = true;
}
SWGDATVModSettings*
SWGChannelSettings::getDatvModSettings() {
return datv_mod_settings;
}
void
SWGChannelSettings::setDatvModSettings(SWGDATVModSettings* datv_mod_settings) {
this->datv_mod_settings = datv_mod_settings;
this->m_datv_mod_settings_isSet = true;
}
SWGDATVDemodSettings*
SWGChannelSettings::getDatvDemodSettings() {
return datv_demod_settings;
@ -1019,6 +1041,9 @@ SWGChannelSettings::isSet(){
if(chirp_chat_mod_settings && chirp_chat_mod_settings->isSet()){
isObjectUpdated = true; break;
}
if(datv_mod_settings && datv_mod_settings->isSet()){
isObjectUpdated = true; break;
}
if(datv_demod_settings && datv_demod_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -34,6 +34,7 @@
#include "SWGChirpChatDemodSettings.h"
#include "SWGChirpChatModSettings.h"
#include "SWGDATVDemodSettings.h"
#include "SWGDATVModSettings.h"
#include "SWGDSDDemodSettings.h"
#include "SWGFileSinkSettings.h"
#include "SWGFileSourceSettings.h"
@ -124,6 +125,9 @@ public:
SWGChirpChatModSettings* getChirpChatModSettings();
void setChirpChatModSettings(SWGChirpChatModSettings* chirp_chat_mod_settings);
SWGDATVModSettings* getDatvModSettings();
void setDatvModSettings(SWGDATVModSettings* datv_mod_settings);
SWGDATVDemodSettings* getDatvDemodSettings();
void setDatvDemodSettings(SWGDATVDemodSettings* datv_demod_settings);
@ -251,6 +255,9 @@ private:
SWGChirpChatModSettings* chirp_chat_mod_settings;
bool m_chirp_chat_mod_settings_isSet;
SWGDATVModSettings* datv_mod_settings;
bool m_datv_mod_settings_isSet;
SWGDATVDemodSettings* datv_demod_settings;
bool m_datv_demod_settings_isSet;

View File

@ -0,0 +1,131 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGDATVModReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDATVModReport::SWGDATVModReport(QString* json) {
init();
this->fromJson(*json);
}
SWGDATVModReport::SWGDATVModReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGDATVModReport::~SWGDATVModReport() {
this->cleanup();
}
void
SWGDATVModReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGDATVModReport::cleanup() {
}
SWGDATVModReport*
SWGDATVModReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDATVModReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGDATVModReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDATVModReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGDATVModReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGDATVModReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGDATVModReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGDATVModReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGDATVModReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,64 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGDATVModReport.h
*
* DATVMod
*/
#ifndef SWGDATVModReport_H_
#define SWGDATVModReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDATVModReport: public SWGObject {
public:
SWGDATVModReport();
SWGDATVModReport(QString* json);
virtual ~SWGDATVModReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDATVModReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGDATVModReport_H_ */

View File

@ -0,0 +1,599 @@
/**
* 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 "SWGDATVModSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDATVModSettings::SWGDATVModSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGDATVModSettings::SWGDATVModSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
standard = 0;
m_standard_isSet = false;
modulation = 0;
m_modulation_isSet = false;
fec = 0;
m_fec_isSet = false;
symbol_rate = 0;
m_symbol_rate_isSet = false;
roll_off = 0.0f;
m_roll_off_isSet = false;
ts_source = 0;
m_ts_source_isSet = false;
ts_file_name = nullptr;
m_ts_file_name_isSet = false;
ts_file_play_loop = 0;
m_ts_file_play_loop_isSet = false;
ts_file_play = 0;
m_ts_file_play_isSet = false;
udp_address = nullptr;
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
channel_mute = 0;
m_channel_mute_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;
}
SWGDATVModSettings::~SWGDATVModSettings() {
this->cleanup();
}
void
SWGDATVModSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
standard = 0;
m_standard_isSet = false;
modulation = 0;
m_modulation_isSet = false;
fec = 0;
m_fec_isSet = false;
symbol_rate = 0;
m_symbol_rate_isSet = false;
roll_off = 0.0f;
m_roll_off_isSet = false;
ts_source = 0;
m_ts_source_isSet = false;
ts_file_name = new QString("");
m_ts_file_name_isSet = false;
ts_file_play_loop = 0;
m_ts_file_play_loop_isSet = false;
ts_file_play = 0;
m_ts_file_play_isSet = false;
udp_address = new QString("");
m_udp_address_isSet = false;
udp_port = 0;
m_udp_port_isSet = false;
channel_mute = 0;
m_channel_mute_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
SWGDATVModSettings::cleanup() {
if(ts_file_name != nullptr) {
delete ts_file_name;
}
if(udp_address != nullptr) {
delete udp_address;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGDATVModSettings*
SWGDATVModSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDATVModSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&standard, pJson["standard"], "qint32", "");
::SWGSDRangel::setValue(&modulation, pJson["modulation"], "qint32", "");
::SWGSDRangel::setValue(&fec, pJson["fec"], "qint32", "");
::SWGSDRangel::setValue(&symbol_rate, pJson["symbolRate"], "qint32", "");
::SWGSDRangel::setValue(&roll_off, pJson["rollOff"], "float", "");
::SWGSDRangel::setValue(&ts_source, pJson["tsSource"], "qint32", "");
::SWGSDRangel::setValue(&ts_file_name, pJson["tsFileName"], "QString", "QString");
::SWGSDRangel::setValue(&ts_file_play_loop, pJson["tsFilePlayLoop"], "qint32", "");
::SWGSDRangel::setValue(&ts_file_play, pJson["tsFilePlay"], "qint32", "");
::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString");
::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", "");
::SWGSDRangel::setValue(&channel_mute, pJson["channelMute"], "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
SWGDATVModSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDATVModSettings::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_standard_isSet){
obj->insert("standard", QJsonValue(standard));
}
if(m_modulation_isSet){
obj->insert("modulation", QJsonValue(modulation));
}
if(m_fec_isSet){
obj->insert("fec", QJsonValue(fec));
}
if(m_symbol_rate_isSet){
obj->insert("symbolRate", QJsonValue(symbol_rate));
}
if(m_roll_off_isSet){
obj->insert("rollOff", QJsonValue(roll_off));
}
if(m_ts_source_isSet){
obj->insert("tsSource", QJsonValue(ts_source));
}
if(ts_file_name != nullptr && *ts_file_name != QString("")){
toJsonValue(QString("tsFileName"), ts_file_name, obj, QString("QString"));
}
if(m_ts_file_play_loop_isSet){
obj->insert("tsFilePlayLoop", QJsonValue(ts_file_play_loop));
}
if(m_ts_file_play_isSet){
obj->insert("tsFilePlay", QJsonValue(ts_file_play));
}
if(udp_address != nullptr && *udp_address != QString("")){
toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString"));
}
if(m_udp_port_isSet){
obj->insert("udpPort", QJsonValue(udp_port));
}
if(m_channel_mute_isSet){
obj->insert("channelMute", QJsonValue(channel_mute));
}
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
SWGDATVModSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGDATVModSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
float
SWGDATVModSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGDATVModSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
qint32
SWGDATVModSettings::getStandard() {
return standard;
}
void
SWGDATVModSettings::setStandard(qint32 standard) {
this->standard = standard;
this->m_standard_isSet = true;
}
qint32
SWGDATVModSettings::getModulation() {
return modulation;
}
void
SWGDATVModSettings::setModulation(qint32 modulation) {
this->modulation = modulation;
this->m_modulation_isSet = true;
}
qint32
SWGDATVModSettings::getFec() {
return fec;
}
void
SWGDATVModSettings::setFec(qint32 fec) {
this->fec = fec;
this->m_fec_isSet = true;
}
qint32
SWGDATVModSettings::getSymbolRate() {
return symbol_rate;
}
void
SWGDATVModSettings::setSymbolRate(qint32 symbol_rate) {
this->symbol_rate = symbol_rate;
this->m_symbol_rate_isSet = true;
}
float
SWGDATVModSettings::getRollOff() {
return roll_off;
}
void
SWGDATVModSettings::setRollOff(float roll_off) {
this->roll_off = roll_off;
this->m_roll_off_isSet = true;
}
qint32
SWGDATVModSettings::getTsSource() {
return ts_source;
}
void
SWGDATVModSettings::setTsSource(qint32 ts_source) {
this->ts_source = ts_source;
this->m_ts_source_isSet = true;
}
QString*
SWGDATVModSettings::getTsFileName() {
return ts_file_name;
}
void
SWGDATVModSettings::setTsFileName(QString* ts_file_name) {
this->ts_file_name = ts_file_name;
this->m_ts_file_name_isSet = true;
}
qint32
SWGDATVModSettings::getTsFilePlayLoop() {
return ts_file_play_loop;
}
void
SWGDATVModSettings::setTsFilePlayLoop(qint32 ts_file_play_loop) {
this->ts_file_play_loop = ts_file_play_loop;
this->m_ts_file_play_loop_isSet = true;
}
qint32
SWGDATVModSettings::getTsFilePlay() {
return ts_file_play;
}
void
SWGDATVModSettings::setTsFilePlay(qint32 ts_file_play) {
this->ts_file_play = ts_file_play;
this->m_ts_file_play_isSet = true;
}
QString*
SWGDATVModSettings::getUdpAddress() {
return udp_address;
}
void
SWGDATVModSettings::setUdpAddress(QString* udp_address) {
this->udp_address = udp_address;
this->m_udp_address_isSet = true;
}
qint32
SWGDATVModSettings::getUdpPort() {
return udp_port;
}
void
SWGDATVModSettings::setUdpPort(qint32 udp_port) {
this->udp_port = udp_port;
this->m_udp_port_isSet = true;
}
qint32
SWGDATVModSettings::getChannelMute() {
return channel_mute;
}
void
SWGDATVModSettings::setChannelMute(qint32 channel_mute) {
this->channel_mute = channel_mute;
this->m_channel_mute_isSet = true;
}
qint32
SWGDATVModSettings::getRgbColor() {
return rgb_color;
}
void
SWGDATVModSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGDATVModSettings::getTitle() {
return title;
}
void
SWGDATVModSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGDATVModSettings::getStreamIndex() {
return stream_index;
}
void
SWGDATVModSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGDATVModSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGDATVModSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGDATVModSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGDATVModSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGDATVModSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGDATVModSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGDATVModSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGDATVModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGDATVModSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGDATVModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
bool
SWGDATVModSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(m_standard_isSet){
isObjectUpdated = true; break;
}
if(m_modulation_isSet){
isObjectUpdated = true; break;
}
if(m_fec_isSet){
isObjectUpdated = true; break;
}
if(m_symbol_rate_isSet){
isObjectUpdated = true; break;
}
if(m_roll_off_isSet){
isObjectUpdated = true; break;
}
if(m_ts_source_isSet){
isObjectUpdated = true; break;
}
if(ts_file_name && *ts_file_name != QString("")){
isObjectUpdated = true; break;
}
if(m_ts_file_play_loop_isSet){
isObjectUpdated = true; break;
}
if(m_ts_file_play_isSet){
isObjectUpdated = true; break;
}
if(udp_address && *udp_address != QString("")){
isObjectUpdated = true; break;
}
if(m_udp_port_isSet){
isObjectUpdated = true; break;
}
if(m_channel_mute_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,185 @@
/**
* 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.
*/
/*
* SWGDATVModSettings.h
*
* DATVMod
*/
#ifndef SWGDATVModSettings_H_
#define SWGDATVModSettings_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDATVModSettings: public SWGObject {
public:
SWGDATVModSettings();
SWGDATVModSettings(QString* json);
virtual ~SWGDATVModSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDATVModSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
qint32 getStandard();
void setStandard(qint32 standard);
qint32 getModulation();
void setModulation(qint32 modulation);
qint32 getFec();
void setFec(qint32 fec);
qint32 getSymbolRate();
void setSymbolRate(qint32 symbol_rate);
float getRollOff();
void setRollOff(float roll_off);
qint32 getTsSource();
void setTsSource(qint32 ts_source);
QString* getTsFileName();
void setTsFileName(QString* ts_file_name);
qint32 getTsFilePlayLoop();
void setTsFilePlayLoop(qint32 ts_file_play_loop);
qint32 getTsFilePlay();
void setTsFilePlay(qint32 ts_file_play);
QString* getUdpAddress();
void setUdpAddress(QString* udp_address);
qint32 getUdpPort();
void setUdpPort(qint32 udp_port);
qint32 getChannelMute();
void setChannelMute(qint32 channel_mute);
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;
qint32 standard;
bool m_standard_isSet;
qint32 modulation;
bool m_modulation_isSet;
qint32 fec;
bool m_fec_isSet;
qint32 symbol_rate;
bool m_symbol_rate_isSet;
float roll_off;
bool m_roll_off_isSet;
qint32 ts_source;
bool m_ts_source_isSet;
QString* ts_file_name;
bool m_ts_file_name_isSet;
qint32 ts_file_play_loop;
bool m_ts_file_play_loop_isSet;
qint32 ts_file_play;
bool m_ts_file_play_isSet;
QString* udp_address;
bool m_udp_address_isSet;
qint32 udp_port;
bool m_udp_port_isSet;
qint32 channel_mute;
bool m_channel_mute_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 /* SWGDATVModSettings_H_ */

View File

@ -73,6 +73,8 @@
#include "SWGCommand.h"
#include "SWGComplex.h"
#include "SWGDATVDemodSettings.h"
#include "SWGDATVModReport.h"
#include "SWGDATVModSettings.h"
#include "SWGDSDDemodReport.h"
#include "SWGDSDDemodSettings.h"
#include "SWGDVSerialDevice.h"
@ -432,6 +434,12 @@ namespace SWGSDRangel {
if(QString("SWGDATVDemodSettings").compare(type) == 0) {
return new SWGDATVDemodSettings();
}
if(QString("SWGDATVModReport").compare(type) == 0) {
return new SWGDATVModReport();
}
if(QString("SWGDATVModSettings").compare(type) == 0) {
return new SWGDATVModSettings();
}
if(QString("SWGDSDDemodReport").compare(type) == 0) {
return new SWGDSDDemodReport();
}