1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-17 13:51:47 -05:00

Merge pull request #850 from srcejon/dab

Add DAB/DAB+ demodulator
This commit is contained in:
Edouard Griffiths 2021-04-17 18:21:27 +02:00 committed by GitHub
commit f174569a67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 5280 additions and 7 deletions

View File

@ -79,9 +79,9 @@ for:
qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5multimedia5-plugins libqt5websockets5-dev \ qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5multimedia5-plugins libqt5websockets5-dev \
libqt5quick5 \ libqt5quick5 \
qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick-window2 qml-module-qtquick-dialogs \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick-window2 qml-module-qtquick-dialogs \
qml-module-qtquick-controls qml-module-qtquick-layouts \ qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-layouts \
libqt5serialport5-dev qtdeclarative5-dev qtpositioning5-dev qtlocation5-dev \ libqt5serialport5-dev qtdeclarative5-dev qtpositioning5-dev qtlocation5-dev \
libqt5charts5-dev libqt5texttospeech5-dev \ libqt5charts5-dev libqt5texttospeech5-dev libfaad-dev zlib1g-dev \
libusb-1.0-0-dev libboost-all-dev libasound2-dev libopencv-dev libopencv-imgcodecs-dev \ libusb-1.0-0-dev libboost-all-dev libasound2-dev libopencv-dev libopencv-imgcodecs-dev \
libxml2-dev bison flex ffmpeg libpostproc-dev libavcodec-dev libavformat-dev \ libxml2-dev bison flex ffmpeg libpostproc-dev libavcodec-dev libavformat-dev \
libopus-dev libcodec2-dev libairspy-dev libhackrf-dev \ libopus-dev libcodec2-dev libairspy-dev libhackrf-dev \

View File

@ -230,6 +230,11 @@ elseif (WIN32)
set(UHD_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/uhd/lib/uhd.lib" CACHE INTERNAL "") set(UHD_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/uhd/lib/uhd.lib" CACHE INTERNAL "")
set(UHD_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/uhd/bin" CACHE INTERNAL "") set(UHD_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/uhd/bin" CACHE INTERNAL "")
set(DAB_FOUND ON CACHE INTERNAL "")
set(DAB_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/dab" CACHE INTERNAL "")
set(DAB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/dab/dab_lib.lib" CACHE INTERNAL "")
set(DAB_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/dab" CACHE INTERNAL "")
set(OPENSSL_FOUND ON CACHE INTERNAL "") set(OPENSSL_FOUND ON CACHE INTERNAL "")
set(OPENSSL_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/openssl" CACHE INTERNAL "") set(OPENSSL_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/openssl" CACHE INTERNAL "")
@ -255,6 +260,14 @@ elseif (WIN32)
set(SDRPLAY_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/sdrplay/x64/sdrplay_api.lib" CACHE INTERNAL "") set(SDRPLAY_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/sdrplay/x64/sdrplay_api.lib" CACHE INTERNAL "")
set(SDRPLAY_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/sdrplay/x64/" CACHE INTERNAL "") set(SDRPLAY_DLL_DIR "${EXTERNAL_LIBRARY_FOLDER}/sdrplay/x64/" CACHE INTERNAL "")
set(FAAD_FOUND ON CACHE INTERNAL "")
set(FAAD_INCLUDE_DIR "${EXTERNAL_LIBRARY_FOLDER}/faad2/include" CACHE INTERNAL "")
set(FAAD_LIBRARY "${EXTERNAL_LIBRARY_FOLDER}/faad2/lib/libfaad.lib" CACHE INTERNAL "")
set(ZLIB_FOUND ON CACHE INTERNAL "")
set(ZLIB_INCLUDE_DIRS "${EXTERNAL_LIBRARY_FOLDER}/zlib/include" CACHE INTERNAL "")
set(ZLIB_LIBRARIES "${EXTERNAL_LIBRARY_FOLDER}/zlib/lib/zlibstaticd.lib" CACHE INTERNAL "")
# used on fixup_bundle phase # used on fixup_bundle phase
set(WINDOWS_FIXUP_BUNDLE_LIB_DIRS set(WINDOWS_FIXUP_BUNDLE_LIB_DIRS
"${EXTERNAL_LIBRARY_FOLDER}/fftw-3" "${EXTERNAL_LIBRARY_FOLDER}/fftw-3"
@ -330,6 +343,8 @@ find_package(FFTW3F REQUIRED)
find_package(LibUSB REQUIRED) # used by so many packages find_package(LibUSB REQUIRED) # used by so many packages
find_package(OpenCV OPTIONAL_COMPONENTS core highgui imgproc imgcodecs videoio) # channeltx/modatv find_package(OpenCV OPTIONAL_COMPONENTS core highgui imgproc imgcodecs videoio) # channeltx/modatv
find_package(LibSigMF) # SigMF recording files support find_package(LibSigMF) # SigMF recording files support
find_package(ZLIB) # For DAB
find_package(FAAD) # For DAB
if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (LIBSIGMF_FOUND AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_definitions(-DHAS_LIBSIGMF) add_definitions(-DHAS_LIBSIGMF)

View File

@ -0,0 +1,31 @@
IF(NOT FAAD_FOUND)
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_FAAD faad2)
FIND_PATH(
FAAD_INCLUDE_DIR
NAMES faad.h
HINTS ${FAAD_DIR}/include
${PC_FAAD_INCLUDE_DIRS}
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
FAAD_LIBRARY
NAMES faad
HINTS ${FAAD_DIR}/lib
${PC_FAAD_LIBRARY_DIRS}
PATHS /usr/local/lib
/usr/lib
/usr/lib64
)
message(STATUS "FAAD LIBRARY " ${FAAD_LIBRARY})
message(STATUS "FAAD INCLUDE DIRS " ${FAAD_INCLUDE_DIR})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FAAD DEFAULT_MSG FAAD_LIBRARY FAAD_INCLUDE_DIR)
MARK_AS_ADVANCED(FAAD_LIBRARY FAAD_INCLUDE_DIR)
ENDIF(NOT FAAD_FOUND)

13
debian/control vendored
View File

@ -20,6 +20,7 @@ Build-Depends: debhelper (>= 9),
qml-module-qtquick-window2, qml-module-qtquick-window2,
qml-module-qtquick-dialogs, qml-module-qtquick-dialogs,
qml-module-qtquick-controls, qml-module-qtquick-controls,
qml-module-qtquick-controls2,
qml-module-qtquick-layouts, qml-module-qtquick-layouts,
libqt5serialport5-dev, libqt5serialport5-dev,
libqt5charts5-dev, libqt5charts5-dev,
@ -36,12 +37,14 @@ Build-Depends: debhelper (>= 9),
bison, bison,
flex, flex,
ffmpeg, ffmpeg,
libfaad-dev,
libavcodec-dev, libavcodec-dev,
libavformat-dev, libavformat-dev,
libopus-dev, libopus-dev,
libairspy-dev, libairspy-dev,
libhackrf-dev, libhackrf-dev,
libuhd-dev libuhd-dev,
zlib1g-dev
# TODO: # TODO:
# - more dependencies based on version; newer has more devices # - more dependencies based on version; newer has more devices
# - manage dependencies not present upstream # - manage dependencies not present upstream
@ -51,16 +54,16 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libasound2, libgl1-mesa-glx, libqt5multimedia5-plugins, pulseaudio, ffmpeg Depends: ${shlibs:Depends}, ${misc:Depends}, libasound2, libgl1-mesa-glx, libqt5multimedia5-plugins, pulseaudio, ffmpeg
Description: SDR/Analyzer/Generator front-end for various hardware Description: SDR/Analyzer/Generator front-end for various hardware
SDR/Analyzer/Generator front-end for Airspy, BladeRF, HackRF, SDR/Analyzer/Generator front-end for Airspy, BladeRF, HackRF,
RTL-SDR, FunCube, LimeSDR, PlutoSDR, USRP. RTL-SDR, FunCube, LimeSDR, PlutoSDR, RSP, USRP.
Also File source and sink for I/Q samples, network I/Q sources with Also File source and sink for I/Q samples, network I/Q sources with
remote instance. remote instance.
Based on Qt5 framework and OpenGL for the spectrum and scope rendering. Based on Qt5 framework and OpenGL for the spectrum and scope rendering.
Builds on Linux, Windows and Mac O/S Builds on Linux, Windows and Mac O/S
Reception modes supported: Reception modes supported:
Analog: AM, ATV, NFM, WFM, SSB, broadcast FM Analog: AM, ATV, NFM, WFM, SSB, broadcast FM, APT
Digital: D-Star, Yaesu SF, DMR, dPMR, LoRa, ADS-B, Packet (AX.25/APRS) Digital: D-Star, Yaesu SF, DMR, dPMR, FreeDV, DAB, DVB-S, LoRa, ADS-B, Packet (AX.25/APRS)
Analyzer: Generic channel Analyzer: Generic channel
Transmission modes supported: Transmission modes supported:
Analog: AM, ATV, NFM, SSB, WFM Analog: AM, ATV, NFM, SSB, WFM
Digital: Packet (AX.25), 802.15.4 Digital: DVB-S, Packet (AX.25), 802.15.4
Homepage: https://github.com/f4exb/sdrangel Homepage: https://github.com/f4exb/sdrangel

BIN
doc/img/DABDemod_plugin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -443,6 +443,51 @@ if (WIN32)
install(FILES "${source_dir}/pthreadVC2${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") install(FILES "${source_dir}/pthreadVC2${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}")
endif (WIN32) endif (WIN32)
# For DAB demodulator
if (ZLIB_FOUND AND FAAD_FOUND)
if (WIN32)
set(DAB_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/dab_lib.lib" CACHE INTERNAL "")
elseif (LINUX)
set(DAB_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib/libdab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "")
endif()
if (WIN32)
set(PTHREADS_OPT_FOR_DAB "-DPTHREADS=${PTHREADS4W_LIBRARIES}" CACHE INTERNAL "")
else()
set(PTHREADS_OPT_FOR_DAB "" CACHE INTERNAL "")
endif()
ExternalProject_Add(dab
GIT_REPOSITORY https://github.com/srcejon/dab-cmdline.git
GIT_TAG msvc
PREFIX "${EXTERNAL_BUILD_LIBRARIES}/dab"
SOURCE_SUBDIR "library"
CMAKE_ARGS ${COMMON_CMAKE_ARGS}
-DFFTW3F_INCLUDE_DIRS=${FFTW3F_INCLUDE_DIRS}
-DFFTW3F_LIBRARIES=${FFTW3F_LIBRARIES}
-DFAAD_INCLUDE_DIR=${FAAD_INCLUDE_DIR}
-DFAAD_LIBRARY=${FAAD_LIBRARY}
-DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIRS}
-DZLIB_LIBRARY=${ZLIB_LIBRARIES}
${PTHREADS_OPT_FOR_DAB}
BUILD_BYPRODUCTS "${DAB_LIBRARIES}"
INSTALL_COMMAND ""
TEST_COMMAND ""
)
ExternalProject_Get_Property(dab source_dir binary_dir)
set(DAB_FOUND ON CACHE INTERNAL "")
set(DAB_EXTERNAL ON CACHE INTERNAL "")
set(DAB_INCLUDE_DIR "${EXTERNAL_BUILD_LIBRARIES}/dab/src/dab" CACHE INTERNAL "")
if (WIN32)
install(FILES "${SDRANGEL_BINARY_BIN_DIR}/dab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}")
elseif (APPLE)
set(DAB_LIBRARIES "${binary_dir}/libapt${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "")
install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}"
FILES_MATCHING PATTERN "libdab_lib*${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/")
endif ()
else()
message(STATUS "Can't build DAB library as missing zlib or faad")
endif()
# Device interface libraries. Use external libraries build for these. Only Linux for now. # Device interface libraries. Use external libraries build for these. Only Linux for now.
if (LINUX) if (LINUX)
# apt install liblimesuite-dev (only on ubuntu 18.04). Can be picky on version though so let's build it. # apt install liblimesuite-dev (only on ubuntu 18.04). Can be picky on version though so let's build it.

View File

@ -18,6 +18,10 @@ add_subdirectory(demodchirpchat)
add_subdirectory(demodvorsc) add_subdirectory(demodvorsc)
add_subdirectory(demodpacket) add_subdirectory(demodpacket)
if(DAB_FOUND AND ZLIB_FOUND AND FAAD_FOUND)
add_subdirectory(demoddab)
endif()
if(APT_FOUND) if(APT_FOUND)
add_subdirectory(demodapt) add_subdirectory(demodapt)
endif() endif()

View File

@ -0,0 +1,64 @@
project(demoddab)
add_definitions(-DDABLIN_AAC_FAAD2)
set(demoddab_SOURCES
dabdemod.cpp
dabdemodsettings.cpp
dabdemodbaseband.cpp
dabdemodsink.cpp
dabdemodplugin.cpp
dabdemodwebapiadapter.cpp
dabdemoddevice.cpp
)
set(demoddab_HEADERS
dabdemod.h
dabdemodsettings.h
dabdemodbaseband.h
dabdemodsink.h
dabdemodplugin.h
dabdemodwebapiadapter.h
dabdemoddevice.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${DAB_INCLUDE_DIR}
)
if(NOT SERVER_MODE)
set(demoddab_SOURCES
${demoddab_SOURCES}
dabdemodgui.cpp
dabdemodgui.ui
)
set(demoddab_HEADERS
${demoddab_HEADERS}
dabdemodgui.h
)
set(TARGET_NAME demoddab)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME demoddabsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${demoddab_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
${DAB_LIBRARIES}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,541 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "dabdemod.h"
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QThread>
#include <stdio.h>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGDABDemodSettings.h"
#include "SWGChannelReport.h"
#include "SWGMapItem.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "util/db.h"
#include "maincore.h"
MESSAGE_CLASS_DEFINITION(DABDemod::MsgConfigureDABDemod, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABEnsembleName, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABProgramName, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABProgramData, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABSystemData, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABProgramQuality, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABFIBQuality, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABSampleRate, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABData, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABReset, Message)
MESSAGE_CLASS_DEFINITION(DABDemod::MsgDABResetService, Message)
const char * const DABDemod::m_channelIdURI = "sdrangel.channel.dabdemod";
const char * const DABDemod::m_channelId = "DABDemod";
DABDemod::DABDemod(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_basebandSampleRate(0)
{
setObjectName(m_channelId);
m_basebandSink = new DABDemodBaseband(this);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
DABDemod::~DABDemod()
{
qDebug("DABDemod::~DABDemod");
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
delete m_basebandSink;
}
uint32_t DABDemod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void DABDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
m_basebandSink->feed(begin, end);
}
void DABDemod::start()
{
qDebug("DABDemod::start");
m_basebandSink->reset();
m_basebandSink->startWork();
m_thread.start();
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
DABDemodBaseband::MsgConfigureDABDemodBaseband *msg = DABDemodBaseband::MsgConfigureDABDemodBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
}
void DABDemod::stop()
{
qDebug("DABDemod::stop");
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
bool DABDemod::handleMessage(const Message& cmd)
{
if (MsgConfigureDABDemod::match(cmd))
{
MsgConfigureDABDemod& cfg = (MsgConfigureDABDemod&) cmd;
qDebug() << "DABDemod::handleMessage: MsgConfigureDABDemod";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "DABDemod::handleMessage: DSPSignalNotification";
m_basebandSink->getInputMessageQueue()->push(rep);
// Forward to GUI if any
if (m_guiMessageQueue)
{
rep = new DSPSignalNotification(notif);
m_guiMessageQueue->push(rep);
}
return true;
}
else if (MsgDABSystemData::match(cmd))
{
MsgDABSystemData& report = (MsgDABSystemData&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABSystemData(report));
}
return true;
}
else if (MsgDABProgramQuality::match(cmd))
{
MsgDABProgramQuality& report = (MsgDABProgramQuality&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABProgramQuality(report));
}
return true;
}
else if (MsgDABFIBQuality::match(cmd))
{
MsgDABFIBQuality& report = (MsgDABFIBQuality&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABFIBQuality(report));
}
return true;
}
else if (MsgDABSampleRate::match(cmd))
{
MsgDABSampleRate& report = (MsgDABSampleRate&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABSampleRate(report));
}
return true;
}
else if (MsgDABEnsembleName::match(cmd))
{
MsgDABEnsembleName& report = (MsgDABEnsembleName&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABEnsembleName(report));
}
return true;
}
else if (MsgDABProgramName::match(cmd))
{
MsgDABProgramName& report = (MsgDABProgramName&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABProgramName(report));
}
return true;
}
else if (MsgDABProgramData::match(cmd))
{
MsgDABProgramData& report = (MsgDABProgramData&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABProgramData(report));
}
return true;
}
else if (MsgDABData::match(cmd))
{
MsgDABData& report = (MsgDABData&)cmd;
if (getMessageQueueToGUI())
{
getMessageQueueToGUI()->push(new MsgDABData(report));
}
return true;
}
else if (MsgDABReset::match(cmd))
{
MsgDABReset& report = (MsgDABReset&)cmd;
m_basebandSink->getInputMessageQueue()->push(new MsgDABReset(report));
return true;
}
else if (MsgDABResetService::match(cmd))
{
MsgDABResetService& report = (MsgDABResetService&)cmd;
m_basebandSink->getInputMessageQueue()->push(new MsgDABResetService(report));
return true;
}
else
{
return false;
}
}
void DABDemod::applySettings(const DABDemodSettings& settings, bool force)
{
qDebug() << "DABDemod::applySettings:"
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
reverseAPIKeys.append("rfBandwidth");
}
if ((settings.m_program != m_settings.m_program) || force) {
reverseAPIKeys.append("program");
}
if ((settings.m_volume != m_settings.m_volume) || force) {
reverseAPIKeys.append("volume");
}
if ((settings.m_audioMute != m_settings.m_audioMute) || force) {
reverseAPIKeys.append("audioMute");
}
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) {
reverseAPIKeys.append("audioDeviceName");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
DABDemodBaseband::MsgConfigureDABDemodBaseband *msg = DABDemodBaseband::MsgConfigureDABDemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
if (settings.m_useReverseAPI)
{
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
m_settings = settings;
}
QByteArray DABDemod::serialize() const
{
return m_settings.serialize();
}
bool DABDemod::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureDABDemod *msg = MsgConfigureDABDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureDABDemod *msg = MsgConfigureDABDemod::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int DABDemod::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDabDemodSettings(new SWGSDRangel::SWGDABDemodSettings());
response.getDabDemodSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int DABDemod::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
DABDemodSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureDABDemod *msg = MsgConfigureDABDemod::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("DABDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureDABDemod *msgToGUI = MsgConfigureDABDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void DABDemod::webapiUpdateChannelSettings(
DABDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getDabDemodSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("rfBandwidth")) {
settings.m_rfBandwidth = response.getDabDemodSettings()->getRfBandwidth();
}
if (channelSettingsKeys.contains("program")) {
settings.m_program = *response.getDabDemodSettings()->getProgram();
}
if (channelSettingsKeys.contains("m_volume")) {
settings.m_volume = response.getDabDemodSettings()->getVolume();
}
if (channelSettingsKeys.contains("audioMute")) {
settings.m_audioMute = response.getDabDemodSettings()->getAudioMute();
}
if (channelSettingsKeys.contains("audioDeviceName")) {
settings.m_audioDeviceName = *response.getDabDemodSettings()->getAudioDeviceName();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getDabDemodSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getDabDemodSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getDabDemodSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getDabDemodSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getDabDemodSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getDabDemodSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getDabDemodSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getDabDemodSettings()->getReverseApiChannelIndex();
}
}
void DABDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DABDemodSettings& settings)
{
response.getDabDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getDabDemodSettings()->setRfBandwidth(settings.m_rfBandwidth);
response.getDabDemodSettings()->setProgram(new QString(settings.m_program));
response.getDabDemodSettings()->setVolume(settings.m_volume);
response.getDabDemodSettings()->setAudioMute(settings.m_audioMute);
response.getDabDemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
response.getDabDemodSettings()->setRgbColor(settings.m_rgbColor);
if (response.getDabDemodSettings()->getTitle()) {
*response.getDabDemodSettings()->getTitle() = settings.m_title;
} else {
response.getDabDemodSettings()->setTitle(new QString(settings.m_title));
}
response.getDabDemodSettings()->setStreamIndex(settings.m_streamIndex);
response.getDabDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getDabDemodSettings()->getReverseApiAddress()) {
*response.getDabDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getDabDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getDabDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getDabDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getDabDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
}
void DABDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DABDemodSettings& 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 DABDemod::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DABDemodSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("DABDemod"));
swgChannelSettings->setDabDemodSettings(new SWGSDRangel::SWGDABDemodSettings());
SWGSDRangel::SWGDABDemodSettings *swgDABDemodSettings = swgChannelSettings->getDabDemodSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgDABDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("rfBandwidth") || force) {
swgDABDemodSettings->setRfBandwidth(settings.m_rfBandwidth);
}
if (channelSettingsKeys.contains("program") || force) {
swgDABDemodSettings->setProgram(new QString(settings.m_program));
}
if (channelSettingsKeys.contains("volume") || force) {
swgDABDemodSettings->setVolume(settings.m_volume);
}
if (channelSettingsKeys.contains("audioMute") || force) {
swgDABDemodSettings->setAudioMute(settings.m_audioMute);
}
if (channelSettingsKeys.contains("audioDeviceName") || force) {
swgDABDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgDABDemodSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgDABDemodSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgDABDemodSettings->setStreamIndex(settings.m_streamIndex);
}
}
void DABDemod::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "DABDemod::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("DABDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

View File

@ -0,0 +1,375 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMOD_H
#define INCLUDE_DABDEMOD_H
#include <vector>
#include <QNetworkRequest>
#include <QThread>
#include "dsp/basebandsamplesink.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "dabdemodbaseband.h"
#include "dabdemodsettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class DABDemod : public BasebandSampleSink, public ChannelAPI {
Q_OBJECT
public:
class MsgConfigureDABDemod : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DABDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureDABDemod* create(const DABDemodSettings& settings, bool force)
{
return new MsgConfigureDABDemod(settings, force);
}
private:
DABDemodSettings m_settings;
bool m_force;
MsgConfigureDABDemod(const DABDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgDABEnsembleName : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QString getName() const { return m_name; }
int getId() const { return m_id; }
static MsgDABEnsembleName* create(const QString& name, int id)
{
return new MsgDABEnsembleName(name, id);
}
private:
QString m_name;
int m_id;
MsgDABEnsembleName(const QString& name, int id) :
Message(),
m_name(name),
m_id(id)
{ }
};
class MsgDABProgramName : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QString getName() const { return m_name; }
int getId() const { return m_id; }
static MsgDABProgramName* create(const QString& name, int id)
{
return new MsgDABProgramName(name, id);
}
private:
QString m_name;
int m_id;
MsgDABProgramName(const QString& name, int id) :
Message(),
m_name(name),
m_id(id)
{ }
};
class MsgDABProgramData: public Message {
MESSAGE_CLASS_DECLARATION
public:
int getBitrate() const { return m_bitrate; }
const QString getAudio() const { return m_audio; }
const QString getLanguage() const { return m_language; }
const QString getProgramType() const { return m_programType; }
static MsgDABProgramData* create(int bitrate, const QString& audio, const QString& language, const QString& programType)
{
return new MsgDABProgramData(bitrate, audio, language, programType);
}
private:
int m_bitrate;
QString m_audio;
QString m_language;
QString m_programType;
MsgDABProgramData(int bitrate, const QString& audio, const QString& language, const QString& programType) :
Message(),
m_bitrate(bitrate),
m_audio(audio),
m_language(language),
m_programType(programType)
{ }
};
class MsgDABSystemData : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getSync() const { return m_sync; }
int16_t getSNR() const { return m_snr; }
int32_t getFrequencyOffset() const { return m_frequencyOffset; }
static MsgDABSystemData* create(bool sync, int16_t snr, int32_t frequencyOffset)
{
return new MsgDABSystemData(sync, snr, frequencyOffset);
}
private:
bool m_sync;
int16_t m_snr;
int32_t m_frequencyOffset;
MsgDABSystemData(bool sync, int16_t snr, int32_t frequencyOffset) :
Message(),
m_sync(sync),
m_snr(snr),
m_frequencyOffset(frequencyOffset)
{ }
};
class MsgDABProgramQuality : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getFrames() const { return m_frames; }
int getRS() const { return m_rs; }
int getAAC() const { return m_aac; }
static MsgDABProgramQuality* create(int frames, int rs, int aac)
{
return new MsgDABProgramQuality(frames, rs, aac);
}
private:
int m_frames;
int m_rs;
int m_aac;
MsgDABProgramQuality(int frames, int rs, int aac) :
Message(),
m_frames(frames),
m_rs(rs),
m_aac(aac)
{ }
};
class MsgDABFIBQuality : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getPercent() const { return m_percent; }
static MsgDABFIBQuality* create(int percent)
{
return new MsgDABFIBQuality(percent);
}
private:
int m_percent;
MsgDABFIBQuality(int percent) :
Message(),
m_percent(percent)
{ }
};
class MsgDABSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getSampleRate() const { return m_sampleRate; }
static MsgDABSampleRate* create(int sampleRate)
{
return new MsgDABSampleRate(sampleRate);
}
private:
int m_sampleRate;
MsgDABSampleRate(int sampleRate) :
Message(),
m_sampleRate(sampleRate)
{ }
};
class MsgDABData : public Message {
MESSAGE_CLASS_DECLARATION
public:
const QString getData() const { return m_data; }
static MsgDABData* create(const QString& data)
{
return new MsgDABData(data);
}
private:
QString m_data;
MsgDABData(const QString& data) :
Message(),
m_data(data)
{ }
};
class MsgDABReset : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgDABReset* create()
{
return new MsgDABReset();
}
private:
MsgDABReset() :
Message()
{ }
};
class MsgDABResetService : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgDABResetService* create()
{
return new MsgDABResetService();
}
private:
MsgDABResetService() :
Message()
{ }
};
DABDemod(DeviceAPI *deviceAPI);
virtual ~DABDemod();
virtual void destroy() { delete this; }
using BasebandSampleSink::feed;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual const QString& getURI() const { return getName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return 0; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return 0;
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const DABDemodSettings& settings);
static void webapiUpdateChannelSettings(
DABDemodSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
double getMagSq() const { return m_basebandSink->getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
}
/* void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}*/
uint32_t getNumberOfDeviceStreams() const;
static const char * const m_channelIdURI;
static const char * const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
DABDemodBaseband* m_basebandSink;
DABDemodSettings m_settings;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
qint64 m_centerFrequency;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void applySettings(const DABDemodSettings& settings, bool force = false);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DABDemodSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DABDemodSettings& settings,
bool force
);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_DABDEMOD_H

View File

@ -0,0 +1,203 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/downchannelizer.h"
#include "dabdemodbaseband.h"
#include "dabdemod.h"
MESSAGE_CLASS_DEFINITION(DABDemodBaseband::MsgConfigureDABDemodBaseband, Message)
DABDemodBaseband::DABDemodBaseband(DABDemod *packetDemod) :
m_sink(packetDemod),
m_running(false),
m_mutex(QMutex::Recursive)
{
qDebug("DABDemodBaseband::DABDemodBaseband");
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(1000000));
m_channelizer = new DownChannelizer(&m_sink);
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue());
m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate());
}
DABDemodBaseband::~DABDemodBaseband()
{
m_inputMessageQueue.clear();
DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo());
delete m_channelizer;
}
void DABDemodBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_sampleFifo.reset();
}
void DABDemodBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&DABDemodBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_running = true;
}
void DABDemodBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&DABDemodBaseband::handleData
);
m_running = false;
}
void DABDemodBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void DABDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void DABDemodBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
{
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if (part1begin != part1end) {
m_channelizer->feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
m_channelizer->feed(part2begin, part2end);
}
m_sampleFifo.readCommit((unsigned int) count);
}
}
void DABDemodBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool DABDemodBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureDABDemodBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureDABDemodBaseband& cfg = (MsgConfigureDABDemodBaseband&) cmd;
qDebug() << "DABDemodBaseband::handleMessage: MsgConfigureDABDemodBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "DABDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
return true;
}
else if (DABDemod::MsgDABReset::match(cmd))
{
m_sink.reset();
return true;
}
else if (DABDemod::MsgDABResetService::match(cmd))
{
m_sink.resetService();
return true;
}
else
{
return false;
}
}
void DABDemodBaseband::applySettings(const DABDemodSettings& settings, bool force)
{
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(DABDEMOD_CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}
if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
{
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName);
audioDeviceManager->removeAudioSink(m_sink.getAudioFifo());
audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex);
int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
if (m_sink.getAudioSampleRate() != audioSampleRate)
{
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
m_sink.applyAudioSampleRate(audioSampleRate);
}
}
m_sink.applySettings(settings, force);
m_settings = settings;
}
void DABDemodBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}

View File

@ -0,0 +1,95 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_DABDEMODBASEBAND_H
#define INCLUDE_DABDEMODBASEBAND_H
#include <QObject>
#include <QMutex>
#include "dsp/samplesinkfifo.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "dabdemodsink.h"
class DownChannelizer;
class ChannelAPI;
class DABDemod;
class DABDemodBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureDABDemodBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DABDemodSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureDABDemodBaseband* create(const DABDemodSettings& settings, bool force)
{
return new MsgConfigureDABDemodBaseband(settings, force);
}
private:
DABDemodSettings m_settings;
bool m_force;
MsgConfigureDABDemodBaseband(const DABDemodSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
DABDemodBaseband(DABDemod *packetDemod);
~DABDemodBaseband();
void reset();
void startWork();
void stopWork();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_sink.getMagSqLevels(avg, peak, nbSamples);
}
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
void setChannel(ChannelAPI *channel);
double getMagSq() const { return m_sink.getMagSq(); }
bool isRunning() const { return m_running; }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
DABDemodSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
DABDemodSettings m_settings;
bool m_running;
QMutex m_mutex;
bool handleMessage(const Message& cmd);
void applySettings(const DABDemodSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_DABDEMODBASEBAND_H

View File

@ -0,0 +1,120 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2010, 2011, 2012 Jan van Katwijk (J.vanKatwijk@gmail.com)
// 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 "dabdemoddevice.h"
// Implementation of default device, as not included in library
deviceHandler::deviceHandler(void)
{
lastFrequency = 100000;
}
deviceHandler::~deviceHandler(void)
{
}
bool deviceHandler::restartReader(int32_t)
{
return true;
}
void deviceHandler::stopReader(void)
{
}
void deviceHandler::run(void)
{
}
int32_t deviceHandler::getSamples(std::complex<float> *v, int32_t amount)
{
(void)v;
(void)amount;
return 0;
}
int32_t deviceHandler::Samples(void)
{
return 0;
}
int32_t deviceHandler::defaultFrequency(void)
{
return 220000000;
}
void deviceHandler::resetBuffer(void)
{
}
void deviceHandler::setGain(int32_t x)
{
(void)x;
}
bool deviceHandler::has_autogain(void)
{
return false;
}
void deviceHandler::set_autogain(bool b)
{
(void)b;
}
void deviceHandler::set_ifgainReduction(int x)
{
(void)x;
}
void deviceHandler::set_lnaState(int x)
{
(void)x;
}
// Device with source data from SDRangel
// Other methods not needed for DAB library
DABDemodDevice::DABDemodDevice() :
m_iqBuffer (4 * 1024 * 1024)
{
}
void DABDemodDevice::putSample(std::complex<float> s)
{
std::complex<float> localBuf[1];
localBuf[0] = s;
if (m_iqBuffer.GetRingBufferWriteAvailable() > 0)
m_iqBuffer.putDataIntoBuffer(localBuf, 1);
}
int32_t DABDemodDevice::getSamples(std::complex<float> *buf, int32_t size)
{
return m_iqBuffer.getDataFromBuffer(buf, size);
}
int32_t DABDemodDevice::Samples(void)
{
return m_iqBuffer.GetRingBufferReadAvailable();
}
void DABDemodDevice::resetBuffer(void)
{
m_iqBuffer.FlushRingBuffer();
}

View File

@ -0,0 +1,38 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMODDEVICE_H
#define INCLUDE_DABDEMODDEVICE_H
#include <dab-api.h>
class DABDemodDevice : public deviceHandler {
public:
DABDemodDevice();
void putSample(std::complex<float> s);
int32_t getSamples(std::complex<float> *buf, int32_t size) override;
int32_t Samples(void) override;
void resetBuffer(void) override;
private:
RingBuffer<std::complex<float>> m_iqBuffer;
};
#endif // INCLUDE_DABDEMODDEVICE_H

View File

@ -0,0 +1,651 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <limits>
#include <ctype.h>
#include <QDebug>
#include <QAction>
#include <QRegExp>
#include "dabdemodgui.h"
#include "device/deviceuiset.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "ui_dabdemodgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "gui/audioselectdialog.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "dsp/dspengine.h"
#include "gui/crightclickenabler.h"
#include "channel/channelwebapiutils.h"
#include "maincore.h"
#include "dabdemod.h"
#include "dabdemodsink.h"
// Table column indexes
#define PROGRAMS_COL_NAME 0
#define PROGRAMS_COL_ID 1
#define PROGRAMS_COL_FREQUENCY 2
void DABDemodGUI::resizeTable()
{
// Fill table with a row of dummy data that will size the columns nicely
// Trailing spaces are for sort arrow
int row = ui->programs->rowCount();
ui->programs->setRowCount(row + 1);
ui->programs->setItem(row, PROGRAMS_COL_NAME, new QTableWidgetItem("Some Random Radio Station"));
ui->programs->setItem(row, PROGRAMS_COL_ID, new QTableWidgetItem("123456"));
ui->programs->setItem(row, PROGRAMS_COL_FREQUENCY, new QTableWidgetItem("200.000"));
ui->programs->resizeColumnsToContents();
ui->programs->removeRow(row);
}
// Columns in table reordered
void DABDemodGUI::programs_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{
(void) oldVisualIndex;
m_settings.m_columnIndexes[logicalIndex] = newVisualIndex;
}
// Column in table resized (when hidden size is 0)
void DABDemodGUI::programs_sectionResized(int logicalIndex, int oldSize, int newSize)
{
(void) oldSize;
m_settings.m_columnSizes[logicalIndex] = newSize;
}
// Right click in table header - show column select menu
void DABDemodGUI::columnSelectMenu(QPoint pos)
{
menu->popup(ui->programs->horizontalHeader()->viewport()->mapToGlobal(pos));
}
// Hide/show column when menu selected
void DABDemodGUI::columnSelectMenuChecked(bool checked)
{
(void) checked;
QAction* action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
int idx = action->data().toInt(nullptr);
ui->programs->setColumnHidden(idx, !action->isChecked());
}
}
// Create column select menu item
QAction *DABDemodGUI::createCheckableItem(QString &text, int idx, bool checked)
{
QAction *action = new QAction(text, this);
action->setCheckable(true);
action->setChecked(checked);
action->setData(QVariant(idx));
connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked()));
return action;
}
DABDemodGUI* DABDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
DABDemodGUI* gui = new DABDemodGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void DABDemodGUI::destroy()
{
delete this;
}
void DABDemodGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray DABDemodGUI::serialize() const
{
return m_settings.serialize();
}
bool DABDemodGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
// Add row to table
void DABDemodGUI::addProgramName(const DABDemod::MsgDABProgramName& program)
{
ui->programs->setSortingEnabled(false);
int row = ui->programs->rowCount();
ui->programs->setRowCount(row + 1);
QTableWidgetItem *nameItem = new QTableWidgetItem();
QTableWidgetItem *idItem = new QTableWidgetItem();
QTableWidgetItem *frequencyItem = new QTableWidgetItem();
ui->programs->setItem(row, PROGRAMS_COL_NAME, nameItem);
ui->programs->setItem(row, PROGRAMS_COL_ID, idItem);
ui->programs->setItem(row, PROGRAMS_COL_FREQUENCY, frequencyItem);
nameItem->setText(program.getName());
idItem->setText(QString::number(program.getId()));
double frequencyInHz;
if (ChannelWebAPIUtils::getCenterFrequency(m_dabDemod->getDeviceSetIndex(), frequencyInHz))
{
double frequencyInMHz = (frequencyInHz+m_settings.m_inputFrequencyOffset)/1e6;
frequencyItem->setText(QString::number(frequencyInMHz, 'f', 3));
frequencyItem->setData(Qt::UserRole, frequencyInHz+m_settings.m_inputFrequencyOffset);
}
else
frequencyItem->setData(Qt::UserRole, 0.0);
ui->programs->setSortingEnabled(true);
filterRow(row);
}
// Tune to the selected program
void DABDemodGUI::on_programs_cellDoubleClicked(int row, int column)
{
(void) column;
m_settings.m_program = ui->programs->item(row, PROGRAMS_COL_NAME)->text();
double frequencyInHz = ui->programs->item(row, PROGRAMS_COL_FREQUENCY)->data(Qt::UserRole).toDouble();
ChannelWebAPIUtils::setCenterFrequency(m_dabDemod->getDeviceSetIndex(), frequencyInHz-m_settings.m_inputFrequencyOffset);
applySettings();
}
bool DABDemodGUI::handleMessage(const Message& message)
{
if (DABDemod::MsgConfigureDABDemod::match(message))
{
qDebug("DABDemodGUI::handleMessage: DABDemod::MsgConfigureDABDemod");
const DABDemod::MsgConfigureDABDemod& cfg = (DABDemod::MsgConfigureDABDemod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (DSPSignalNotification&) message;
m_basebandSampleRate = notif.getSampleRate();
return true;
}
else if (DABDemod::MsgDABEnsembleName::match(message))
{
DABDemod::MsgDABEnsembleName& report = (DABDemod::MsgDABEnsembleName&) message;
ui->ensemble->setText(report.getName());
return true;
}
else if (DABDemod::MsgDABProgramName::match(message))
{
DABDemod::MsgDABProgramName& report = (DABDemod::MsgDABProgramName&) message;
addProgramName(report);
return true;
}
else if (DABDemod::MsgDABProgramData::match(message))
{
DABDemod::MsgDABProgramData& report = (DABDemod::MsgDABProgramData&) message;
ui->program->setText(m_settings.m_program);
ui->bitrate->setText(QString("%1kbps").arg(report.getBitrate()));
ui->audio->setText(report.getAudio());
ui->language->setText(report.getLanguage());
ui->programType->setText(report.getProgramType());
return true;
}
else if (DABDemod::MsgDABSystemData::match(message))
{
DABDemod::MsgDABSystemData& report = (DABDemod::MsgDABSystemData&) message;
if (report.getSync())
ui->sync->setText("Yes");
else
ui->sync->setText("No");
ui->snr->setText(QString("%1").arg(report.getSNR()));
ui->freqOffset->setText(QString("%1").arg(report.getFrequencyOffset()));
return true;
}
else if (DABDemod::MsgDABProgramQuality::match(message))
{
DABDemod::MsgDABProgramQuality& report = (DABDemod::MsgDABProgramQuality&) message;
ui->frames->setText(QString("%1%").arg(report.getFrames()));
ui->rs->setText(QString("%1%").arg(report.getRS()));
ui->aac->setText(QString("%1%").arg(report.getAAC()));
return true;
}
else if (DABDemod::MsgDABFIBQuality::match(message))
{
DABDemod::MsgDABFIBQuality& report = (DABDemod::MsgDABFIBQuality&) message;
ui->fib->setText(QString("%1%").arg(report.getPercent()));
return true;
}
else if (DABDemod::MsgDABSampleRate::match(message))
{
DABDemod::MsgDABSampleRate& report = (DABDemod::MsgDABSampleRate&) message;
qDebug() << "Ssample rate: " << report.getSampleRate();
ui->sampleRate->setText(QString("%1k").arg(report.getSampleRate()/1000.0, 0, 'f', 0));
return true;
}
else if (DABDemod::MsgDABData::match(message))
{
DABDemod::MsgDABData& report = (DABDemod::MsgDABData&) message;
ui->data->setText(report.getData());
return true;
}
return false;
}
void DABDemodGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void DABDemodGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void DABDemodGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void DABDemodGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void DABDemodGUI::on_audioMute_toggled(bool checked)
{
m_settings.m_audioMute = checked;
applySettings();
}
void DABDemodGUI::on_volume_valueChanged(int value)
{
ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
m_settings.m_volume = value / 10.0;
applySettings();
}
void DABDemodGUI::on_rfBW_valueChanged(int value)
{
float bw = value * 100.0f;
ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_channelMarker.setBandwidth(bw);
m_settings.m_rfBandwidth = bw;
applySettings();
}
void DABDemodGUI::on_filter_editingFinished()
{
m_settings.m_filter = ui->filter->text();
filter();
applySettings();
}
void DABDemodGUI::on_clearTable_clicked()
{
ui->programs->setRowCount(0);
// Reset the DAB library, so it re-outputs program names
DABDemod::MsgDABReset* message = DABDemod::MsgDABReset::create();
m_dabDemod->getInputMessageQueue()->push(message);
}
void DABDemodGUI::filterRow(int row)
{
bool hidden = false;
if (m_settings.m_filter != "")
{
QRegExp re(m_settings.m_filter);
QTableWidgetItem *fromItem = ui->programs->item(row, PROGRAMS_COL_NAME);
if (re.indexIn(fromItem->text()) == -1)
hidden = true;
}
ui->programs->setRowHidden(row, hidden);
}
void DABDemodGUI::filter()
{
for (int i = 0; i < ui->programs->rowCount(); i++)
{
filterRow(i);
}
}
void DABDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
void DABDemodGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.move(p);
dialog.exec();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine))
{
DeviceStreamSelectionDialog dialog(this);
dialog.setNumberOfStreams(m_dabDemod->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();
}
DABDemodGUI::DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::DABDemodGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_doApplySettings(true),
m_tickCount(0),
m_channelFreq(0.0)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_dabDemod = reinterpret_cast<DABDemod*>(rxChannel);
m_dabDemod->setMessageQueueToGUI(getInputMessageQueue());
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect()));
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle("Packet Demodulator");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
setTitleColor(m_channelMarker.getColor());
m_settings.setChannelMarker(&m_channelMarker);
m_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
// Resize the table using dummy data
resizeTable();
// Allow user to reorder columns
ui->programs->horizontalHeader()->setSectionsMovable(true);
// Allow user to sort table by clicking on headers
ui->programs->setSortingEnabled(true);
// Add context menu to allow hiding/showing of columns
menu = new QMenu(ui->programs);
for (int i = 0; i < ui->programs->horizontalHeader()->count(); i++)
{
QString text = ui->programs->horizontalHeaderItem(i)->text();
menu->addAction(createCheckableItem(text, i, true));
}
ui->programs->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->programs->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint)));
// Get signals when columns change
connect(ui->programs->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(programs_sectionMoved(int, int, int)));
connect(ui->programs->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(programs_sectionResized(int, int, int)));
displaySettings();
applySettings(true);
}
DABDemodGUI::~DABDemodGUI()
{
delete ui;
}
void DABDemodGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void DABDemodGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
DABDemod::MsgConfigureDABDemod* message = DABDemod::MsgConfigureDABDemod::create( m_settings, force);
m_dabDemod->getInputMessageQueue()->push(message);
}
}
void DABDemodGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->audioMute->setChecked(m_settings.m_audioMute);
ui->volume->setValue(m_settings.m_volume * 10.0);
ui->volumeText->setText(QString("%1").arg(m_settings.m_volume, 0, 'f', 1));
ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
displayStreamIndex();
ui->filter->setText(m_settings.m_filter);
// Order and size columns
QHeaderView *header = ui->programs->horizontalHeader();
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
{
bool hidden = m_settings.m_columnSizes[i] == 0;
header->setSectionHidden(i, hidden);
menu->actions().at(i)->setChecked(!hidden);
if (m_settings.m_columnSizes[i] > 0)
ui->programs->setColumnWidth(i, m_settings.m_columnSizes[i]);
header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]);
}
filter();
blockApplySettings(false);
}
void DABDemodGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
} else {
setStreamIndicator("S"); // single channel indicator
}
}
void DABDemodGUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);
}
void DABDemodGUI::enterEvent(QEvent*)
{
m_channelMarker.setHighlighted(true);
}
void DABDemodGUI::resetService()
{
// Reset DAB audio service, to avoid unpleasent noise when changing frequency
DABDemod::MsgDABResetService* message = DABDemod::MsgDABResetService::create();
m_dabDemod->getInputMessageQueue()->push(message);
// Clear current program
ui->program->setText("-");
ui->ensemble->setText("-");
ui->programType->setText("-");
ui->language->setText("-");
ui->audio->setText("-");
ui->bitrate->setText("-");
ui->sampleRate->setText("-");
ui->data->setText("");
}
void DABDemodGUI::on_channel_currentIndexChanged(int index)
{
(void) index;
QString text = ui->channel->currentText();
if (!text.isEmpty())
{
resetService();
// Tune to requested channel
QString freq = text.split(" ")[2];
m_channelFreq = freq.toDouble() * 1e6;
ChannelWebAPIUtils::setCenterFrequency(m_dabDemod->getDeviceSetIndex(), m_channelFreq - m_settings.m_inputFrequencyOffset);
}
}
void DABDemodGUI::audioSelect()
{
AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName);
audioSelect.exec();
if (audioSelect.m_selected)
{
m_settings.m_audioDeviceName = audioSelect.m_audioDeviceName;
applySettings();
}
}
void DABDemodGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_dabDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(100.0f + powDbAvg) / 100.0f,
(100.0f + powDbPeak) / 100.0f,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
}
// Update channel combobox according to current frequency
double frequencyInHz;
if (ChannelWebAPIUtils::getCenterFrequency(m_dabDemod->getDeviceSetIndex(), frequencyInHz))
{
frequencyInHz += m_settings.m_inputFrequencyOffset;
double frequencyInkHz = std::round(frequencyInHz / 1e3);
double frequencyInMHz = frequencyInkHz / 1000.0;
if (m_channelFreq != frequencyInMHz * 1e6)
{
QString freqText = QString::number(frequencyInMHz, 'f', 3);
int i;
for (i = 0; i < ui->channel->count(); i++)
{
if (ui->channel->itemText(i).contains(freqText))
{
ui->channel->blockSignals(true);
ui->channel->setCurrentIndex(i);
ui->channel->blockSignals(false);
break;
}
}
if (i == ui->channel->count())
{
ui->channel->setCurrentIndex(-1);
m_channelFreq = -1.0;
}
}
}
m_tickCount++;
}

View File

@ -0,0 +1,113 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMODGUI_H
#define INCLUDE_DABDEMODGUI_H
#include <QAbstractListModel>
#include <QTableWidgetItem>
#include <QMenu>
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
#include "util/messagequeue.h"
#include "dabdemodsettings.h"
#include "dabdemod.h"
class PluginAPI;
class DeviceUISet;
class BasebandSampleSink;
class DABDemod;
class DABDemodGUI;
namespace Ui {
class DABDemodGUI;
}
class DABDemodGUI;
class DABDemodGUI : public ChannelGUI {
Q_OBJECT
public:
static DABDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
private:
Ui::DABDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
DABDemodSettings m_settings;
bool m_doApplySettings;
DABDemod* m_dabDemod;
int m_basebandSampleRate;
uint32_t m_tickCount;
MessageQueue m_inputMessageQueue;
double m_channelFreq;
QMenu *menu; // Column select context menu
explicit DABDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~DABDemodGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayStreamIndex();
void addProgramName(const DABDemod::MsgDABProgramName& program);
bool handleMessage(const Message& message);
void audioSelect();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
void resetService();
void resizeTable();
QAction *createCheckableItem(QString& text, int idx, bool checked);
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_audioMute_toggled(bool checked);
void on_volume_valueChanged(int value);
void on_rfBW_valueChanged(int index);
void on_filter_editingFinished();
void on_clearTable_clicked();
void on_programs_cellDoubleClicked(int row, int column);
void on_channel_currentIndexChanged(int index);
void filterRow(int row);
void filter();
void programs_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void programs_sectionResized(int logicalIndex, int oldSize, int newSize);
void columnSelectMenu(QPoint pos);
void columnSelectMenuChecked(bool checked = false);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
};
#endif // INCLUDE_DABDEMODGUI_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "dabdemodgui.h"
#endif
#include "dabdemod.h"
#include "dabdemodwebapiadapter.h"
#include "dabdemodplugin.h"
const PluginDescriptor DABDemodPlugin::m_pluginDescriptor = {
DABDemod::m_channelId,
QStringLiteral("DAB Demodulator"),
QStringLiteral("6.9.0"),
QStringLiteral("(c) Jon Beniston, M7RCE. DAB library by Jvan Katwijk"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
DABDemodPlugin::DABDemodPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& DABDemodPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void DABDemodPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerRxChannel(DABDemod::m_channelIdURI, DABDemod::m_channelId, this);
}
void DABDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
DABDemod *instance = new DABDemod(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* DABDemodPlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return 0;
}
#else
ChannelGUI* DABDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return DABDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* DABDemodPlugin::createChannelWebAPIAdapter() const
{
return new DABDemodWebAPIAdapter();
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMODPLUGIN_H
#define INCLUDE_DABDEMODPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class DABDemodPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.dabdemod")
public:
explicit DABDemodPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_DABDEMODPLUGIN_H

View File

@ -0,0 +1,149 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "dabdemodsettings.h"
DABDemodSettings::DABDemodSettings() :
m_channelMarker(0)
{
resetToDefaults();
}
void DABDemodSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_rfBandwidth = 1537000.0f;
m_filter = "";
m_volume = 5.0f;
m_audioMute = false;
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_rgbColor = QColor(77, 105, 25).rgb();
m_title = "DAB Demodulator";
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
{
m_columnIndexes[i] = i;
m_columnSizes[i] = -1; // Autosize
}
}
QByteArray DABDemodSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_streamIndex);
s.writeString(3, m_filter);
s.writeFloat(4, m_rfBandwidth);
s.writeFloat(5, m_volume);
s.writeBool(6, m_audioMute);
s.writeString(7, m_audioDeviceName);
if (m_channelMarker) {
s.writeBlob(8, m_channelMarker->serialize());
}
s.writeU32(9, m_rgbColor);
s.writeString(10, m_title);
s.writeBool(11, m_useReverseAPI);
s.writeString(12, m_reverseAPIAddress);
s.writeU32(13, m_reverseAPIPort);
s.writeU32(14, m_reverseAPIDeviceIndex);
s.writeU32(15, m_reverseAPIChannelIndex);
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
s.writeS32(100 + i, m_columnIndexes[i]);
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
s.writeS32(200 + i, m_columnSizes[i]);
return s.final();
}
bool DABDemodSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_streamIndex, 0);
d.readString(3, &m_filter, "");
d.readFloat(4, &m_rfBandwidth, 1537000.0f);
d.readFloat(5, &m_volume, 5.0f);
d.readBool(6, &m_audioMute, false);
d.readString(7, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readBlob(8, &bytetmp);
if (m_channelMarker) {
m_channelMarker->deserialize(bytetmp);
}
d.readU32(9, &m_rgbColor, QColor(77, 105, 25).rgb());
d.readString(10, &m_title, "DAB Demodulator");
d.readBool(11, &m_useReverseAPI, false);
d.readString(12, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(13, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(14, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(15, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
d.readS32(100 + i, &m_columnIndexes[i], i);
for (int i = 0; i < DABDEMOD_COLUMNS; i++)
d.readS32(200 + i, &m_columnSizes[i], -1);
return true;
}
else
{
resetToDefaults();
return false;
}
}

View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMODSETTINGS_H
#define INCLUDE_DABDEMODSETTINGS_H
#include <QByteArray>
class Serializable;
// Number of columns in the table
#define DABDEMOD_COLUMNS 3
struct DABDemodSettings
{
qint32 m_inputFrequencyOffset;
Real m_rfBandwidth;
QString m_filter;
QString m_program;
Real m_volume;
bool m_audioMute;
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
QString m_audioDeviceName;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
int m_columnIndexes[DABDEMOD_COLUMNS];//!< How the columns are ordered in the table
int m_columnSizes[DABDEMOD_COLUMNS]; //!< Size of the columns in the table
DABDemodSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
#endif /* INCLUDE_DABDEMODSETTINGS_H */

View File

@ -0,0 +1,644 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <complex.h>
#include "dsp/dspengine.h"
#include "dsp/datafifo.h"
#include "util/db.h"
#include "pipes/pipeendpoint.h"
#include "maincore.h"
#include "dabdemod.h"
#include "dabdemodsink.h"
// Callbacks from DAB library
void syncHandler(bool value, void *ctx)
{
(void)value;
(void)ctx;
}
void systemDataHandler(bool sync, int16_t snr, int32_t freqOffset, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->systemData(sync, snr, freqOffset);
}
void ensembleNameHandler(std::string name, int32_t id, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->ensembleName(QString::fromStdString(name), id);
}
void programNameHandler(std::string name, int32_t id, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->programName(QString::fromStdString(name), id);
}
void fibQualityHandler(int16_t percent, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->fibQuality(percent);
}
void audioHandler(int16_t *buffer, int size, int samplerate, bool stereo, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->audio(buffer, size, samplerate, stereo);
}
void dataHandler(std::string data, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->data(QString::fromStdString(data));
}
void byteHandler(uint8_t *data, int16_t a, uint8_t b, void *ctx)
{
(void)data;
(void)a;
(void)b;
(void)ctx;
}
// Note: North America has different table
static const char *dabProgramType[] =
{
"No programme type",
"News",
"Current Affairs",
"Information",
"Sport",
"Education",
"Drama",
"Culture",
"Science",
"Varied",
"Pop Music",
"Rock Music",
"Easy Listening Music",
"Light Classical",
"Serious Classical",
"Other Music",
"Weather/meteorology",
"Finance/Business",
"Children's programmes",
"Social Affairs",
"Religion",
"Phone In",
"Travel",
"Leisure",
"Jazz Music",
"Country Music",
"National Music",
"Oldies Music",
"Folk Music",
"Documentary",
"Not used",
"Not used",
};
static const char *dabLanguageCode[] =
{
"Unknown",
"Albanian",
"Breton",
"Catalan",
"Croatian",
"Welsh",
"Czech",
"Danish",
"German",
"English",
"Spanish",
"Esperanto",
"Estonian",
"Basque",
"Faroese",
"French",
"Frisian",
"Irish",
"Gaelic",
"Galician",
"Icelandic",
"Italian",
"Sami",
"Latin",
"Latvian",
"Luxembourgian",
"Lithuanian",
"Hungarian",
"Maltese",
"Dutch",
"Norwegian",
"Occitan",
"Polish",
"Portuguese",
"Romanian",
"Romansh",
"Serbian",
"Slovak",
"Slovene",
"Finnish",
"Swedish",
"Turkish",
"Flemish",
"Walloon",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Background sound",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Zulu",
"Vietnamese",
"Uzbek",
"Urdu",
"Ukranian",
"Thai",
"Telugu",
"Tatar",
"Tamil",
"Tadzhik",
"Swahili",
"Sranan Tongo",
"Somali",
"Sinhalese",
"Shona",
"Serbo-Croat",
"Rusyn",
"Russian",
"Quechua",
"Pushtu",
"Punjabi",
"Persian",
"Papiamento",
"Oriya",
"Nepali",
"Ndebele",
"Marathi",
"Moldavian",
"Malaysian",
"Malagasay",
"Macedonian",
"Laotian",
"Korean",
"Khmer",
"Kazakh",
"Kannada",
"Japanese",
"Indonesian",
"Hindi",
"Hebrew",
"Hausa",
"Gurani",
"Gujurati",
"Greek",
"Georgian",
"Fulani",
"Dari",
"Chuvash",
"Chinese",
"Burmese",
"Bulgarian",
"Bengali",
"Belorussian",
"Bambora",
"Azerbaijani",
"Assamese",
"Armenian",
"Arabic",
"Amharic",
};
void programDataHandler(audiodata *data, void *ctx)
{
QString audio;
if (data->ASCTy == 0)
audio = "DAB";
else if (data->ASCTy == 63)
audio = "DAB+";
else
audio = "Unknown";
QString language = "";
if ((data->language < 0x80) && (data->language >= 0))
language = dabLanguageCode[data->language & 0x7f];
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->programData(data->bitRate, audio, language, dabProgramType[data->programType & 0x1f]);
}
void programQualityHandler(int16_t frames, int16_t rs, int16_t aac, void *ctx)
{
DABDemodSink *sink = (DABDemodSink *)ctx;
sink->programQuality(frames, rs, aac);
}
void motDataHandler(std::string data, int a, void *ctx)
{
(void)data;
(void)ctx;
}
void DABDemodSink::systemData(bool sync, int16_t snr, int32_t freqOffset)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABSystemData *msg = DABDemod::MsgDABSystemData::create(sync, snr, freqOffset);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::ensembleName(const QString& name, int id)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABEnsembleName *msg = DABDemod::MsgDABEnsembleName::create(name, id);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::programName(const QString& name, int id)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABProgramName *msg = DABDemod::MsgDABProgramName::create(name, id);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::programData(int bitrate, const QString& audio, const QString& language, const QString& programType)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABProgramData *msg = DABDemod::MsgDABProgramData::create(bitrate, audio, language, programType);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::fibQuality(int16_t percent)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABFIBQuality *msg = DABDemod::MsgDABFIBQuality::create(percent);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::programQuality(int16_t frames, int16_t rs, int16_t aac)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABProgramQuality *msg = DABDemod::MsgDABProgramQuality::create(frames, rs, aac);
getMessageQueueToChannel()->push(msg);
}
}
void DABDemodSink::data(const QString& data)
{
if (getMessageQueueToChannel())
{
DABDemod::MsgDABData *msg = DABDemod::MsgDABData::create(data);
getMessageQueueToChannel()->push(msg);
}
}
static int16_t scale(int16_t sample, float factor)
{
int32_t prod = (int32_t)(((int32_t)sample) * factor);
prod = std::min(prod, 32767);
prod = std::max(prod, -32768);
return (int16_t)prod;
}
void DABDemodSink::audio(int16_t *buffer, int size, int samplerate, bool stereo)
{
(void)stereo;
(void)samplerate;
if (samplerate != m_dabAudioSampleRate)
{
applyDABAudioSampleRate(samplerate);
if (getMessageQueueToChannel())
{
DABDemod::MsgDABSampleRate *msg = DABDemod::MsgDABSampleRate::create(samplerate);
getMessageQueueToChannel()->push(msg);
}
}
// buffer is always 2 channels
for (int i = 0; i < size; i+=2)
{
Complex ci, ca;
if (!m_settings.m_audioMute)
{
ci.real(buffer[i]);
ci.imag(buffer[i+1]);
}
else
{
ci.real(0.0f);
ci.imag(0.0f);
}
if (m_audioInterpolatorDistance < 1.0f) // interpolate
{
while (!m_audioInterpolator.interpolate(&m_audioInterpolatorDistanceRemain, ci, &ca))
{
processOneAudioSample(ca);
m_audioInterpolatorDistanceRemain += m_audioInterpolatorDistance;
}
}
else // decimate
{
if (m_audioInterpolator.decimate(&m_audioInterpolatorDistanceRemain, ci, &ca))
{
processOneAudioSample(ca);
m_audioInterpolatorDistanceRemain += m_audioInterpolatorDistance;
}
}
}
}
void DABDemodSink::reset()
{
dabReset(m_dab);
}
void DABDemodSink::resetService()
{
dabReset_msc(m_dab);
}
void DABDemodSink::processOneAudioSample(Complex &ci)
{
float factor = m_settings.m_volume / 5.0f; // Should this be 5 or 10? 5 allows some positive gain
qint16 l = scale(ci.real(), factor);
qint16 r = scale(ci.real(), factor);
m_audioBuffer[m_audioBufferFill].l = l;
m_audioBuffer[m_audioBufferFill].r = r;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
{
uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("DABDemodSink::audio: %u/%u audio samples written", res, m_audioBufferFill);
m_audioFifo.clear();
}
m_audioBufferFill = 0;
}
m_demodBuffer[m_demodBufferFill++] = l; // FIXME: What about right channel?
if (m_demodBufferFill >= m_demodBuffer.size())
{
QList<DataFifo*> *dataFifos = MainCore::instance()->getDataPipes().getFifos(m_channel, "demod");
if (dataFifos)
{
QList<DataFifo*>::iterator it = dataFifos->begin();
for (; it != dataFifos->end(); ++it) {
(*it)->write((quint8*) &m_demodBuffer[0], m_demodBuffer.size() * sizeof(qint16));
}
}
m_demodBufferFill = 0;
}
}
DABDemodSink::DABDemodSink(DABDemod *packetDemod) :
m_dabDemod(packetDemod),
m_audioSampleRate(48000),
m_dabAudioSampleRate(10000), // Unused value to begin with
m_channelSampleRate(DABDEMOD_CHANNEL_SAMPLE_RATE),
m_channelFrequencyOffset(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_audioFifo(48000)
{
m_audioBuffer.resize(1<<14);
m_audioBufferFill = 0;
m_magsq = 0.0;
m_demodBuffer.resize(1<<12);
m_demodBufferFill = 0;
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
int mode = 1; // Latest DAB spec only has mode 1
m_dab = dabInit(&m_device,
mode,
syncHandler,
systemDataHandler,
ensembleNameHandler,
programNameHandler,
fibQualityHandler,
audioHandler,
dataHandler,
byteHandler,
programDataHandler,
programQualityHandler,
motDataHandler,
nullptr,
nullptr,
this);
dabStartProcessing(m_dab);
}
DABDemodSink::~DABDemodSink()
{
dabExit(m_dab);
}
void DABDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
Complex ci;
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
c *= m_nco.nextIQ();
if (m_interpolatorDistance == 1.0f)
{
processOneSample(c);
}
else if (m_interpolatorDistance < 1.0f) // interpolate
{
while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
else // decimate
{
if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci))
{
processOneSample(ci);
m_interpolatorDistanceRemain += m_interpolatorDistance;
}
}
}
}
void DABDemodSink::processOneSample(Complex &ci)
{
// Calculate average and peak levels for level meter
double magsqRaw = ci.real()*ci.real() + ci.imag()*ci.imag();
Real magsq = (Real)(magsqRaw / (SDR_RX_SCALED*SDR_RX_SCALED));
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
// Send sample to DAB library
std::complex<float> c;
c.real(ci.real()/SDR_RX_SCALED);
c.imag(ci.imag()/SDR_RX_SCALED);
m_device.putSample(c);
}
void DABDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "DABDemodSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
if ((m_channelFrequencyOffset != channelFrequencyOffset) ||
(m_channelSampleRate != channelSampleRate) || force)
{
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
if ((m_channelSampleRate != channelSampleRate) || force)
{
m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) channelSampleRate / (Real) DABDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void DABDemodSink::applySettings(const DABDemodSettings& settings, bool force)
{
qDebug() << "DABDemodSink::applySettings:"
<< " force: " << force;
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
{
m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) DABDEMOD_CHANNEL_SAMPLE_RATE;
m_interpolatorDistanceRemain = m_interpolatorDistance;
}
if ((settings.m_program != m_settings.m_program) || force)
{
if (!settings.m_program.isEmpty())
{
QByteArray ba = settings.m_program.toLatin1();
const char *program = ba.data();
if (!is_audioService (m_dab, program))
qWarning() << settings.m_program << " is not an audio service";
else
{
dataforAudioService(m_dab, program, &m_ad, 0);
if (!m_ad.defined)
qWarning() << settings.m_program << " audio data is not defined";
else
{
dabReset_msc(m_dab);
set_audioChannel(m_dab, &m_ad);
}
}
}
}
m_settings = settings;
}
// Called when audio device sample rate changes
void DABDemodSink::applyAudioSampleRate(int sampleRate)
{
if (sampleRate < 0)
{
qWarning("DABDemodSink::applyAudioSampleRate: invalid sample rate: %d", sampleRate);
return;
}
qDebug("DABDemodSink::applyAudioSampleRate: m_audioSampleRate: %d m_dabAudioSampleRate: %d", sampleRate, m_dabAudioSampleRate);
m_audioInterpolator.create(16, m_dabAudioSampleRate, m_dabAudioSampleRate/2.2f);
m_audioInterpolatorDistanceRemain = 0;
m_audioInterpolatorDistance = (Real) m_dabAudioSampleRate / (Real) sampleRate;
m_audioFifo.setSize(sampleRate);
m_audioSampleRate = sampleRate;
}
// Called when DAB audio sample rate changes
void DABDemodSink::applyDABAudioSampleRate(int sampleRate)
{
qDebug("DABDemodSink::applyDABAudioSampleRate: m_audioSampleRate: %d new m_dabAudioSampleRate: %d", m_audioSampleRate, sampleRate);
m_audioInterpolator.create(16, sampleRate, sampleRate/2.2f);
m_audioInterpolatorDistanceRemain = 0;
m_audioInterpolatorDistance = (Real) sampleRate / (Real) m_audioSampleRate;
m_dabAudioSampleRate = sampleRate;
}

View File

@ -0,0 +1,147 @@
///////////////////////////////////////////////////////////////////////////////////
// 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_DABDEMODSINK_H
#define INCLUDE_DABDEMODSINK_H
#include <QVector>
#include "dsp/channelsamplesink.h"
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/movingaverage.h"
#include "util/messagequeue.h"
#include "audio/audiofifo.h"
#include "dabdemodsettings.h"
#include "dabdemoddevice.h"
#include <vector>
#include <dab-api.h>
#define DABDEMOD_CHANNEL_SAMPLE_RATE 2048000
class ChannelAPI;
class DABDemod;
class DABDemodSink : public ChannelSampleSink {
public:
DABDemodSink(DABDemod *packetDemod);
~DABDemodSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const DABDemodSettings& settings, bool force = false);
void applyAudioSampleRate(int sampleRate);
void applyDABAudioSampleRate(int sampleRate);
int getAudioSampleRate() const { return m_audioSampleRate; }
AudioFifo *getAudioFifo() { return &m_audioFifo; }
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
double getMagSq() const { return m_magsq; }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_magsqCount > 0)
{
m_magsq = m_magsqSum / m_magsqCount;
m_magSqLevelStore.m_magsq = m_magsq;
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
}
avg = m_magSqLevelStore.m_magsq;
peak = m_magSqLevelStore.m_magsqPeak;
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
}
void reset();
void resetService();
// Callbacks
void systemData(bool sync, int16_t snr, int32_t freqOffset);
void ensembleName(const QString& name, int id);
void programName(const QString& name, int id);
void programData(int bitrate, const QString& audio, const QString& language, const QString& programType);
void audio(int16_t *buffer, int size, int samplerate, bool stereo);
void programQuality(int16_t frames, int16_t rs, int16_t aac);
void fibQuality(int16_t percent);
void data(const QString& data);
private:
struct MagSqLevelsStore
{
MagSqLevelsStore() :
m_magsq(1e-12),
m_magsqPeak(1e-12)
{}
double m_magsq;
double m_magsqPeak;
};
DABDemod *m_dabDemod;
DABDemodSettings m_settings;
ChannelAPI *m_channel;
int m_audioSampleRate; // Output device sample rate
int m_dabAudioSampleRate;
int m_channelSampleRate;
int m_channelFrequencyOffset;
void *m_dab;
DABDemodDevice m_device;
audiodata m_ad;
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MessageQueue *m_messageQueueToChannel;
MovingAverageUtil<Real, double, 16> m_movingAverage;
Interpolator m_audioInterpolator;
Real m_audioInterpolatorDistance;
Real m_audioInterpolatorDistanceRemain;
AudioVector m_audioBuffer;
AudioFifo m_audioFifo;
uint32_t m_audioBufferFill;
QVector<qint16> m_demodBuffer;
int m_demodBufferFill;
void processOneSample(Complex &ci);
void processOneAudioSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
};
#endif // INCLUDE_DABDEMODSINK_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 "dabdemod.h"
#include "dabdemodwebapiadapter.h"
DABDemodWebAPIAdapter::DABDemodWebAPIAdapter()
{}
DABDemodWebAPIAdapter::~DABDemodWebAPIAdapter()
{}
int DABDemodWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDabDemodSettings(new SWGSDRangel::SWGDABDemodSettings());
response.getDabDemodSettings()->init();
DABDemod::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int DABDemodWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force;
(void) errorMessage;
DABDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DABDEMOD_WEBAPIADAPTER_H
#define INCLUDE_DABDEMOD_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "dabdemodsettings.h"
/**
* Standalone API adapter only for the settings
*/
class DABDemodWebAPIAdapter : public ChannelWebAPIAdapter {
public:
DABDemodWebAPIAdapter();
virtual ~DABDemodWebAPIAdapter();
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:
DABDemodSettings m_settings;
};
#endif // INCLUDE_DABDEMOD_WEBAPIADAPTER_H

View File

@ -0,0 +1,80 @@
<h1>DAB demodulator plugin</h1>
<h2>Introduction</h2>
This plugin can be used to demodulate DAB and DAB+ radio.
The DAB demodulator uses a sample rate of 2.048MHz.
<h2>Interface</h2>
![DAB Demodulator plugin GUI](../../../doc/img/DABDemod_plugin.png)
<h3>1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>2: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>3: Audio mute</h3>
Left click on this button to toggle audio mute for this channel.
If you right click on it it will open a dialog to select the audio output device. See [audio management documentation](../../../sdrgui/audio.md) for details.
<h3>4: RF level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>5: Audio volume</h3>
This is the volume of the audio signal from 0.0 (mute) to 10.0 (maximum). It can be varied continuously in 0.1 steps using the dial button. A value of 5.0 corresponds to a gain of 0dB.
<h3>8: Channel</h3>
Displays a list of DAB Band III channels and frequencies. Selecting an item will set the Device center frequency accordingly.
If the center frequency is set manually, this box will be updated to reflect the corresponding channel, or left blank if there is not a channel that corresponds to the current centre frequency.
<h3>5: RF Bandwidth</h3>
This specifies the bandwidth of a filter that is applied to the input signal before decimation or interpolation to limit the received signal's bandwidth. This should typically be 1.537 MHz.
<h3>6: Find</h3>
Enter a regular expression used to filter the program table.
<h3>7: Clear programs</h3>
Clear all programs in the table.
<h3>Program Table</h3>
The program table shows programs that have been detected within a tuned ensemble. Double clicking on a program will cause
the demodulator to set the corresponding center frequency and then to play audio for the program.
<h3>Current Program</h3>
The current program area display information about the currently playing program, including:
* Program name.
* Ensemble name.
* Program type (E.g. News / Pop).
* Language.
* Audio (DAB or DAB+).
* Bitrate in kbps.
* Audio sample rate (in kSa/s). If this does not match the sample rate of the selected audio device, it will be resampled to match.
* Data broadcast with the program (E.g. song name).
<h3>Statistics</h3>
The statitics areas displays statistics generated by the demodulator that may give an indiciation of the quality of the received signal.
If you are hearing dropouts in audio, try adjusting your antenna in order to improve the reported SNR.
<h2>Attribution</h2>
The DAB demodulator used DAB library by Jvan Katwijk.

View File

@ -25,6 +25,7 @@
const QStringList DemodAnalyzerSettings::m_channelTypes = { const QStringList DemodAnalyzerSettings::m_channelTypes = {
QStringLiteral("AMDemod"), QStringLiteral("AMDemod"),
QStringLiteral("AMMod"), QStringLiteral("AMMod"),
QStringLiteral("DABDemod"),
QStringLiteral("DSDDemod"), QStringLiteral("DSDDemod"),
QStringLiteral("NFMDemod"), QStringLiteral("NFMDemod"),
QStringLiteral("NFMMod"), QStringLiteral("NFMMod"),
@ -39,6 +40,7 @@ const QStringList DemodAnalyzerSettings::m_channelTypes = {
const QStringList DemodAnalyzerSettings::m_channelURIs = { const QStringList DemodAnalyzerSettings::m_channelURIs = {
QStringLiteral("sdrangel.channel.amdemod"), QStringLiteral("sdrangel.channel.amdemod"),
QStringLiteral("sdrangel.channeltx.modam"), QStringLiteral("sdrangel.channeltx.modam"),
QStringLiteral("sdrangel.channel.dabdemod"),
QStringLiteral("sdrangel.channel.dsddemod"), QStringLiteral("sdrangel.channel.dsddemod"),
QStringLiteral("sdrangel.channel.nfmdemod"), QStringLiteral("sdrangel.channel.nfmdemod"),
QStringLiteral("sdrangel.channeltx.modnfm"), QStringLiteral("sdrangel.channeltx.modnfm"),

View File

@ -3871,6 +3871,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setDatvModSettings(new SWGSDRangel::SWGDATVModSettings()); channelSettings->setDatvModSettings(new SWGSDRangel::SWGDATVModSettings());
channelSettings->getDatvModSettings()->fromJsonObject(settingsJsonObject); channelSettings->getDatvModSettings()->fromJsonObject(settingsJsonObject);
} }
else if (channelSettingsKey == "DABDemodSettings")
{
channelSettings->setDabDemodSettings(new SWGSDRangel::SWGDABDemodSettings());
channelSettings->getDabDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DSDDemodSettings") else if (channelSettingsKey == "DSDDemodSettings")
{ {
channelSettings->setDsdDemodSettings(new SWGSDRangel::SWGDSDDemodSettings()); channelSettings->setDsdDemodSettings(new SWGSDRangel::SWGDSDDemodSettings());
@ -4595,6 +4600,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setAtvModSettings(nullptr); channelSettings.setAtvModSettings(nullptr);
channelSettings.setBfmDemodSettings(nullptr); channelSettings.setBfmDemodSettings(nullptr);
channelSettings.setDatvModSettings(nullptr); channelSettings.setDatvModSettings(nullptr);
channelSettings.setDabDemodSettings(nullptr);
channelSettings.setDsdDemodSettings(nullptr); channelSettings.setDsdDemodSettings(nullptr);
channelSettings.setIeee802154ModSettings(nullptr); channelSettings.setIeee802154ModSettings(nullptr);
channelSettings.setNfmDemodSettings(nullptr); channelSettings.setNfmDemodSettings(nullptr);

View File

@ -37,6 +37,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channel.modchirpchat", "ChirpChatModSettings"}, {"sdrangel.channel.modchirpchat", "ChirpChatModSettings"},
{"sdrangel.channel.demodatv", "ATVDemodSettings"}, {"sdrangel.channel.demodatv", "ATVDemodSettings"},
{"sdrangel.channel.demoddatv", "DATVDemodSettings"}, {"sdrangel.channel.demoddatv", "DATVDemodSettings"},
{"sdrangel.channel.dabdemod", "DABDemodSettings"},
{"sdrangel.channel.dsddemod", "DSDDemodSettings"}, {"sdrangel.channel.dsddemod", "DSDDemodSettings"},
{"sdrangel.channel.filesink", "FileSinkSettings"}, {"sdrangel.channel.filesink", "FileSinkSettings"},
{"sdrangel.channeltx.filesource", "FileSourceSettings"}, {"sdrangel.channeltx.filesource", "FileSourceSettings"},
@ -132,6 +133,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"ChirpChatMod", "ChirpChatModSettings"}, {"ChirpChatMod", "ChirpChatModSettings"},
{"DATVDemod", "DATVDemodSettings"}, {"DATVDemod", "DATVDemodSettings"},
{"DATVMod", "DATVModSettings"}, {"DATVMod", "DATVModSettings"},
{"DABDemod", "DABDemodSettings"},
{"DSDDemod", "DSDDemodSettings"}, {"DSDDemod", "DSDDemodSettings"},
{"FileSink", "FileSinkSettings"}, {"FileSink", "FileSinkSettings"},
{"FileSource", "FileSourceSettings"}, {"FileSource", "FileSourceSettings"},

View File

@ -43,6 +43,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVMod.yaml#/DATVModSettings" $ref: "http://swgserver:8081/api/swagger/include/DATVMod.yaml#/DATVModSettings"
DATVDemodSettings: DATVDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodSettings" $ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DABDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DABDemod.yaml#/DABDemodSettings"
DSDDemodSettings: DSDDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodSettings" $ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodSettings"
FileSinkSettings: FileSinkSettings:

View File

@ -0,0 +1,36 @@
DABDemodSettings:
description: DABDemod
properties:
inputFrequencyOffset:
type: integer
format: int64
rfBandwidth:
type: number
format: float
program:
type: string
volume:
type: number
format: float
audioMute:
type: integer
audioDeviceName:
type: string
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

View File

@ -62,6 +62,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_datv_mod_settings_isSet = false; m_datv_mod_settings_isSet = false;
datv_demod_settings = nullptr; datv_demod_settings = nullptr;
m_datv_demod_settings_isSet = false; m_datv_demod_settings_isSet = false;
dab_demod_settings = nullptr;
m_dab_demod_settings_isSet = false;
dsd_demod_settings = nullptr; dsd_demod_settings = nullptr;
m_dsd_demod_settings_isSet = false; m_dsd_demod_settings_isSet = false;
file_sink_settings = nullptr; file_sink_settings = nullptr;
@ -154,6 +156,8 @@ SWGChannelSettings::init() {
m_datv_mod_settings_isSet = false; m_datv_mod_settings_isSet = false;
datv_demod_settings = new SWGDATVDemodSettings(); datv_demod_settings = new SWGDATVDemodSettings();
m_datv_demod_settings_isSet = false; m_datv_demod_settings_isSet = false;
dab_demod_settings = new SWGDABDemodSettings();
m_dab_demod_settings_isSet = false;
dsd_demod_settings = new SWGDSDDemodSettings(); dsd_demod_settings = new SWGDSDDemodSettings();
m_dsd_demod_settings_isSet = false; m_dsd_demod_settings_isSet = false;
file_sink_settings = new SWGFileSinkSettings(); file_sink_settings = new SWGFileSinkSettings();
@ -253,6 +257,9 @@ SWGChannelSettings::cleanup() {
if(datv_demod_settings != nullptr) { if(datv_demod_settings != nullptr) {
delete datv_demod_settings; delete datv_demod_settings;
} }
if(dab_demod_settings != nullptr) {
delete dab_demod_settings;
}
if(dsd_demod_settings != nullptr) { if(dsd_demod_settings != nullptr) {
delete dsd_demod_settings; delete dsd_demod_settings;
} }
@ -375,6 +382,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&datv_demod_settings, pJson["DATVDemodSettings"], "SWGDATVDemodSettings", "SWGDATVDemodSettings"); ::SWGSDRangel::setValue(&datv_demod_settings, pJson["DATVDemodSettings"], "SWGDATVDemodSettings", "SWGDATVDemodSettings");
::SWGSDRangel::setValue(&dab_demod_settings, pJson["DABDemodSettings"], "SWGDABDemodSettings", "SWGDABDemodSettings");
::SWGSDRangel::setValue(&dsd_demod_settings, pJson["DSDDemodSettings"], "SWGDSDDemodSettings", "SWGDSDDemodSettings"); ::SWGSDRangel::setValue(&dsd_demod_settings, pJson["DSDDemodSettings"], "SWGDSDDemodSettings", "SWGDSDDemodSettings");
::SWGSDRangel::setValue(&file_sink_settings, pJson["FileSinkSettings"], "SWGFileSinkSettings", "SWGFileSinkSettings"); ::SWGSDRangel::setValue(&file_sink_settings, pJson["FileSinkSettings"], "SWGFileSinkSettings", "SWGFileSinkSettings");
@ -492,6 +501,9 @@ SWGChannelSettings::asJsonObject() {
if((datv_demod_settings != nullptr) && (datv_demod_settings->isSet())){ if((datv_demod_settings != nullptr) && (datv_demod_settings->isSet())){
toJsonValue(QString("DATVDemodSettings"), datv_demod_settings, obj, QString("SWGDATVDemodSettings")); toJsonValue(QString("DATVDemodSettings"), datv_demod_settings, obj, QString("SWGDATVDemodSettings"));
} }
if((dab_demod_settings != nullptr) && (dab_demod_settings->isSet())){
toJsonValue(QString("DABDemodSettings"), dab_demod_settings, obj, QString("SWGDABDemodSettings"));
}
if((dsd_demod_settings != nullptr) && (dsd_demod_settings->isSet())){ if((dsd_demod_settings != nullptr) && (dsd_demod_settings->isSet())){
toJsonValue(QString("DSDDemodSettings"), dsd_demod_settings, obj, QString("SWGDSDDemodSettings")); toJsonValue(QString("DSDDemodSettings"), dsd_demod_settings, obj, QString("SWGDSDDemodSettings"));
} }
@ -741,6 +753,16 @@ SWGChannelSettings::setDatvDemodSettings(SWGDATVDemodSettings* datv_demod_settin
this->m_datv_demod_settings_isSet = true; this->m_datv_demod_settings_isSet = true;
} }
SWGDABDemodSettings*
SWGChannelSettings::getDabDemodSettings() {
return dab_demod_settings;
}
void
SWGChannelSettings::setDabDemodSettings(SWGDABDemodSettings* dab_demod_settings) {
this->dab_demod_settings = dab_demod_settings;
this->m_dab_demod_settings_isSet = true;
}
SWGDSDDemodSettings* SWGDSDDemodSettings*
SWGChannelSettings::getDsdDemodSettings() { SWGChannelSettings::getDsdDemodSettings() {
return dsd_demod_settings; return dsd_demod_settings;
@ -1047,6 +1069,9 @@ SWGChannelSettings::isSet(){
if(datv_demod_settings && datv_demod_settings->isSet()){ if(datv_demod_settings && datv_demod_settings->isSet()){
isObjectUpdated = true; break; isObjectUpdated = true; break;
} }
if(dab_demod_settings && dab_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(dsd_demod_settings && dsd_demod_settings->isSet()){ if(dsd_demod_settings && dsd_demod_settings->isSet()){
isObjectUpdated = true; break; isObjectUpdated = true; break;
} }

View File

@ -33,6 +33,7 @@
#include "SWGChannelAnalyzerSettings.h" #include "SWGChannelAnalyzerSettings.h"
#include "SWGChirpChatDemodSettings.h" #include "SWGChirpChatDemodSettings.h"
#include "SWGChirpChatModSettings.h" #include "SWGChirpChatModSettings.h"
#include "SWGDABDemodSettings.h"
#include "SWGDATVDemodSettings.h" #include "SWGDATVDemodSettings.h"
#include "SWGDATVModSettings.h" #include "SWGDATVModSettings.h"
#include "SWGDSDDemodSettings.h" #include "SWGDSDDemodSettings.h"
@ -131,6 +132,9 @@ public:
SWGDATVDemodSettings* getDatvDemodSettings(); SWGDATVDemodSettings* getDatvDemodSettings();
void setDatvDemodSettings(SWGDATVDemodSettings* datv_demod_settings); void setDatvDemodSettings(SWGDATVDemodSettings* datv_demod_settings);
SWGDABDemodSettings* getDabDemodSettings();
void setDabDemodSettings(SWGDABDemodSettings* dab_demod_settings);
SWGDSDDemodSettings* getDsdDemodSettings(); SWGDSDDemodSettings* getDsdDemodSettings();
void setDsdDemodSettings(SWGDSDDemodSettings* dsd_demod_settings); void setDsdDemodSettings(SWGDSDDemodSettings* dsd_demod_settings);
@ -261,6 +265,9 @@ private:
SWGDATVDemodSettings* datv_demod_settings; SWGDATVDemodSettings* datv_demod_settings;
bool m_datv_demod_settings_isSet; bool m_datv_demod_settings_isSet;
SWGDABDemodSettings* dab_demod_settings;
bool m_dab_demod_settings_isSet;
SWGDSDDemodSettings* dsd_demod_settings; SWGDSDDemodSettings* dsd_demod_settings;
bool m_dsd_demod_settings_isSet; bool m_dsd_demod_settings_isSet;

View File

@ -0,0 +1,415 @@
/**
* 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 "SWGDABDemodSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDABDemodSettings::SWGDABDemodSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGDABDemodSettings::SWGDABDemodSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
program = nullptr;
m_program_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
audio_device_name = nullptr;
m_audio_device_name_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;
}
SWGDABDemodSettings::~SWGDABDemodSettings() {
this->cleanup();
}
void
SWGDABDemodSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
rf_bandwidth = 0.0f;
m_rf_bandwidth_isSet = false;
program = new QString("");
m_program_isSet = false;
volume = 0.0f;
m_volume_isSet = false;
audio_mute = 0;
m_audio_mute_isSet = false;
audio_device_name = new QString("");
m_audio_device_name_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
SWGDABDemodSettings::cleanup() {
if(program != nullptr) {
delete program;
}
if(audio_device_name != nullptr) {
delete audio_device_name;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGDABDemodSettings*
SWGDABDemodSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDABDemodSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", "");
::SWGSDRangel::setValue(&program, pJson["program"], "QString", "QString");
::SWGSDRangel::setValue(&volume, pJson["volume"], "float", "");
::SWGSDRangel::setValue(&audio_mute, pJson["audioMute"], "qint32", "");
::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString");
::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
SWGDABDemodSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDABDemodSettings::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(program != nullptr && *program != QString("")){
toJsonValue(QString("program"), program, obj, QString("QString"));
}
if(m_volume_isSet){
obj->insert("volume", QJsonValue(volume));
}
if(m_audio_mute_isSet){
obj->insert("audioMute", QJsonValue(audio_mute));
}
if(audio_device_name != nullptr && *audio_device_name != QString("")){
toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString"));
}
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
SWGDABDemodSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGDABDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
float
SWGDABDemodSettings::getRfBandwidth() {
return rf_bandwidth;
}
void
SWGDABDemodSettings::setRfBandwidth(float rf_bandwidth) {
this->rf_bandwidth = rf_bandwidth;
this->m_rf_bandwidth_isSet = true;
}
QString*
SWGDABDemodSettings::getProgram() {
return program;
}
void
SWGDABDemodSettings::setProgram(QString* program) {
this->program = program;
this->m_program_isSet = true;
}
float
SWGDABDemodSettings::getVolume() {
return volume;
}
void
SWGDABDemodSettings::setVolume(float volume) {
this->volume = volume;
this->m_volume_isSet = true;
}
qint32
SWGDABDemodSettings::getAudioMute() {
return audio_mute;
}
void
SWGDABDemodSettings::setAudioMute(qint32 audio_mute) {
this->audio_mute = audio_mute;
this->m_audio_mute_isSet = true;
}
QString*
SWGDABDemodSettings::getAudioDeviceName() {
return audio_device_name;
}
void
SWGDABDemodSettings::setAudioDeviceName(QString* audio_device_name) {
this->audio_device_name = audio_device_name;
this->m_audio_device_name_isSet = true;
}
qint32
SWGDABDemodSettings::getRgbColor() {
return rgb_color;
}
void
SWGDABDemodSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGDABDemodSettings::getTitle() {
return title;
}
void
SWGDABDemodSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGDABDemodSettings::getStreamIndex() {
return stream_index;
}
void
SWGDABDemodSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGDABDemodSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGDABDemodSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGDABDemodSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGDABDemodSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGDABDemodSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGDABDemodSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGDABDemodSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGDABDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGDABDemodSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGDABDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
bool
SWGDABDemodSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_rf_bandwidth_isSet){
isObjectUpdated = true; break;
}
if(program && *program != QString("")){
isObjectUpdated = true; break;
}
if(m_volume_isSet){
isObjectUpdated = true; break;
}
if(m_audio_mute_isSet){
isObjectUpdated = true; break;
}
if(audio_device_name && *audio_device_name != QString("")){
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,137 @@
/**
* 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.
*/
/*
* SWGDABDemodSettings.h
*
* DABDemod
*/
#ifndef SWGDABDemodSettings_H_
#define SWGDABDemodSettings_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDABDemodSettings: public SWGObject {
public:
SWGDABDemodSettings();
SWGDABDemodSettings(QString* json);
virtual ~SWGDABDemodSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDABDemodSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
float getRfBandwidth();
void setRfBandwidth(float rf_bandwidth);
QString* getProgram();
void setProgram(QString* program);
float getVolume();
void setVolume(float volume);
qint32 getAudioMute();
void setAudioMute(qint32 audio_mute);
QString* getAudioDeviceName();
void setAudioDeviceName(QString* audio_device_name);
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;
QString* program;
bool m_program_isSet;
float volume;
bool m_volume_isSet;
qint32 audio_mute;
bool m_audio_mute_isSet;
QString* audio_device_name;
bool m_audio_device_name_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 /* SWGDABDemodSettings_H_ */

View File

@ -72,6 +72,7 @@
#include "SWGChirpChatModSettings.h" #include "SWGChirpChatModSettings.h"
#include "SWGCommand.h" #include "SWGCommand.h"
#include "SWGComplex.h" #include "SWGComplex.h"
#include "SWGDABDemodSettings.h"
#include "SWGDATVDemodSettings.h" #include "SWGDATVDemodSettings.h"
#include "SWGDATVModReport.h" #include "SWGDATVModReport.h"
#include "SWGDATVModSettings.h" #include "SWGDATVModSettings.h"
@ -436,6 +437,9 @@ namespace SWGSDRangel {
if(QString("SWGComplex").compare(type) == 0) { if(QString("SWGComplex").compare(type) == 0) {
return new SWGComplex(); return new SWGComplex();
} }
if(QString("SWGDABDemodSettings").compare(type) == 0) {
return new SWGDABDemodSettings();
}
if(QString("SWGDATVDemodSettings").compare(type) == 0) { if(QString("SWGDATVDemodSettings").compare(type) == 0) {
return new SWGDATVDemodSettings(); return new SWGDATVDemodSettings();
} }