diff --git a/plugins/channeltx/CMakeLists.txt b/plugins/channeltx/CMakeLists.txt index f13dfeb8d..07bb75b9f 100644 --- a/plugins/channeltx/CMakeLists.txt +++ b/plugins/channeltx/CMakeLists.txt @@ -61,4 +61,5 @@ endif() if (ENABLE_CHANNELTX_MODFREEDV AND CODEC2_FOUND) add_subdirectory(modfreedv) + add_subdirectory(modm17) endif(CODEC2_FOUND) diff --git a/plugins/channeltx/modm17/CMakeLists.txt b/plugins/channeltx/modm17/CMakeLists.txt new file mode 100644 index 000000000..bf11c0d73 --- /dev/null +++ b/plugins/channeltx/modm17/CMakeLists.txt @@ -0,0 +1,64 @@ +project(modm17) + +set(modm17_SOURCES + m17mod.cpp + m17modbaseband.cpp + m17modsource.cpp + m17modplugin.cpp + m17modsettings.cpp + m17modwebapiadapter.cpp +) + +set(modm17_HEADERS + m17mod.h + m17modbaseband.h + m17modsource.h + m17modplugin.h + m17modsettings.h + m17modwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(modm17_SOURCES + ${modm17_SOURCES} + m17modgui.cpp + m17modgui.ui + ) + set(modm17_HEADERS + ${modm17_HEADERS} + m17modgui.h + ) + + set(TARGET_NAME modm17) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME modm17srv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${modm17_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + swagger +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/channeltx/modm17/m17mod.cpp b/plugins/channeltx/modm17/m17mod.cpp new file mode 100644 index 000000000..c79ddd73f --- /dev/null +++ b/plugins/channeltx/modm17/m17mod.cpp @@ -0,0 +1,766 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" +#include "SWGChannelReport.h" +#include "SWGNFMModReport.h" + +#include +#include +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "device/deviceapi.h" +#include "feature/feature.h" +#include "util/db.h" +#include "maincore.h" + +#include "m17modbaseband.h" +#include "m17mod.h" + +MESSAGE_CLASS_DEFINITION(M17Mod::MsgConfigureM17Mod, Message) +MESSAGE_CLASS_DEFINITION(M17Mod::MsgConfigureFileSourceName, Message) +MESSAGE_CLASS_DEFINITION(M17Mod::MsgConfigureFileSourceSeek, Message) +MESSAGE_CLASS_DEFINITION(M17Mod::MsgConfigureFileSourceStreamTiming, Message) +MESSAGE_CLASS_DEFINITION(M17Mod::MsgReportFileSourceStreamData, Message) +MESSAGE_CLASS_DEFINITION(M17Mod::MsgReportFileSourceStreamTiming, Message) + +const char* const M17Mod::m_channelIdURI = "sdrangel.channeltx.modm17"; +const char* const M17Mod::m_channelId = "M17Mod"; + +M17Mod::M17Mod(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource), + m_deviceAPI(deviceAPI), + m_settingsMutex(QMutex::Recursive), + m_fileSize(0), + m_recordLength(0), + m_sampleRate(48000) +{ + setObjectName(m_channelId); + + m_thread = new QThread(this); + m_basebandSource = new M17ModBaseband(); + m_basebandSource->setInputFileStream(&m_ifstream); + m_basebandSource->setChannel(this); + m_basebandSource->moveToThread(m_thread); + + applySettings(m_settings, true); + + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSourceAPI(this); + + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &M17Mod::networkManagerFinished + ); +} + +M17Mod::~M17Mod() +{ + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &M17Mod::networkManagerFinished + ); + delete m_networkManager; + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + delete m_basebandSource; + delete m_thread; +} + +void M17Mod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSource(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + +void M17Mod::start() +{ + qDebug("M17Mod::start"); + m_basebandSource->reset(); + m_thread->start(); +} + +void M17Mod::stop() +{ + qDebug("M17Mod::stop"); + m_thread->exit(); + m_thread->wait(); +} + +void M17Mod::pull(SampleVector::iterator& begin, unsigned int nbSamples) +{ + m_basebandSource->pull(begin, nbSamples); +} + +void M17Mod::setCenterFrequency(qint64 frequency) +{ + M17ModSettings settings = m_settings; + settings.m_inputFrequencyOffset = frequency; + applySettings(settings, false); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureM17Mod *msgToGUI = MsgConfigureM17Mod::create(settings, false); + m_guiMessageQueue->push(msgToGUI); + } +} + +bool M17Mod::handleMessage(const Message& cmd) +{ + if (MsgConfigureM17Mod::match(cmd)) + { + MsgConfigureM17Mod& cfg = (MsgConfigureM17Mod&) cmd; + qDebug() << "M17Mod::handleMessage: MsgConfigureM17Mod"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgConfigureFileSourceName::match(cmd)) + { + MsgConfigureFileSourceName& conf = (MsgConfigureFileSourceName&) cmd; + m_fileName = conf.getFileName(); + openFileStream(); + qDebug() << "M17Mod::handleMessage: MsgConfigureFileSourceName:" + << " m_fileName: " << m_fileName; + return true; + } + else if (MsgConfigureFileSourceSeek::match(cmd)) + { + MsgConfigureFileSourceSeek& conf = (MsgConfigureFileSourceSeek&) cmd; + int seekPercentage = conf.getPercentage(); + seekFileStream(seekPercentage); + qDebug() << "M17Mod::handleMessage: MsgConfigureFileSourceSeek:" + << " seekPercentage: " << seekPercentage; + + return true; + } + else if (MsgConfigureFileSourceStreamTiming::match(cmd)) + { + std::size_t samplesCount; + + if (m_ifstream.eof()) { + samplesCount = m_fileSize / sizeof(Real); + } else { + samplesCount = m_ifstream.tellg() / sizeof(Real); + } + + MsgReportFileSourceStreamTiming *report; + report = MsgReportFileSourceStreamTiming::create(samplesCount); + getMessageQueueToGUI()->push(report); + + return true; + } + else if (MsgConfigureFileSourceName::match(cmd)) + { + MsgConfigureFileSourceName& conf = (MsgConfigureFileSourceName&) cmd; + m_fileName = conf.getFileName(); + openFileStream(); + qDebug() << "M17Mod::handleMessage: MsgConfigureFileSourceName:" + << " m_fileName: " << m_fileName; + return true; + } + else if (MsgConfigureFileSourceSeek::match(cmd)) + { + MsgConfigureFileSourceSeek& conf = (MsgConfigureFileSourceSeek&) cmd; + int seekPercentage = conf.getPercentage(); + seekFileStream(seekPercentage); + qDebug() << "M17Mod::handleMessage: MsgConfigureFileSourceSeek:" + << " seekPercentage: " << seekPercentage; + + return true; + } + else if (MsgConfigureFileSourceStreamTiming::match(cmd)) + { + std::size_t samplesCount; + + if (m_ifstream.eof()) { + samplesCount = m_fileSize / sizeof(Real); + } else { + samplesCount = m_ifstream.tellg() / sizeof(Real); + } + + MsgReportFileSourceStreamTiming *report; + report = MsgReportFileSourceStreamTiming::create(samplesCount); + getMessageQueueToGUI()->push(report); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + // Forward to the source + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "M17Mod::handleMessage: DSPSignalNotification"; + m_basebandSource->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push(new DSPSignalNotification(notif)); + } + + return true; + } + else if (MainCore::MsgChannelDemodQuery::match(cmd)) + { + qDebug() << "M17Mod::handleMessage: MsgChannelDemodQuery"; + sendSampleRateToDemodAnalyzer(); + + return true; + } + else + { + return false; + } +} + +void M17Mod::openFileStream() +{ + if (m_ifstream.is_open()) { + m_ifstream.close(); + } + + m_ifstream.open(m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate); + m_fileSize = m_ifstream.tellg(); + m_ifstream.seekg(0,std::ios_base::beg); + + m_sampleRate = 48000; // fixed rate + m_recordLength = m_fileSize / (sizeof(Real) * m_sampleRate); + + qDebug() << "M17Mod::openFileStream: " << m_fileName.toStdString().c_str() + << " fileSize: " << m_fileSize << "bytes" + << " length: " << m_recordLength << " seconds"; + + MsgReportFileSourceStreamData *report; + report = MsgReportFileSourceStreamData::create(m_sampleRate, m_recordLength); + getMessageQueueToGUI()->push(report); +} + +void M17Mod::seekFileStream(int seekPercentage) +{ + QMutexLocker mutexLocker(&m_settingsMutex); + + if (m_ifstream.is_open()) + { + int seekPoint = ((m_recordLength * seekPercentage) / 100) * m_sampleRate; + seekPoint *= sizeof(Real); + m_ifstream.clear(); + m_ifstream.seekg(seekPoint, std::ios::beg); + } +} + +void M17Mod::applySettings(const M17ModSettings& settings, bool force) +{ + qDebug() << "M17Mod::applySettings:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " m_fmDeviation: " << settings.m_fmDeviation + << " m_volumeFactor: " << settings.m_volumeFactor + << " m_toneFrequency: " << settings.m_toneFrequency + << " m_channelMute: " << settings.m_channelMute + << " m_playLoop: " << settings.m_playLoop + << " m_modAFInput " << settings.m_modAFInput + << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIAddress: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex + << " force: " << force; + + QList reverseAPIKeys; + + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + reverseAPIKeys.append("inputFrequencyOffset"); + } + if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) { + reverseAPIKeys.append("fmDeviation"); + } + if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) { + reverseAPIKeys.append("volumeFactor"); + } + if ((settings.m_channelMute != m_settings.m_channelMute) || force) { + reverseAPIKeys.append("channelMute"); + } + if ((settings.m_playLoop != m_settings.m_playLoop) || force) { + reverseAPIKeys.append("playLoop"); + } + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) { + reverseAPIKeys.append("modAFInput"); + } + if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { + reverseAPIKeys.append("rfBandwidth"); + } + if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); + } + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); + } + if ((settings.m_feedbackAudioDeviceName != m_settings.m_feedbackAudioDeviceName) || force) { + reverseAPIKeys.append("feedbackAudioDeviceName"); + } + + if (m_settings.m_streamIndex != settings.m_streamIndex) + { + if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only + { + m_deviceAPI->removeChannelSourceAPI(this); + m_deviceAPI->removeChannelSource(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSource(this, settings.m_streamIndex); + m_deviceAPI->addChannelSourceAPI(this); + } + + reverseAPIKeys.append("streamIndex"); + } + + M17ModBaseband::MsgConfigureM17ModBaseband *msg = M17ModBaseband::MsgConfigureM17ModBaseband::create(settings, force); + m_basebandSource->getInputMessageQueue()->push(msg); + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + QList pipes; + MainCore::instance()->getMessagePipes().getMessagePipes(this, "settings", pipes); + + if (pipes.size() > 0) { + sendChannelSettings(pipes, reverseAPIKeys, settings, force); + } + + m_settings = settings; +} + +QByteArray M17Mod::serialize() const +{ + return m_settings.serialize(); +} + +bool M17Mod::deserialize(const QByteArray& data) +{ + bool success = true; + + if (!m_settings.deserialize(data)) + { + m_settings.resetToDefaults(); + success = false; + } + + MsgConfigureM17Mod *msg = MsgConfigureM17Mod::create(m_settings, true); + m_inputMessageQueue.push(msg); + + return success; +} + +void M17Mod::sendSampleRateToDemodAnalyzer() +{ + QList pipes; + MainCore::instance()->getMessagePipes().getMessagePipes(this, "reportdemod", pipes); + + if (pipes.size() > 0) + { + for (const auto& pipe : pipes) + { + MessageQueue* messageQueue = qobject_cast(pipe->m_element); + MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create( + this, + getAudioSampleRate() + ); + messageQueue->push(msg); + } + } +} + +int M17Mod::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setM17ModSettings(new SWGSDRangel::SWGM17ModSettings()); + response.getM17ModSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + return 200; +} + +int M17Mod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + +int M17Mod::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + M17ModSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigureM17Mod *msg = MsgConfigureM17Mod::create(settings, force); + m_inputMessageQueue.push(msg); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureM17Mod *msgToGUI = MsgConfigureM17Mod::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +void M17Mod::webapiUpdateChannelSettings( + M17ModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + if (channelSettingsKeys.contains("channelMute")) { + settings.m_channelMute = response.getM17ModSettings()->getChannelMute() != 0; + } + if (channelSettingsKeys.contains("fmDeviation")) { + settings.m_fmDeviation = response.getM17ModSettings()->getFmDeviation(); + } + if (channelSettingsKeys.contains("inputFrequencyOffset")) { + settings.m_inputFrequencyOffset = response.getM17ModSettings()->getInputFrequencyOffset(); + } + if (channelSettingsKeys.contains("modAFInput")) { + settings.m_modAFInput = (M17ModSettings::M17ModInputAF) response.getM17ModSettings()->getModAfInput(); + } + if (channelSettingsKeys.contains("playLoop")) { + settings.m_playLoop = response.getM17ModSettings()->getPlayLoop() != 0; + } + if (channelSettingsKeys.contains("rfBandwidth")) { + settings.m_rfBandwidth = response.getM17ModSettings()->getRfBandwidth(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getM17ModSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getM17ModSettings()->getTitle(); + } + if (channelSettingsKeys.contains("toneFrequency")) { + settings.m_toneFrequency = response.getM17ModSettings()->getToneFrequency(); + } + if (channelSettingsKeys.contains("volumeFactor")) { + settings.m_volumeFactor = response.getM17ModSettings()->getVolumeFactor(); + } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.getM17ModSettings()->getStreamIndex(); + } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getM17ModSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getM17ModSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getM17ModSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getM17ModSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getNfmModSettings()->getReverseApiChannelIndex(); + } + if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getM17ModSettings()->getChannelMarker()); + } + if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(channelSettingsKeys, response.getM17ModSettings()->getRollupState()); + } +} + +int M17Mod::webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setM17ModReport(new SWGSDRangel::SWGM17ModReport()); + response.getM17ModReport()->init(); + webapiFormatChannelReport(response); + return 200; +} + +void M17Mod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const M17ModSettings& settings) +{ + response.getM17ModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0); + response.getM17ModSettings()->setFmDeviation(settings.m_fmDeviation); + response.getM17ModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getM17ModSettings()->setModAfInput((int) settings.m_modAFInput); + response.getM17ModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0); + response.getM17ModSettings()->setRfBandwidth(settings.m_rfBandwidth); + response.getM17ModSettings()->setRgbColor(settings.m_rgbColor); + + if (response.getM17ModSettings()->getTitle()) { + *response.getM17ModSettings()->getTitle() = settings.m_title; + } else { + response.getM17ModSettings()->setTitle(new QString(settings.m_title)); + } + + response.getM17ModSettings()->setToneFrequency(settings.m_toneFrequency); + response.getM17ModSettings()->setVolumeFactor(settings.m_volumeFactor); + + if (response.getM17ModSettings()->getAudioDeviceName()) { + *response.getM17ModSettings()->getAudioDeviceName() = settings.m_audioDeviceName; + } else { + response.getM17ModSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + + response.getM17ModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getM17ModSettings()->getReverseApiAddress()) { + *response.getM17ModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getM17ModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getM17ModSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getM17ModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getM17ModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + + if (settings.m_channelMarker) + { + if (response.getM17ModSettings()->getChannelMarker()) + { + settings.m_channelMarker->formatTo(response.getM17ModSettings()->getChannelMarker()); + } + else + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + response.getM17ModSettings()->setChannelMarker(swgChannelMarker); + } + } + + if (settings.m_rollupState) + { + if (response.getM17ModSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.getM17ModSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.getM17ModSettings()->setRollupState(swgRollupState); + } + } +} + +void M17Mod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +{ + response.getM17ModReport()->setChannelPowerDb(CalcDb::dbPower(getMagSq())); + response.getM17ModReport()->setAudioSampleRate(m_basebandSource->getAudioSampleRate()); + response.getM17ModReport()->setChannelSampleRate(m_basebandSource->getChannelSampleRate()); +} + +void M17Mod::webapiReverseSendSettings(QList& channelSettingsKeys, const M17ModSettings& 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 M17Mod::sendChannelSettings( + const QList& pipes, + QList& channelSettingsKeys, + const M17ModSettings& settings, + bool force) +{ + for (const auto& pipe : pipes) + { + MessageQueue *messageQueue = qobject_cast(pipe->m_element); + + if (messageQueue) + { + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force); + MainCore::MsgChannelSettings *msg = MainCore::MsgChannelSettings::create( + this, + channelSettingsKeys, + swgChannelSettings, + force + ); + messageQueue->push(msg); + } + } +} + +void M17Mod::webapiFormatChannelSettings( + QList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const M17ModSettings& settings, + bool force +) +{ + swgChannelSettings->setDirection(1); // single source (Tx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString(m_channelId)); + swgChannelSettings->setM17ModSettings(new SWGSDRangel::SWGM17ModSettings()); + SWGSDRangel::SWGM17ModSettings *swgM17ModSettings = swgChannelSettings->getM17ModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("channelMute") || force) { + swgM17ModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgM17ModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgM17ModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgM17ModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgM17ModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgM17ModSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgM17ModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgM17ModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgM17ModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgM17ModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgM17ModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgM17ModSettings->setStreamIndex(settings.m_streamIndex); + } + + if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + swgM17ModSettings->setChannelMarker(swgChannelMarker); + } + + if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force)) + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + swgM17ModSettings->setRollupState(swgRollupState); + } +} + +void M17Mod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "M17Mod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("M17Mod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +double M17Mod::getMagSq() const +{ + return m_basebandSource->getMagSq(); +} + +void M17Mod::setLevelMeter(QObject *levelMeter) +{ + connect(m_basebandSource, SIGNAL(levelChanged(qreal, qreal, int)), levelMeter, SLOT(levelChanged(qreal, qreal, int))); +} + +uint32_t M17Mod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSinkStreams(); +} + +int M17Mod::getAudioSampleRate() const +{ + return m_basebandSource->getAudioSampleRate(); +} + +int M17Mod::getFeedbackAudioSampleRate() const +{ + return m_basebandSource->getFeedbackAudioSampleRate(); +} diff --git a/plugins/channeltx/modm17/m17mod.h b/plugins/channeltx/modm17/m17mod.h new file mode 100644 index 000000000..0f68030e3 --- /dev/null +++ b/plugins/channeltx/modm17/m17mod.h @@ -0,0 +1,288 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELTX_MODM17_M17MOD_H_ +#define PLUGINS_CHANNELTX_MODM17_M17MOD_H_ + +#include +#include +#include + +#include +#include + +#include "dsp/basebandsamplesource.h" +#include "channel/channelapi.h" +#include "util/message.h" + +#include "m17modsettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class DeviceAPI; +class M17ModBaseband; +class ObjectPipe; + +class M17Mod : public BasebandSampleSource, public ChannelAPI { +public: + class MsgConfigureM17Mod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const M17ModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureM17Mod* create(const M17ModSettings& settings, bool force) + { + return new MsgConfigureM17Mod(settings, force); + } + + private: + M17ModSettings m_settings; + bool m_force; + + MsgConfigureM17Mod(const M17ModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgConfigureFileSourceName : public Message + { + MESSAGE_CLASS_DECLARATION + + public: + const QString& getFileName() const { return m_fileName; } + + static MsgConfigureFileSourceName* create(const QString& fileName) + { + return new MsgConfigureFileSourceName(fileName); + } + + private: + QString m_fileName; + + MsgConfigureFileSourceName(const QString& fileName) : + Message(), + m_fileName(fileName) + { } + }; + + class MsgConfigureFileSourceSeek : public Message + { + MESSAGE_CLASS_DECLARATION + + public: + int getPercentage() const { return m_seekPercentage; } + + static MsgConfigureFileSourceSeek* create(int seekPercentage) + { + return new MsgConfigureFileSourceSeek(seekPercentage); + } + + protected: + int m_seekPercentage; //!< percentage of seek position from the beginning 0..100 + + MsgConfigureFileSourceSeek(int seekPercentage) : + Message(), + m_seekPercentage(seekPercentage) + { } + }; + + class MsgConfigureFileSourceStreamTiming : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgConfigureFileSourceStreamTiming* create() + { + return new MsgConfigureFileSourceStreamTiming(); + } + + private: + + MsgConfigureFileSourceStreamTiming() : + Message() + { } + }; + + class MsgReportFileSourceStreamTiming : public Message + { + MESSAGE_CLASS_DECLARATION + + public: + std::size_t getSamplesCount() const { return m_samplesCount; } + + static MsgReportFileSourceStreamTiming* create(std::size_t samplesCount) + { + return new MsgReportFileSourceStreamTiming(samplesCount); + } + + protected: + std::size_t m_samplesCount; + + MsgReportFileSourceStreamTiming(std::size_t samplesCount) : + Message(), + m_samplesCount(samplesCount) + { } + }; + + class MsgReportFileSourceStreamData : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getSampleRate() const { return m_sampleRate; } + quint32 getRecordLength() const { return m_recordLength; } + + static MsgReportFileSourceStreamData* create(int sampleRate, + quint32 recordLength) + { + return new MsgReportFileSourceStreamData(sampleRate, recordLength); + } + + protected: + int m_sampleRate; + quint32 m_recordLength; + + MsgReportFileSourceStreamData(int sampleRate, + quint32 recordLength) : + Message(), + m_sampleRate(sampleRate), + m_recordLength(recordLength) + { } + }; + + //================================================================= + + M17Mod(DeviceAPI *deviceAPI); + virtual ~M17Mod(); + virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } + + virtual void start(); + virtual void stop(); + virtual void pull(SampleVector::iterator& begin, unsigned int nbSamples); + virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); } + virtual QString getSourceName() { return objectName(); } + + virtual void getIdentifier(QString& id) { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual void getTitle(QString& title) { title = m_settings.m_title; } + virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; } + virtual void setCenterFrequency(qint64 frequency); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 1; } + virtual int getNbSourceStreams() const { return 0; } + + virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const + { + (void) streamIndex; + (void) sinkElseSource; + return m_settings.m_inputFrequencyOffset; + } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage); + + static void webapiFormatChannelSettings( + SWGSDRangel::SWGChannelSettings& response, + const M17ModSettings& settings); + + static void webapiUpdateChannelSettings( + M17ModSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + double getMagSq() const; + void setLevelMeter(QObject *levelMeter); + uint32_t getNumberOfDeviceStreams() const; + int getAudioSampleRate() const; + int getFeedbackAudioSampleRate() const; + + static const char* const m_channelIdURI; + static const char* const m_channelId; + +private: + enum RateState { + RSInitialFill, + RSRunning + }; + + DeviceAPI* m_deviceAPI; + QThread *m_thread; + M17ModBaseband* m_basebandSource; + M17ModSettings m_settings; + + SampleVector m_sampleBuffer; + QMutex m_settingsMutex; + + std::ifstream m_ifstream; + QString m_fileName; + quint64 m_fileSize; //!< raw file size (bytes) + quint32 m_recordLength; //!< record length in seconds computed from file size + int m_sampleRate; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + virtual bool handleMessage(const Message& cmd); + void applySettings(const M17ModSettings& settings, bool force = false); + void sendSampleRateToDemodAnalyzer(); + void openFileStream(); + void seekFileStream(int seekPercentage); + void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const M17ModSettings& settings, bool force); + void sendChannelSettings( + const QList& pipes, + QList& channelSettingsKeys, + const M17ModSettings& settings, + bool force + ); + void webapiFormatChannelSettings( + QList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const M17ModSettings& settings, + bool force + ); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + + +#endif /* PLUGINS_CHANNELTX_MODM17_M17MOD_H_ */ diff --git a/plugins/channeltx/modm17/m17modbaseband.cpp b/plugins/channeltx/modm17/m17modbaseband.cpp new file mode 100644 index 000000000..685900f17 --- /dev/null +++ b/plugins/channeltx/modm17/m17modbaseband.cpp @@ -0,0 +1,232 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/upchannelizer.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" + +#include "m17modbaseband.h" + +MESSAGE_CLASS_DEFINITION(M17ModBaseband::MsgConfigureM17ModBaseband, Message) + +M17ModBaseband::M17ModBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(48000)); + m_channelizer = new UpChannelizer(&m_source); + + qDebug("M17ModBaseband::M17ModBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSourceFifo::dataRead, + this, + &M17ModBaseband::handleData, + Qt::QueuedConnection + ); + + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_source.getFeedbackAudioFifo(), getInputMessageQueue()); + m_source.applyFeedbackAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +M17ModBaseband::~M17ModBaseband() +{ + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_source.getFeedbackAudioFifo()); + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSource(m_source.getAudioFifo()); + delete m_channelizer; +} + +void M17ModBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void M17ModBaseband::setChannel(ChannelAPI *channel) +{ + m_source.setChannel(channel); +} + +void M17ModBaseband::pull(const SampleVector::iterator& begin, unsigned int nbSamples) +{ + unsigned int part1Begin, part1End, part2Begin, part2End; + m_sampleFifo.read(nbSamples, part1Begin, part1End, part2Begin, part2End); + SampleVector& data = m_sampleFifo.getData(); + + if (part1Begin != part1End) + { + std::copy( + data.begin() + part1Begin, + data.begin() + part1End, + begin + ); + } + + unsigned int shift = part1End - part1Begin; + + if (part2Begin != part2End) + { + std::copy( + data.begin() + part2Begin, + data.begin() + part2End, + begin + shift + ); + } +} + +void M17ModBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + SampleVector& data = m_sampleFifo.getData(); + unsigned int ipart1begin; + unsigned int ipart1end; + unsigned int ipart2begin; + unsigned int ipart2end; + qreal rmsLevel, peakLevel; + int numSamples; + + unsigned int remainder = m_sampleFifo.remainder(); + + while ((remainder > 0) && (m_inputMessageQueue.size() == 0)) + { + m_sampleFifo.write(remainder, ipart1begin, ipart1end, ipart2begin, ipart2end); + + if (ipart1begin != ipart1end) { // first part of FIFO data + processFifo(data, ipart1begin, ipart1end); + } + + if (ipart2begin != ipart2end) { // second part of FIFO data (used when block wraps around) + processFifo(data, ipart2begin, ipart2end); + } + + remainder = m_sampleFifo.remainder(); + } + + m_source.getLevels(rmsLevel, peakLevel, numSamples); + emit levelChanged(rmsLevel, peakLevel, numSamples); +} + +void M17ModBaseband::processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd) +{ + m_channelizer->prefetch(iEnd - iBegin); + m_channelizer->pull(data.begin() + iBegin, iEnd - iBegin); +} + +void M17ModBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool M17ModBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureM17ModBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureM17ModBaseband& cfg = (MsgConfigureM17ModBaseband&) cmd; + qDebug() << "M17ModBaseband::handleMessage: MsgConfigureM17ModBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "M17ModBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change + + return true; + } + else + { + return false; + } +} + +void M17ModBaseband::applySettings(const M17ModSettings& settings, bool force) +{ + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + m_channelizer->setChannelization(m_source.getAudioSampleRate(), settings.m_inputFrequencyOffset); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change + + } + + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + + if (getAudioSampleRate() != audioSampleRate) + { + m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(audioSampleRate); + } + } + + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + + if (settings.m_modAFInput == M17ModSettings::M17ModInputAudio) { + audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + } else { + audioDeviceManager->removeAudioSource(getAudioFifo()); + } + } + + if ((settings.m_feedbackAudioDeviceName != m_settings.m_feedbackAudioDeviceName) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName); + audioDeviceManager->removeAudioSink(getFeedbackAudioFifo()); + audioDeviceManager->addAudioSink(getFeedbackAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + + if (getFeedbackAudioSampleRate() != audioSampleRate) { + m_source.applyFeedbackAudioSampleRate(audioSampleRate); + } + } + + m_source.applySettings(settings, force); + + m_settings = settings; +} + +int M17ModBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} diff --git a/plugins/channeltx/modm17/m17modbaseband.h b/plugins/channeltx/modm17/m17modbaseband.h new file mode 100644 index 000000000..43724cf29 --- /dev/null +++ b/plugins/channeltx/modm17/m17modbaseband.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_M17MODBASEBAND_H +#define INCLUDE_M17MODBASEBAND_H + +#include +#include + +#include "dsp/samplesourcefifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "m17modsource.h" + +class UpChannelizer; +class ChannelAPI; + +class M17ModBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureM17ModBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const M17ModSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureM17ModBaseband* create(const M17ModSettings& settings, bool force) + { + return new MsgConfigureM17ModBaseband(settings, force); + } + + private: + M17ModSettings m_settings; + bool m_force; + + MsgConfigureM17ModBaseband(const M17ModSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + M17ModBaseband(); + ~M17ModBaseband(); + void reset(); + void pull(const SampleVector::iterator& begin, unsigned int nbSamples); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + double getMagSq() const { return m_source.getMagSq(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } + int getChannelSampleRate() const; + void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } + AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } + AudioFifo *getFeedbackAudioFifo() { return m_source.getFeedbackAudioFifo(); } + void setChannel(ChannelAPI *channel); + +signals: + /** + * Level changed + * \param rmsLevel RMS level in range 0.0 - 1.0 + * \param peakLevel Peak level in range 0.0 - 1.0 + * \param numSamples Number of audio samples analyzed + */ + void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples); + +private: + SampleSourceFifo m_sampleFifo; + UpChannelizer *m_channelizer; + M17ModSource m_source; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + M17ModSettings m_settings; + QMutex m_mutex; + + void processFifo(SampleVector& data, unsigned int iBegin, unsigned int iEnd); + bool handleMessage(const Message& cmd); + void applySettings(const M17ModSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + + +#endif // INCLUDE_M17MODBASEBAND_H diff --git a/plugins/channeltx/modm17/m17modgui.cpp b/plugins/channeltx/modm17/m17modgui.cpp new file mode 100644 index 000000000..7c7dea247 --- /dev/null +++ b/plugins/channeltx/modm17/m17modgui.cpp @@ -0,0 +1,599 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include "device/deviceuiset.h" +#include "plugin/pluginapi.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "gui/crightclickenabler.h" +#include "gui/audioselectdialog.h" +#include "gui/basicchannelsettingsdialog.h" +#include "gui/devicestreamselectiondialog.h" +#include "maincore.h" + +#include "ui_m17modgui.h" +#include "m17modgui.h" + + +M17ModGUI* M17ModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx) +{ + M17ModGUI* gui = new M17ModGUI(pluginAPI, deviceUISet, channelTx); + return gui; +} + +void M17ModGUI::destroy() +{ + delete this; +} + +void M17ModGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray M17ModGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool M17ModGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applySettings(true); + return true; + } else { + resetToDefaults(); + return false; + } +} + +void M17ModGUI::resizeEvent(QResizeEvent* size) +{ + int maxWidth = getRollupContents()->maximumWidth(); + int minHeight = getRollupContents()->minimumHeight() + getAdditionalHeight(); + resize(width() < maxWidth ? width() : maxWidth, minHeight); + size->accept(); +} + +bool M17ModGUI::handleMessage(const Message& message) +{ + if (M17Mod::MsgReportFileSourceStreamData::match(message)) + { + m_recordSampleRate = ((M17Mod::MsgReportFileSourceStreamData&)message).getSampleRate(); + m_recordLength = ((M17Mod::MsgReportFileSourceStreamData&)message).getRecordLength(); + m_samplesCount = 0; + updateWithStreamData(); + return true; + } + else if (M17Mod::MsgReportFileSourceStreamTiming::match(message)) + { + m_samplesCount = ((M17Mod::MsgReportFileSourceStreamTiming&)message).getSamplesCount(); + updateWithStreamTime(); + return true; + } + else if (M17Mod::MsgConfigureM17Mod::match(message)) + { + const M17Mod::MsgConfigureM17Mod& cfg = (M17Mod::MsgConfigureM17Mod&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + m_channelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); + displaySettings(); + blockApplySettings(false); + return true; + } + else if (DSPSignalNotification::match(message)) + { + const DSPSignalNotification& notif = (const DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } + else + { + return false; + } +} + +void M17ModGUI::channelMarkerChangedByCursor() +{ + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySettings(); +} + +void M17ModGUI::handleSourceMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void M17ModGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); + applySettings(); +} + +void M17ModGUI::on_rfBW_valueChanged(int value) +{ + ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_rfBandwidth = value * 100.0; + m_channelMarker.setBandwidth(m_settings.m_rfBandwidth); + + applySettings(); +} + +void M17ModGUI::on_fmDev_valueChanged(int value) +{ + ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(value / 10.0, 0, 'f', 1)); + m_settings.m_fmDeviation = value * 200.0; + applySettings(); +} + +void M17ModGUI::on_volume_valueChanged(int value) +{ + ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_volumeFactor = value / 10.0; + applySettings(); +} + +void M17ModGUI::on_toneFrequency_valueChanged(int value) +{ + ui->toneFrequencyText->setText(QString("%1k").arg(value / 100.0, 0, 'f', 2)); + m_settings.m_toneFrequency = value * 10.0; + applySettings(); +} + +void M17ModGUI::on_channelMute_toggled(bool checked) +{ + m_settings.m_channelMute = checked; + applySettings(); +} + +void M17ModGUI::on_playLoop_toggled(bool checked) +{ + m_settings.m_playLoop = checked; + applySettings(); +} + +void M17ModGUI::on_play_toggled(bool checked) +{ + ui->tone->setEnabled(!checked); // release other source inputs + ui->mic->setEnabled(!checked); + m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputFile : M17ModSettings::M17ModInputNone; + applySettings(); + ui->navTimeSlider->setEnabled(!checked); + m_enableNavTime = !checked; +} + +void M17ModGUI::on_tone_toggled(bool checked) +{ + ui->play->setEnabled(!checked); // release other source inputs + ui->mic->setEnabled(!checked); + m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputTone : M17ModSettings::M17ModInputNone; + applySettings(); +} + +void M17ModGUI::on_mic_toggled(bool checked) +{ + ui->play->setEnabled(!checked); // release other source inputs + ui->tone->setEnabled(!checked); // release other source inputs + m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputAudio : M17ModSettings::M17ModInputNone; + applySettings(); +} + +void M17ModGUI::on_feedbackEnable_toggled(bool checked) +{ + m_settings.m_feedbackAudioEnable = checked; + applySettings(); +} + +void M17ModGUI::on_feedbackVolume_valueChanged(int value) +{ + ui->feedbackVolumeText->setText(QString("%1").arg(value / 100.0, 0, 'f', 2)); + m_settings.m_feedbackVolumeFactor = value / 100.0; + applySettings(); +} + +void M17ModGUI::on_navTimeSlider_valueChanged(int value) +{ + if (m_enableNavTime && ((value >= 0) && (value <= 100))) + { + int t_sec = (m_recordLength * value) / 100; + QTime t(0, 0, 0, 0); + t = t.addSecs(t_sec); + + M17Mod::MsgConfigureFileSourceSeek* message = M17Mod::MsgConfigureFileSourceSeek::create(value); + m_m17Mod->getInputMessageQueue()->push(message); + } +} + +void M17ModGUI::on_showFileDialog_clicked(bool checked) +{ + (void) checked; + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open raw audio file"), ".", tr("Raw audio Files (*.raw)"), 0, QFileDialog::DontUseNativeDialog); + + if (fileName != "") + { + m_fileName = fileName; + ui->recordFileText->setText(m_fileName); + ui->play->setEnabled(true); + configureFileName(); + } +} + +void M17ModGUI::configureFileName() +{ + qDebug() << "M17ModGUI::configureFileName: " << m_fileName.toStdString().c_str(); + M17Mod::MsgConfigureFileSourceName* message = M17Mod::MsgConfigureFileSourceName::create(m_fileName); + m_m17Mod->getInputMessageQueue()->push(message); +} + +void M17ModGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + getRollupContents()->saveState(m_rollupState); + applySettings(); +} + +void M17ModGUI::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.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_m17Mod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + + 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); + setTitle(m_channelMarker.getTitle()); + setTitleColor(m_settings.m_rgbColor); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } + + applySettings(); + } + + resetContextMenuType(); +} + +M17ModGUI::M17ModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent) : + ChannelGUI(parent), + ui(new Ui::M17ModGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_deviceCenterFrequency(0), + m_basebandSampleRate(1), + m_doApplySettings(true), + m_recordLength(0), + m_recordSampleRate(48000), + m_samplesCount(0), + m_audioSampleRate(-1), + m_feedbackAudioSampleRate(-1), + m_tickCount(0), + m_enableNavTime(false), + m_dcsCodeValidator(QRegExp("[0-7]{1,3}")) +{ + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channeltx/modm17/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_m17Mod = (M17Mod*) channelTx; + m_m17Mod->setMessageQueueToGUI(getInputMessageQueue()); + + connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); + + CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); + connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); + + CRightClickEnabler *feedbackRightClickEnabler = new CRightClickEnabler(ui->feedbackEnable); + connect(feedbackRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioFeedbackSelect())); + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(Qt::red); + m_channelMarker.setBandwidth(12500); + m_channelMarker.setCenterFrequency(0); + m_channelMarker.setTitle("M17 Modulator"); + m_channelMarker.setSourceOrSinkStream(false); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + m_deviceUISet->addChannelMarker(&m_channelMarker); + + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); + + ui->play->setEnabled(false); + ui->play->setChecked(false); + ui->tone->setChecked(false); + ui->mic->setChecked(false); + + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); + m_m17Mod->setLevelMeter(ui->volumeMeter); + + m_settings.setChannelMarker(&m_channelMarker); + m_settings.setRollupState(&m_rollupState); + + displaySettings(); + makeUIConnections(); + applySettings(); +} + +M17ModGUI::~M17ModGUI() +{ + delete ui; +} + +void M17ModGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void M17ModGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + M17Mod::MsgConfigureM17Mod *msg = M17Mod::MsgConfigureM17Mod::create(m_settings, force); + m_m17Mod->getInputMessageQueue()->push(msg); + } +} + +void M17ModGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + 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()); + setTitle(m_channelMarker.getTitle()); + updateIndexLabel(); + + blockApplySettings(true); + + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + + ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); + ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0); + + ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1)); + ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0); + + ui->volumeText->setText(QString("%1").arg(m_settings.m_volumeFactor, 0, 'f', 1)); + ui->volume->setValue(m_settings.m_volumeFactor * 10.0); + + ui->toneFrequencyText->setText(QString("%1k").arg(m_settings.m_toneFrequency / 1000.0, 0, 'f', 2)); + ui->toneFrequency->setValue(m_settings.m_toneFrequency / 10.0); + + ui->channelMute->setChecked(m_settings.m_channelMute); + ui->playLoop->setChecked(m_settings.m_playLoop); + + ui->tone->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputTone) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone)); + ui->mic->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputAudio) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone)); + ui->play->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputFile) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone)); + + ui->tone->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputTone); + ui->mic->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputAudio); + ui->play->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputFile); + + ui->feedbackEnable->setChecked(m_settings.m_feedbackAudioEnable); + ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); + ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); + + getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); + blockApplySettings(false); +} + +void M17ModGUI::leaveEvent(QEvent* event) +{ + m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); +} + +void M17ModGUI::enterEvent(QEvent* event) +{ + m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); +} + +void M17ModGUI::audioSelect() +{ + qDebug("M17ModGUI::audioSelect"); + AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName, true); // true for input + audioSelect.exec(); + + if (audioSelect.m_selected) + { + m_settings.m_audioDeviceName = audioSelect.m_audioDeviceName; + applySettings(); + } +} + +void M17ModGUI::audioFeedbackSelect() +{ + qDebug("M17ModGUI::audioFeedbackSelect"); + AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName, false); // false for output + audioSelect.exec(); + + if (audioSelect.m_selected) + { + m_settings.m_feedbackAudioDeviceName = audioSelect.m_audioDeviceName; + applySettings(); + } +} + +void M17ModGUI::tick() +{ + double powDb = CalcDb::dbPower(m_m17Mod->getMagSq()); + m_channelPowerDbAvg(powDb); + ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + + int audioSampleRate = m_m17Mod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + + int feedbackAudioSampleRate = m_m17Mod->getFeedbackAudioSampleRate(); + + if (feedbackAudioSampleRate != m_feedbackAudioSampleRate) + { + if (feedbackAudioSampleRate < 0) { + ui->feedbackEnable->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->feedbackEnable->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_feedbackAudioSampleRate = feedbackAudioSampleRate; + } + + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == M17ModSettings::M17ModInputFile)) + { + M17Mod::MsgConfigureFileSourceStreamTiming* message = M17Mod::MsgConfigureFileSourceStreamTiming::create(); + m_m17Mod->getInputMessageQueue()->push(message); + } +} + +void M17ModGUI::updateWithStreamData() +{ + QTime recordLength(0, 0, 0, 0); + recordLength = recordLength.addSecs(m_recordLength); + QString s_time = recordLength.toString("HH:mm:ss"); + ui->recordLengthText->setText(s_time); + updateWithStreamTime(); +} + +void M17ModGUI::updateWithStreamTime() +{ + int t_sec = 0; + int t_msec = 0; + + if (m_recordSampleRate > 0) + { + t_msec = ((m_samplesCount * 1000) / m_recordSampleRate) % 1000; + t_sec = m_samplesCount / m_recordSampleRate; + } + + QTime t(0, 0, 0, 0); + t = t.addSecs(t_sec); + t = t.addMSecs(t_msec); + QString s_timems = t.toString("HH:mm:ss.zzz"); + QString s_time = t.toString("HH:mm:ss"); + ui->relTimeText->setText(s_timems); + + if (!m_enableNavTime) + { + float posRatio = (float) t_sec / (float) m_recordLength; + ui->navTimeSlider->setValue((int) (posRatio * 100.0)); + } +} + +void M17ModGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &M17ModGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &M17ModGUI::on_rfBW_valueChanged); + QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &M17ModGUI::on_fmDev_valueChanged); + QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &M17ModGUI::on_toneFrequency_valueChanged); + QObject::connect(ui->volume, &QDial::valueChanged, this, &M17ModGUI::on_volume_valueChanged); + QObject::connect(ui->channelMute, &QToolButton::toggled, this, &M17ModGUI::on_channelMute_toggled); + QObject::connect(ui->tone, &ButtonSwitch::toggled, this, &M17ModGUI::on_tone_toggled); + QObject::connect(ui->mic, &ButtonSwitch::toggled, this, &M17ModGUI::on_mic_toggled); + QObject::connect(ui->play, &ButtonSwitch::toggled, this, &M17ModGUI::on_play_toggled); + QObject::connect(ui->playLoop, &ButtonSwitch::toggled, this, &M17ModGUI::on_playLoop_toggled); + QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &M17ModGUI::on_navTimeSlider_valueChanged); + QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &M17ModGUI::on_showFileDialog_clicked); + QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &M17ModGUI::on_feedbackEnable_toggled); + QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &M17ModGUI::on_feedbackVolume_valueChanged); +} + +void M17ModGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channeltx/modm17/m17modgui.h b/plugins/channeltx/modm17/m17modgui.h new file mode 100644 index 000000000..a32b8a4bf --- /dev/null +++ b/plugins/channeltx/modm17/m17modgui.h @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELTX_MODM17_M17MODGUI_H_ +#define PLUGINS_CHANNELTX_MODM17_M17MODGUI_H_ + +#include + +#include "channel/channelgui.h" +#include "dsp/channelmarker.h" +#include "util/movingaverage.h" +#include "util/messagequeue.h" +#include "settings/rollupstate.h" + +#include "m17mod.h" +#include "m17modsettings.h" + +class PluginAPI; +class DeviceUISet; +class BasebandSampleSource; + +namespace Ui { + class M17ModGUI; +} + +class M17ModGUI : public ChannelGUI { + Q_OBJECT + +public: + static M17ModGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } + +public slots: + void channelMarkerChangedByCursor(); + +protected: + void resizeEvent(QResizeEvent* size); + +private: + Ui::M17ModGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + RollupState m_rollupState; + M17ModSettings m_settings; + qint64 m_deviceCenterFrequency; + int m_basebandSampleRate; + bool m_doApplySettings; + + M17Mod* m_m17Mod; + MovingAverageUtil m_channelPowerDbAvg; + + QString m_fileName; + quint32 m_recordLength; + int m_recordSampleRate; + int m_samplesCount; + int m_audioSampleRate; + int m_feedbackAudioSampleRate; + std::size_t m_tickCount; + bool m_enableNavTime; + M17ModSettings::M17ModInputAF m_modAFInput; + MessageQueue m_inputMessageQueue; + QRegExpValidator m_dcsCodeValidator; + + explicit M17ModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx, QWidget* parent = nullptr); + virtual ~M17ModGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void displaySettings(); + void updateWithStreamData(); + void updateWithStreamTime(); + bool handleMessage(const Message& message); + void makeUIConnections(); + void updateAbsoluteCenterFrequency(); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + +private slots: + void handleSourceMessages(); + + void on_deltaFrequency_changed(qint64 value); + void on_channelSpacingApply_clicked(); + void on_rfBW_valueChanged(int value); + void on_fmDev_valueChanged(int value); + void on_toneFrequency_valueChanged(int value); + void on_volume_valueChanged(int value); + void on_channelMute_toggled(bool checked); + void on_tone_toggled(bool checked); + void on_mic_toggled(bool checked); + void on_play_toggled(bool checked); + + void on_playLoop_toggled(bool checked); + void on_navTimeSlider_valueChanged(int value); + void on_showFileDialog_clicked(bool checked); + + void on_feedbackEnable_toggled(bool checked); + void on_feedbackVolume_valueChanged(int value); + + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + + void configureFileName(); + void audioSelect(); + void audioFeedbackSelect(); + void tick(); +}; + +#endif /* PLUGINS_CHANNELTX_MODM17_M17MODGUI_H_ */ diff --git a/plugins/channeltx/modm17/m17modgui.ui b/plugins/channeltx/modm17/m17modgui.ui new file mode 100644 index 000000000..ff29be418 --- /dev/null +++ b/plugins/channeltx/modm17/m17modgui.ui @@ -0,0 +1,737 @@ + + + M17ModGUI + + + + 0 + 0 + 360 + 278 + + + + + 0 + 0 + + + + + 360 + 0 + + + + + 560 + 16777215 + + + + + Liberation Sans + 9 + + + + Qt::StrongFocus + + + M17 Modulator + + + + + 0 + 0 + 358 + 271 + + + + + 358 + 0 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + Channel power + + + Qt::RightToLeft + + + -100.0 dB + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Mute/Unmute channel + + + ... + + + + :/txon.png + :/txoff.png:/txon.png + + + true + + + + + + + + + + + RFBW + + + + + + + RF bandwidth (kHz) + + + 1 + + + 480 + + + 1 + + + 160 + + + Qt::Horizontal + + + + + + + + 35 + 0 + + + + 25.0k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Dev + + + + + + + + 0 + 0 + + + + FM peak deviation (kHz) + + + 200 + + + 1 + + + 50 + + + Qt::Horizontal + + + + + + + + 40 + 0 + + + + +20.0k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + Vol + + + + + + + + 24 + 24 + + + + Audio input gain + + + 50 + + + 1 + + + 10 + + + + + + + + 25 + 0 + + + + Audio input gain value + + + 1.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + Liberation Mono + 8 + + + + Level (% full range) top trace: average, bottom trace: instantaneous peak, tip: peak hold + + + + + + + + + + + Tone modulation (1 kHz) + + + ... + + + + :/carrier.png:/carrier.png + + + + + + + + 24 + 24 + + + + Tone frequency + + + 10 + + + 250 + + + 1 + + + 100 + + + + + + + + 36 + 0 + + + + Tone frequency (kHz) + + + 1.00k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Audio input + + + ... + + + + :/microphone.png:/microphone.png + + + true + + + + + + + Qt::Vertical + + + + + + + Left: enable / disable audio feedback - Right: select audio output device + + + ... + + + + :/sound_off.png + :/sound_on.png:/sound_off.png + + + true + + + + + + + + 24 + 24 + + + + Audio feedback volume + + + 0 + + + 100 + + + 1 + + + 50 + + + + + + + + 30 + 16777215 + + + + Audio feedback volume + + + 1.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + + + Qt::Horizontal + + + + + + + + + ... + + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Open record file (48 kHz 32 bit float LE mono) + + + + + + + :/preset-load.png:/preset-load.png + + + + + + + Play record file in a loop + + + ... + + + + :/playloop.png:/playloop.png + + + + + + + Record file play/pause + + + ... + + + + :/play.png + :/pause.png + :/play.png + :/play.png:/play.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + false + + + + 90 + 0 + + + + Record time from start + + + 00:00:00.000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + false + + + + 60 + 0 + + + + Total record time + + + 00:00:00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Record file time navigator + + + 100 + + + 1 + + + Qt::Horizontal + + + + + + + + + + + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + LevelMeterVU + QWidget +
gui/levelmeter.h
+ 1 +
+
+ + + + +
diff --git a/plugins/channeltx/modm17/m17modplugin.cpp b/plugins/channeltx/modm17/m17modplugin.cpp new file mode 100644 index 000000000..795f63e1d --- /dev/null +++ b/plugins/channeltx/modm17/m17modplugin.cpp @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "m17modgui.h" +#endif +#include "m17mod.h" +#include "m17modwebapiadapter.h" +#include "m17modplugin.h" + +const PluginDescriptor M17ModPlugin::m_pluginDescriptor = { + M17Mod::m_channelId, + QStringLiteral("M17 Modulator"), + QStringLiteral("7.4.0"), + QStringLiteral("(c) Edouard Griffiths, F4EXB"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +M17ModPlugin::M17ModPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(nullptr) +{ +} + +const PluginDescriptor& M17ModPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void M17ModPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register AM modulator + m_pluginAPI->registerTxChannel(M17Mod::m_channelIdURI, M17Mod::m_channelId, this); +} + +void M17ModPlugin::createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const +{ + if (bs || cs) + { + M17Mod *instance = new M17Mod(deviceAPI); + + if (bs) { + *bs = instance; + } + + if (cs) { + *cs = instance; + } + } +} + +#ifdef SERVER_MODE +ChannelGUI* M17ModPlugin::createTxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSource *txChannel) const +{ + (void) deviceUISet; + (void) txChannel; + return nullptr; +} +#else +ChannelGUI* M17ModPlugin::createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *txChannel) const +{ + return M17ModGUI::create(m_pluginAPI, deviceUISet, txChannel); +} +#endif + +ChannelWebAPIAdapter* M17ModPlugin::createChannelWebAPIAdapter() const +{ + return new M17ModWebAPIAdapter(); +} diff --git a/plugins/channeltx/modm17/m17modplugin.h b/plugins/channeltx/modm17/m17modplugin.h new file mode 100644 index 000000000..05b25bf42 --- /dev/null +++ b/plugins/channeltx/modm17/m17modplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_M17MODPLUGIN_H +#define INCLUDE_M17MODPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSource; + +class M17ModPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.channeltx.modm17") + +public: + explicit M17ModPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void createTxChannel(DeviceAPI *deviceAPI, BasebandSampleSource **bs, ChannelAPI **cs) const; + virtual ChannelGUI* createTxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSource *rxChannel) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_M17MODPLUGIN_H diff --git a/plugins/channeltx/modm17/m17modsettings.cpp b/plugins/channeltx/modm17/m17modsettings.cpp new file mode 100644 index 000000000..2eea4748d --- /dev/null +++ b/plugins/channeltx/modm17/m17modsettings.cpp @@ -0,0 +1,179 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "dsp/dspengine.h" +#include "dsp/ctcssfrequencies.h" +#include "util/simpleserializer.h" +#include "settings/serializable.h" +#include "m17modsettings.h" + +M17ModSettings::M17ModSettings() : + m_channelMarker(nullptr), + m_rollupState(nullptr) +{ + resetToDefaults(); +} + +void M17ModSettings::resetToDefaults() +{ + m_inputFrequencyOffset = 0; + m_rfBandwidth = 16000.0f; + m_fmDeviation = 10000.0f; //!< full deviation + m_toneFrequency = 1000.0f; + m_volumeFactor = 1.0f; + m_channelMute = false; + m_playLoop = false; + m_rgbColor = QColor(255, 0, 255).rgb(); + m_title = "M17 Modulator"; + m_modAFInput = M17ModInputAF::M17ModInputNone; + m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_feedbackAudioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_feedbackVolumeFactor = 0.5f; + m_feedbackAudioEnable = false; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; + m_hidden = false; +} + +QByteArray M17ModSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_inputFrequencyOffset); + s.writeReal(2, m_rfBandwidth); + s.writeReal(4, m_fmDeviation); + s.writeU32(5, m_rgbColor); + s.writeReal(6, m_toneFrequency); + s.writeReal(7, m_volumeFactor); + + if (m_channelMarker) { + s.writeBlob(11, m_channelMarker->serialize()); + } + + s.writeString(12, m_title); + s.writeS32(13, (int) m_modAFInput); + s.writeString(14, m_audioDeviceName); + s.writeBool(15, m_useReverseAPI); + s.writeString(16, m_reverseAPIAddress); + s.writeU32(17, m_reverseAPIPort); + s.writeU32(18, m_reverseAPIDeviceIndex); + s.writeU32(19, m_reverseAPIChannelIndex); + s.writeString(20, m_feedbackAudioDeviceName); + s.writeReal(21, m_feedbackVolumeFactor); + s.writeBool(22, m_feedbackAudioEnable); + s.writeS32(23, m_streamIndex); + + if (m_rollupState) { + s.writeBlob(27, m_rollupState->serialize()); + } + + s.writeS32(28, m_workspaceIndex); + s.writeBlob(29, m_geometryBytes); + s.writeBool(30, m_hidden); + + return s.final(); +} + +bool M17ModSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + qint32 tmp; + uint32_t utmp; + + d.readS32(1, &tmp, 0); + m_inputFrequencyOffset = tmp; + d.readReal(2, &m_rfBandwidth, 12500.0); + d.readReal(4, &m_fmDeviation, 10000.0); + d.readU32(5, &m_rgbColor); + d.readReal(6, &m_toneFrequency, 1000.0); + d.readReal(7, &m_volumeFactor, 1.0); + d.readBlob(8, &bytetmp); + + if (m_channelMarker) + { + d.readBlob(11, &bytetmp); + m_channelMarker->deserialize(bytetmp); + } + + d.readString(12, &m_title, "M17 Modulator"); + + d.readS32(13, &tmp, 0); + if ((tmp < 0) || (tmp > (int) M17ModInputAF::M17ModInputTone)) { + m_modAFInput = M17ModInputNone; + } else { + m_modAFInput = (M17ModInputAF) tmp; + } + + d.readString(14, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readBool(15, &m_useReverseAPI, false); + d.readString(16, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(17, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(18, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(19, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readString(20, &m_feedbackAudioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readReal(21, &m_feedbackVolumeFactor, 1.0); + d.readBool(22, &m_feedbackAudioEnable, false); + d.readS32(23, &m_streamIndex, 0); + d.readS32(25, &tmp, 0023); + + if (m_rollupState) + { + d.readBlob(27, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + + d.readS32(28, &m_workspaceIndex, 0); + d.readBlob(29, &m_geometryBytes); + d.readBool(30, &m_hidden, false); + + return true; + } + else + { + qDebug() << "NFMModSettings::deserialize: ERROR"; + resetToDefaults(); + return false; + } +} + diff --git a/plugins/channeltx/modm17/m17modsettings.h b/plugins/channeltx/modm17/m17modsettings.h new file mode 100644 index 000000000..d0509620e --- /dev/null +++ b/plugins/channeltx/modm17/m17modsettings.h @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELTX_MODM17_M17MODSETTINGS_H_ +#define PLUGINS_CHANNELTX_MODM17_M17MODSETTINGS_H_ + +#include + +#include "dsp/cwkeyersettings.h" + +class Serializable; + +struct M17ModSettings +{ + enum M17ModInputAF + { + M17ModInputNone, + M17ModInputFile, + M17ModInputAudio, + M17ModInputTone + }; + + qint64 m_inputFrequencyOffset; + float m_rfBandwidth; + float m_fmDeviation; + float m_toneFrequency; + float m_volumeFactor; + bool m_channelMute; + bool m_playLoop; + quint32 m_rgbColor; + QString m_title; + M17ModInputAF m_modAFInput; + QString m_audioDeviceName; //!< This is the audio device you get the audio samples from + QString m_feedbackAudioDeviceName; //!< This is the audio device you send the audio samples to for audio feedback + float m_feedbackVolumeFactor; + bool m_feedbackAudioEnable; + int m_streamIndex; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + int m_workspaceIndex; + QByteArray m_geometryBytes; + bool m_hidden; + + Serializable *m_channelMarker; + Serializable *m_rollupState; + + M17ModSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + + + +#endif /* PLUGINS_CHANNELTX_MODM17_M17MODSETTINGS_H_ */ diff --git a/plugins/channeltx/modm17/m17modsource.cpp b/plugins/channeltx/modm17/m17modsource.cpp new file mode 100644 index 000000000..b124fe143 --- /dev/null +++ b/plugins/channeltx/modm17/m17modsource.cpp @@ -0,0 +1,418 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/datafifo.h" +#include "util/messagequeue.h" +#include "maincore.h" + +#include "m17modsource.h" + +const int M17ModSource::m_levelNbSamples = 480; // every 10ms + +M17ModSource::M17ModSource() : + m_channelSampleRate(48000), + m_channelFrequencyOffset(0), + m_modPhasor(0.0f), + m_audioSampleRate(48000), + m_audioFifo(12000), + m_feedbackAudioFifo(48000), + m_levelCalcCount(0), + m_peakLevel(0.0f), + m_levelSum(0.0f), + m_ifstream(nullptr), + m_mutex(QMutex::Recursive) +{ + m_audioFifo.setLabel("M17ModSource.m_audioFifo"); + m_feedbackAudioFifo.setLabel("M17ModSource.m_feedbackAudioFifo"); + m_audioBuffer.resize(24000); + m_audioBufferFill = 0; + m_audioReadBuffer.resize(24000); + m_audioReadBufferFill = 0; + + m_feedbackAudioBuffer.resize(1<<14); + m_feedbackAudioBufferFill = 0; + + m_demodBuffer.resize(1<<12); + m_demodBufferFill = 0; + + m_magsq = 0.0; + + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +M17ModSource::~M17ModSource() +{ +} + +void M17ModSource::pull(SampleVector::iterator begin, unsigned int nbSamples) +{ + std::for_each( + begin, + begin + nbSamples, + [this](Sample& s) { + pullOne(s); + } + ); +} + +void M17ModSource::pullOne(Sample& sample) +{ + if (m_settings.m_channelMute) + { + sample.m_real = 0.0f; + sample.m_imag = 0.0f; + return; + } + + Complex ci; + + if (m_interpolatorDistance > 1.0f) // decimate + { + modulateSample(); + + while (!m_interpolator.decimate(&m_interpolatorDistanceRemain, m_modSample, &ci)) + { + modulateSample(); + } + } + else + { + if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, m_modSample, &ci)) + { + modulateSample(); + } + } + + m_interpolatorDistanceRemain += m_interpolatorDistance; + + ci *= m_carrierNco.nextIQ(); // shift to carrier frequency + + double magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); + magsq /= (SDR_TX_SCALED*SDR_TX_SCALED); + m_movingAverage(magsq); + m_magsq = m_movingAverage.asDouble(); + + sample.m_real = (FixReal) ci.real(); + sample.m_imag = (FixReal) ci.imag(); +} + +void M17ModSource::prefetch(unsigned int nbSamples) +{ + unsigned int nbSamplesAudio = nbSamples * ((Real) m_audioSampleRate / (Real) m_channelSampleRate); + pullAudio(nbSamplesAudio); +} + +void M17ModSource::pullAudio(unsigned int nbSamplesAudio) +{ + QMutexLocker mlock(&m_mutex); + + if (nbSamplesAudio > m_audioBuffer.size()) { + m_audioBuffer.resize(nbSamplesAudio); + } + + std::copy(&m_audioReadBuffer[0], &m_audioReadBuffer[nbSamplesAudio], &m_audioBuffer[0]); + m_audioBufferFill = 0; + + if (m_audioReadBufferFill > nbSamplesAudio) // copy back remaining samples at the start of the read buffer + { + std::copy(&m_audioReadBuffer[nbSamplesAudio], &m_audioReadBuffer[m_audioReadBufferFill], &m_audioReadBuffer[0]); + m_audioReadBufferFill = m_audioReadBufferFill - nbSamplesAudio; // adjust current read buffer fill pointer + } +} + +void M17ModSource::modulateSample() +{ + Real t1, t; + + pullAF(t); + + if (m_settings.m_feedbackAudioEnable) { + pushFeedback(t * m_settings.m_feedbackVolumeFactor * 16384.0f); + } + + calculateLevel(t); + t1 = m_lowpass.filter(t) * 1.2f; + + m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * t1; + + // limit phasor range to ]-pi,pi] + if (m_modPhasor > M_PI) { + m_modPhasor -= (2.0f * M_PI); + } + + m_modSample.real(cos(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); // -1 dB + m_modSample.imag(sin(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); + + m_demodBuffer[m_demodBufferFill] = t1 * std::numeric_limits::max(); + ++m_demodBufferFill; + + if (m_demodBufferFill >= m_demodBuffer.size()) + { + QList dataPipes; + MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes); + + if (dataPipes.size() > 0) + { + QList::iterator it = dataPipes.begin(); + + for (; it != dataPipes.end(); ++it) + { + DataFifo *fifo = qobject_cast((*it)->m_element); + + if (fifo) { + fifo->write((quint8*) &m_demodBuffer[0], m_demodBuffer.size() * sizeof(qint16), DataFifo::DataTypeI16); + } + } + } + + m_demodBufferFill = 0; + } +} + +void M17ModSource::pullAF(Real& sample) +{ + switch (m_settings.m_modAFInput) + { + case M17ModSettings::M17ModInputTone: + sample = m_toneNco.next(); + break; + case M17ModSettings::M17ModInputFile: + // sox f4exb_call.wav --encoding float --endian little f4exb_call.raw + // ffplay -f f32le -ar 48k -ac 1 f4exb_call.raw + if (m_ifstream && m_ifstream->is_open()) + { + if (m_ifstream->eof()) + { + if (m_settings.m_playLoop) + { + m_ifstream->clear(); + m_ifstream->seekg(0, std::ios::beg); + } + } + + if (m_ifstream->eof()) + { + sample = 0.0f; + } + else + { + m_ifstream->read(reinterpret_cast(&sample), sizeof(Real)); + sample *= m_settings.m_volumeFactor; + } + } + else + { + sample = 0.0f; + } + break; + case M17ModSettings::M17ModInputAudio: + if (m_audioBufferFill < m_audioBuffer.size()) + { + sample = ((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_settings.m_volumeFactor; + m_audioBufferFill++; + } + else + { + unsigned int size = m_audioBuffer.size(); + qDebug("NFMModSource::pullAF: starve audio samples: size: %u", size); + sample = ((m_audioBuffer[size-1].l + m_audioBuffer[size-1].r) / 65536.0f) * m_settings.m_volumeFactor; + } + + break; + case M17ModSettings::M17ModInputNone: + default: + sample = 0.0f; + break; + } +} + +void M17ModSource::pushFeedback(Real sample) +{ + Complex c(sample, sample); + Complex ci; + + if (m_feedbackInterpolatorDistance < 1.0f) // interpolate + { + while (!m_feedbackInterpolator.interpolate(&m_feedbackInterpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_feedbackInterpolatorDistanceRemain += m_feedbackInterpolatorDistance; + } + } + else // decimate + { + if (m_feedbackInterpolator.decimate(&m_feedbackInterpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_feedbackInterpolatorDistanceRemain += m_feedbackInterpolatorDistance; + } + } +} + +void M17ModSource::processOneSample(Complex& ci) +{ + m_feedbackAudioBuffer[m_feedbackAudioBufferFill].l = ci.real(); + m_feedbackAudioBuffer[m_feedbackAudioBufferFill].r = ci.imag(); + ++m_feedbackAudioBufferFill; + + if (m_feedbackAudioBufferFill >= m_feedbackAudioBuffer.size()) + { + uint res = m_feedbackAudioFifo.write((const quint8*)&m_feedbackAudioBuffer[0], m_feedbackAudioBufferFill); + + if (res != m_feedbackAudioBufferFill) + { + qDebug("M17ModSource::pushFeedback: %u/%u audio samples written m_feedbackInterpolatorDistance: %f", + res, m_feedbackAudioBufferFill, m_feedbackInterpolatorDistance); + m_feedbackAudioFifo.clear(); + } + + m_feedbackAudioBufferFill = 0; + } +} + +void M17ModSource::calculateLevel(Real& sample) +{ + if (m_levelCalcCount < m_levelNbSamples) + { + m_peakLevel = std::max(std::fabs(m_peakLevel), sample); + m_levelSum += sample * sample; + m_levelCalcCount++; + } + else + { + m_rmsLevel = sqrt(m_levelSum / m_levelNbSamples); + m_peakLevelOut = m_peakLevel; + m_peakLevel = 0.0f; + m_levelSum = 0.0f; + m_levelCalcCount = 0; + } +} + +void M17ModSource::applyAudioSampleRate(int sampleRate) +{ + if (sampleRate < 0) + { + qWarning("M17ModSource::applyAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("M17ModSource::applyAudioSampleRate: %d", sampleRate); + + m_interpolatorDistanceRemain = 0; + m_interpolatorConsumed = false; + m_interpolatorDistance = (Real) sampleRate / (Real) m_channelSampleRate; + m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0); + m_lowpass.create(301, sampleRate, m_settings.m_rfBandwidth); + m_toneNco.setFreq(m_settings.m_toneFrequency, sampleRate); + m_audioSampleRate = sampleRate; + applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate); + + QList pipes; + MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes); + + if (pipes.size() > 0) + { + for (const auto& pipe : pipes) + { + MessageQueue* messageQueue = qobject_cast(pipe->m_element); + MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create(m_channel, sampleRate); + messageQueue->push(msg); + } + } +} + +void M17ModSource::applyFeedbackAudioSampleRate(int sampleRate) +{ + if (sampleRate < 0) + { + qWarning("NFMModSource::applyFeedbackAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("NFMModSource::applyFeedbackAudioSampleRate: %d", sampleRate); + + m_feedbackInterpolatorDistanceRemain = 0; + m_feedbackInterpolatorConsumed = false; + m_feedbackInterpolatorDistance = (Real) sampleRate / (Real) m_audioSampleRate; + Real cutoff = std::min(sampleRate, m_audioSampleRate) / 2.2f; + m_feedbackInterpolator.create(48, sampleRate, cutoff, 3.0); + m_feedbackAudioSampleRate = sampleRate; +} + +void M17ModSource::applySettings(const M17ModSettings& settings, bool force) +{ + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + { + m_settings.m_rfBandwidth = settings.m_rfBandwidth; + applyAudioSampleRate(m_audioSampleRate); + } + + if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + m_toneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate); + } + + if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) + { + if (settings.m_modAFInput == M17ModSettings::M17ModInputAudio) { + connect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio())); + } else { + disconnect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio())); + } + } + + m_settings = settings; +} + +void M17ModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "M17ModSource::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if ((channelFrequencyOffset != m_channelFrequencyOffset) + || (channelSampleRate != m_channelSampleRate) || force) + { + m_carrierNco.setFreq(channelFrequencyOffset, channelSampleRate); + } + + if ((channelSampleRate != m_channelSampleRate) || force) + { + m_interpolatorDistanceRemain = 0; + m_interpolatorConsumed = false; + m_interpolatorDistance = (Real) m_audioSampleRate / (Real) channelSampleRate; + m_interpolator.create(48, m_audioSampleRate, m_settings.m_rfBandwidth / 2.2, 3.0); + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void M17ModSource::handleAudio() +{ + QMutexLocker mlock(&m_mutex); + unsigned int nbRead; + + while ((nbRead = m_audioFifo.read(reinterpret_cast(&m_audioReadBuffer[m_audioReadBufferFill]), 4096)) != 0) + { + if (m_audioReadBufferFill + nbRead + 4096 < m_audioReadBuffer.size()) { + m_audioReadBufferFill += nbRead; + } + } +} diff --git a/plugins/channeltx/modm17/m17modsource.h b/plugins/channeltx/modm17/m17modsource.h new file mode 100644 index 000000000..6b852b4b1 --- /dev/null +++ b/plugins/channeltx/modm17/m17modsource.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_M17MODSOURCE_H +#define INCLUDE_M17MODSOURCE_H + +#include +#include +#include + +#include +#include + +#include "dsp/channelsamplesource.h" +#include "dsp/nco.h" +#include "dsp/ncof.h" +#include "dsp/interpolator.h" +#include "dsp/firfilter.h" +#include "dsp/filterrc.h" +#include "util/movingaverage.h" +#include "audio/audiofifo.h" + +#include "m17modsettings.h" + +class ChannelAPI; + +class M17ModSource : public QObject, public ChannelSampleSource +{ + Q_OBJECT +public: + M17ModSource(); + virtual ~M17ModSource(); + + virtual void pull(SampleVector::iterator begin, unsigned int nbSamples); + virtual void pullOne(Sample& sample); + virtual void prefetch(unsigned int nbSamples); + + void setInputFileStream(std::ifstream *ifstream) { m_ifstream = ifstream; } + AudioFifo *getAudioFifo() { return &m_audioFifo; } + AudioFifo *getFeedbackAudioFifo() { return &m_feedbackAudioFifo; } + void applyAudioSampleRate(int sampleRate); + void applyFeedbackAudioSampleRate(int sampleRate); + int getAudioSampleRate() const { return m_audioSampleRate; } + int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } + void setChannel(ChannelAPI *channel) { m_channel = channel; } + double getMagSq() const { return m_magsq; } + void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const + { + rmsLevel = m_rmsLevel; + peakLevel = m_peakLevelOut; + numSamples = m_levelNbSamples; + } + void applySettings(const M17ModSettings& settings, bool force = false); + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + +private: + int m_channelSampleRate; + int m_channelFrequencyOffset; + M17ModSettings m_settings; + ChannelAPI *m_channel; + + NCO m_carrierNco; + NCOF m_toneNco; + float m_modPhasor; //!< baseband modulator phasor + Complex m_modSample; + + Interpolator m_interpolator; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + bool m_interpolatorConsumed; + + Interpolator m_feedbackInterpolator; + Real m_feedbackInterpolatorDistance; + Real m_feedbackInterpolatorDistanceRemain; + bool m_feedbackInterpolatorConsumed; + + QVector m_demodBuffer; + int m_demodBufferFill; + + Lowpass m_lowpass; + + double m_magsq; + MovingAverageUtil m_movingAverage; + + int m_audioSampleRate; + AudioVector m_audioBuffer; + unsigned int m_audioBufferFill; + AudioVector m_audioReadBuffer; + unsigned int m_audioReadBufferFill; + AudioFifo m_audioFifo; + + int m_feedbackAudioSampleRate; + AudioVector m_feedbackAudioBuffer; + uint m_feedbackAudioBufferFill; + AudioFifo m_feedbackAudioFifo; + + quint32 m_levelCalcCount; + qreal m_rmsLevel; + qreal m_peakLevelOut; + Real m_peakLevel; + Real m_levelSum; + + std::ifstream *m_ifstream; + + QMutex m_mutex; + + static const int m_levelNbSamples; + static const float m_preemphasis; + + void processOneSample(Complex& ci); + void pullAF(Real& sample); + void pullAudio(unsigned int nbSamples); + void pushFeedback(Real sample); + void calculateLevel(Real& sample); + void modulateSample(); + +private slots: + void handleAudio(); +}; + + + +#endif // INCLUDE_M17MODSOURCE_H diff --git a/plugins/channeltx/modm17/m17modwebapiadapter.cpp b/plugins/channeltx/modm17/m17modwebapiadapter.cpp new file mode 100644 index 000000000..829fd378f --- /dev/null +++ b/plugins/channeltx/modm17/m17modwebapiadapter.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGChannelSettings.h" +#include "dsp/cwkeyer.h" +#include "m17mod.h" +#include "m17modwebapiadapter.h" + +M17ModWebAPIAdapter::M17ModWebAPIAdapter() +{} + +M17ModWebAPIAdapter::~M17ModWebAPIAdapter() +{} + +int M17ModWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setM17ModSettings(new SWGSDRangel::SWGM17ModSettings()); + response.getM17ModSettings()->init(); + M17Mod::webapiFormatChannelSettings(response, m_settings); + return 200; +} + +int M17ModWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) force; // no action + (void) errorMessage; + M17Mod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + M17Mod::webapiFormatChannelSettings(response, m_settings); + return 200; +} diff --git a/plugins/channeltx/modm17/m17modwebapiadapter.h b/plugins/channeltx/modm17/m17modwebapiadapter.h new file mode 100644 index 000000000..00ca96109 --- /dev/null +++ b/plugins/channeltx/modm17/m17modwebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2022 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_M17MOD_WEBAPIADAPTER_H +#define INCLUDE_M17MOD_WEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "m17modsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class M17ModWebAPIAdapter : public ChannelWebAPIAdapter { +public: + M17ModWebAPIAdapter(); + virtual ~M17ModWebAPIAdapter(); + + 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: + M17ModSettings m_settings; +}; + +#endif // INCLUDE_M17MOD_WEBAPIADAPTER_H diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 0a3ce0758..c77a093ed 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -3424,6 +3424,9 @@ margin-bottom: 20px; "M17DemodReport" : { "$ref" : "#/definitions/M17DemodReport" }, + "M17ModReport" : { + "$ref" : "#/definitions/M17ModReport" + }, "NFMDemodReport" : { "$ref" : "#/definitions/NFMDemodReport" }, @@ -3579,6 +3582,9 @@ margin-bottom: 20px; "M17DemodSettings" : { "$ref" : "#/definitions/M17DemodSettings" }, + "M17ModSettings" : { + "$ref" : "#/definitions/M17ModSettings" + }, "NFMDemodSettings" : { "$ref" : "#/definitions/NFMDemodSettings" }, @@ -8330,6 +8336,91 @@ margin-bottom: 20px; } }, "description" : "M17Demod" +}; + defs.M17ModReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power transmitted in channel (dB)" + }, + "audioSampleRate" : { + "type" : "integer" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "NFMMod" +}; + defs.M17ModSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "fmDeviation" : { + "type" : "number", + "format" : "float" + }, + "toneFrequency" : { + "type" : "number", + "format" : "float" + }, + "volumeFactor" : { + "type" : "number", + "format" : "float" + }, + "channelMute" : { + "type" : "integer" + }, + "playLoop" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "audioDeviceName" : { + "type" : "string" + }, + "modAFInput" : { + "type" : "integer" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "M17Mod" }; defs.MapActions = { "properties" : { @@ -56289,7 +56380,7 @@ except ApiException as e:
- Generated 2022-06-05T00:26:14.019+02:00 + Generated 2022-06-09T22:25:54.513+02:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml index ddc65e958..099f4b058 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml @@ -51,6 +51,8 @@ ChannelReport: $ref: "/doc/swagger/include/FreqTracker.yaml#/FreqTrackerReport" M17DemodReport: $ref: "/doc/swagger/include/M17Demod.yaml#/M17DemodReport" + M17ModReport: + $ref: "/doc/swagger/include/M17Mod.yaml#/M17ModReport" NFMDemodReport: $ref: "/doc/swagger/include/NFMDemod.yaml#/NFMDemodReport" NFMModReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml index 6833684ef..5f9c3c79c 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml @@ -69,6 +69,8 @@ ChannelSettings: $ref: "/doc/swagger/include/IEEE_802_15_4_Mod.yaml#/IEEE_802_15_4_ModSettings" M17DemodSettings: $ref: "/doc/swagger/include/M17Demod.yaml#/M17DemodSettings" + M17ModSettings: + $ref: "/doc/swagger/include/M17Mod.yaml#/M17ModSettings" NFMDemodSettings: $ref: "/doc/swagger/include/NFMDemod.yaml#/NFMDemodSettings" NFMModSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/M17Mod.yaml b/sdrbase/resources/webapi/doc/swagger/include/M17Mod.yaml new file mode 100644 index 000000000..5cb05b2ce --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/M17Mod.yaml @@ -0,0 +1,60 @@ +M17ModSettings: + description: M17Mod + properties: + inputFrequencyOffset: + type: integer + format: int64 + rfBandwidth: + type: number + format: float + fmDeviation: + type: number + format: float + toneFrequency: + type: number + format: float + volumeFactor: + type: number + format: float + channelMute: + type: integer + playLoop: + type: integer + rgbColor: + type: integer + title: + type: string + audioDeviceName: + type: string + modAFInput: + type: integer + 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 + channelMarker: + $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +M17ModReport: + description: NFMMod + properties: + channelPowerDB: + description: power transmitted in channel (dB) + type: number + format: float + audioSampleRate: + type: integer + channelSampleRate: + type: integer diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 116a9ca87..7c34c7c6c 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4516,16 +4516,21 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setInterferometerSettings(new SWGSDRangel::SWGInterferometerSettings()); channelSettings->getInterferometerSettings()->fromJsonObject(settingsJsonObject); } - else if (channelSettingsKey == "NFMDemodSettings") - { - channelSettings->setNfmDemodSettings(new SWGSDRangel::SWGNFMDemodSettings()); - channelSettings->getNfmDemodSettings()->fromJsonObject(settingsJsonObject); - } else if (channelSettingsKey == "M17DemodSettings") { channelSettings->setM17DemodSettings(new SWGSDRangel::SWGM17DemodSettings()); channelSettings->getM17DemodSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "M17ModSettings") + { + channelSettings->setM17ModSettings(new SWGSDRangel::SWGM17ModSettings()); + channelSettings->getM17ModSettings()->fromJsonObject(settingsJsonObject); + } + else if (channelSettingsKey == "NFMDemodSettings") + { + channelSettings->setNfmDemodSettings(new SWGSDRangel::SWGNFMDemodSettings()); + channelSettings->getNfmDemodSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "NFMModSettings") { channelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings()); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 0c4c5b256..3625e86cf 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -48,6 +48,7 @@ const QMap WebAPIUtils::m_channelURIToSettingsKey = { {"sdrangel.channeltx.freedvmod", "FreeDVModSettings"}, {"sdrangel.channel.freqtracker", "FreqTrackerSettings"}, {"sdrangel.channel.m17demod", "M17DemodSettings"}, + {"sdrangel.channeltx.modm17", "M17ModSettings"}, {"sdrangel.channel.nfmdemod", "NFMDemodSettings"}, {"de.maintech.sdrangelove.channel.nfm", "NFMDemodSettings"}, // remap {"sdrangel.channeltx.modnfm", "NFMModSettings"}, @@ -155,6 +156,7 @@ const QMap WebAPIUtils::m_channelTypeToSettingsKey = { {"FreqTracker", "FreqTrackerSettings"}, {"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModSettings"}, {"M17Demod", "M17DemodSettings"}, + {"M17Mod", "M17ModSettings"}, {"NFMDemod", "NFMDemodSettings"}, {"NFMMod", "NFMModSettings"}, {"NoiseFigure", "NoiseFigureSettings"}, diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index 517b84493..378e6a9f6 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -51,6 +51,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerReport" M17DemodReport: $ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodReport" + M17ModReport: + $ref: "http://swgserver:8081/api/swagger/include/M17Mod.yaml#/M17ModReport" NFMDemodReport: $ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodReport" NFMModReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 0e1bc4723..d599473d2 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -69,6 +69,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/IEEE_802_15_4_Mod.yaml#/IEEE_802_15_4_ModSettings" M17DemodSettings: $ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodSettings" + M17ModSettings: + $ref: "http://swgserver:8081/api/swagger/include/M17Mod.yaml#/M17ModSettings" NFMDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodSettings" NFMModSettings: diff --git a/swagger/sdrangel/api/swagger/include/M17Mod.yaml b/swagger/sdrangel/api/swagger/include/M17Mod.yaml new file mode 100644 index 000000000..42a65efa8 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/M17Mod.yaml @@ -0,0 +1,60 @@ +M17ModSettings: + description: M17Mod + properties: + inputFrequencyOffset: + type: integer + format: int64 + rfBandwidth: + type: number + format: float + fmDeviation: + type: number + format: float + toneFrequency: + type: number + format: float + volumeFactor: + type: number + format: float + channelMute: + type: integer + playLoop: + type: integer + rgbColor: + type: integer + title: + type: string + audioDeviceName: + type: string + modAFInput: + type: integer + 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 + channelMarker: + $ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +M17ModReport: + description: NFMMod + properties: + channelPowerDB: + description: power transmitted in channel (dB) + type: number + format: float + audioSampleRate: + type: integer + channelSampleRate: + type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 0a3ce0758..c77a093ed 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -3424,6 +3424,9 @@ margin-bottom: 20px; "M17DemodReport" : { "$ref" : "#/definitions/M17DemodReport" }, + "M17ModReport" : { + "$ref" : "#/definitions/M17ModReport" + }, "NFMDemodReport" : { "$ref" : "#/definitions/NFMDemodReport" }, @@ -3579,6 +3582,9 @@ margin-bottom: 20px; "M17DemodSettings" : { "$ref" : "#/definitions/M17DemodSettings" }, + "M17ModSettings" : { + "$ref" : "#/definitions/M17ModSettings" + }, "NFMDemodSettings" : { "$ref" : "#/definitions/NFMDemodSettings" }, @@ -8330,6 +8336,91 @@ margin-bottom: 20px; } }, "description" : "M17Demod" +}; + defs.M17ModReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power transmitted in channel (dB)" + }, + "audioSampleRate" : { + "type" : "integer" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "NFMMod" +}; + defs.M17ModSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "fmDeviation" : { + "type" : "number", + "format" : "float" + }, + "toneFrequency" : { + "type" : "number", + "format" : "float" + }, + "volumeFactor" : { + "type" : "number", + "format" : "float" + }, + "channelMute" : { + "type" : "integer" + }, + "playLoop" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "audioDeviceName" : { + "type" : "string" + }, + "modAFInput" : { + "type" : "integer" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "M17Mod" }; defs.MapActions = { "properties" : { @@ -56289,7 +56380,7 @@ except ApiException as e:
- Generated 2022-06-05T00:26:14.019+02:00 + Generated 2022-06-09T22:25:54.513+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index 686f24dca..df8fa4ec5 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -72,6 +72,8 @@ SWGChannelReport::SWGChannelReport() { m_freq_tracker_report_isSet = false; m17_demod_report = nullptr; m_m17_demod_report_isSet = false; + m17_mod_report = nullptr; + m_m17_mod_report_isSet = false; nfm_demod_report = nullptr; m_nfm_demod_report_isSet = false; nfm_mod_report = nullptr; @@ -160,6 +162,8 @@ SWGChannelReport::init() { m_freq_tracker_report_isSet = false; m17_demod_report = new SWGM17DemodReport(); m_m17_demod_report_isSet = false; + m17_mod_report = new SWGM17ModReport(); + m_m17_mod_report_isSet = false; nfm_demod_report = new SWGNFMDemodReport(); m_nfm_demod_report_isSet = false; nfm_mod_report = new SWGNFMModReport(); @@ -264,6 +268,9 @@ SWGChannelReport::cleanup() { if(m17_demod_report != nullptr) { delete m17_demod_report; } + if(m17_mod_report != nullptr) { + delete m17_mod_report; + } if(nfm_demod_report != nullptr) { delete nfm_demod_report; } @@ -375,6 +382,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&m17_demod_report, pJson["M17DemodReport"], "SWGM17DemodReport", "SWGM17DemodReport"); + ::SWGSDRangel::setValue(&m17_mod_report, pJson["M17ModReport"], "SWGM17ModReport", "SWGM17ModReport"); + ::SWGSDRangel::setValue(&nfm_demod_report, pJson["NFMDemodReport"], "SWGNFMDemodReport", "SWGNFMDemodReport"); ::SWGSDRangel::setValue(&nfm_mod_report, pJson["NFMModReport"], "SWGNFMModReport", "SWGNFMModReport"); @@ -493,6 +502,9 @@ SWGChannelReport::asJsonObject() { if((m17_demod_report != nullptr) && (m17_demod_report->isSet())){ toJsonValue(QString("M17DemodReport"), m17_demod_report, obj, QString("SWGM17DemodReport")); } + if((m17_mod_report != nullptr) && (m17_mod_report->isSet())){ + toJsonValue(QString("M17ModReport"), m17_mod_report, obj, QString("SWGM17ModReport")); + } if((nfm_demod_report != nullptr) && (nfm_demod_report->isSet())){ toJsonValue(QString("NFMDemodReport"), nfm_demod_report, obj, QString("SWGNFMDemodReport")); } @@ -771,6 +783,16 @@ SWGChannelReport::setM17DemodReport(SWGM17DemodReport* m17_demod_report) { this->m_m17_demod_report_isSet = true; } +SWGM17ModReport* +SWGChannelReport::getM17ModReport() { + return m17_mod_report; +} +void +SWGChannelReport::setM17ModReport(SWGM17ModReport* m17_mod_report) { + this->m17_mod_report = m17_mod_report; + this->m_m17_mod_report_isSet = true; +} + SWGNFMDemodReport* SWGChannelReport::getNfmDemodReport() { return nfm_demod_report; @@ -1022,6 +1044,9 @@ SWGChannelReport::isSet(){ if(m17_demod_report && m17_demod_report->isSet()){ isObjectUpdated = true; break; } + if(m17_mod_report && m17_mod_report->isSet()){ + isObjectUpdated = true; break; + } if(nfm_demod_report && nfm_demod_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index aa6174786..21d196401 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -42,6 +42,7 @@ #include "SWGFreqTrackerReport.h" #include "SWGIEEE_802_15_4_ModReport.h" #include "SWGM17DemodReport.h" +#include "SWGM17ModReport.h" #include "SWGNFMDemodReport.h" #include "SWGNFMModReport.h" #include "SWGNoiseFigureReport.h" @@ -146,6 +147,9 @@ public: SWGM17DemodReport* getM17DemodReport(); void setM17DemodReport(SWGM17DemodReport* m17_demod_report); + SWGM17ModReport* getM17ModReport(); + void setM17ModReport(SWGM17ModReport* m17_mod_report); + SWGNFMDemodReport* getNfmDemodReport(); void setNfmDemodReport(SWGNFMDemodReport* nfm_demod_report); @@ -270,6 +274,9 @@ private: SWGM17DemodReport* m17_demod_report; bool m_m17_demod_report_isSet; + SWGM17ModReport* m17_mod_report; + bool m_m17_mod_report_isSet; + SWGNFMDemodReport* nfm_demod_report; bool m_nfm_demod_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index 344c697f5..c64c80474 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -88,6 +88,8 @@ SWGChannelSettings::SWGChannelSettings() { m_ieee_802_15_4_mod_settings_isSet = false; m17_demod_settings = nullptr; m_m17_demod_settings_isSet = false; + m17_mod_settings = nullptr; + m_m17_mod_settings_isSet = false; nfm_demod_settings = nullptr; m_nfm_demod_settings_isSet = false; nfm_mod_settings = nullptr; @@ -198,6 +200,8 @@ SWGChannelSettings::init() { m_ieee_802_15_4_mod_settings_isSet = false; m17_demod_settings = new SWGM17DemodSettings(); m_m17_demod_settings_isSet = false; + m17_mod_settings = new SWGM17ModSettings(); + m_m17_mod_settings_isSet = false; nfm_demod_settings = new SWGNFMDemodSettings(); m_nfm_demod_settings_isSet = false; nfm_mod_settings = new SWGNFMModSettings(); @@ -328,6 +332,9 @@ SWGChannelSettings::cleanup() { if(m17_demod_settings != nullptr) { delete m17_demod_settings; } + if(m17_mod_settings != nullptr) { + delete m17_mod_settings; + } if(nfm_demod_settings != nullptr) { delete nfm_demod_settings; } @@ -464,6 +471,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&m17_demod_settings, pJson["M17DemodSettings"], "SWGM17DemodSettings", "SWGM17DemodSettings"); + ::SWGSDRangel::setValue(&m17_mod_settings, pJson["M17ModSettings"], "SWGM17ModSettings", "SWGM17ModSettings"); + ::SWGSDRangel::setValue(&nfm_demod_settings, pJson["NFMDemodSettings"], "SWGNFMDemodSettings", "SWGNFMDemodSettings"); ::SWGSDRangel::setValue(&nfm_mod_settings, pJson["NFMModSettings"], "SWGNFMModSettings", "SWGNFMModSettings"); @@ -612,6 +621,9 @@ SWGChannelSettings::asJsonObject() { if((m17_demod_settings != nullptr) && (m17_demod_settings->isSet())){ toJsonValue(QString("M17DemodSettings"), m17_demod_settings, obj, QString("SWGM17DemodSettings")); } + if((m17_mod_settings != nullptr) && (m17_mod_settings->isSet())){ + toJsonValue(QString("M17ModSettings"), m17_mod_settings, obj, QString("SWGM17ModSettings")); + } if((nfm_demod_settings != nullptr) && (nfm_demod_settings->isSet())){ toJsonValue(QString("NFMDemodSettings"), nfm_demod_settings, obj, QString("SWGNFMDemodSettings")); } @@ -979,6 +991,16 @@ SWGChannelSettings::setM17DemodSettings(SWGM17DemodSettings* m17_demod_settings) this->m_m17_demod_settings_isSet = true; } +SWGM17ModSettings* +SWGChannelSettings::getM17ModSettings() { + return m17_mod_settings; +} +void +SWGChannelSettings::setM17ModSettings(SWGM17ModSettings* m17_mod_settings) { + this->m17_mod_settings = m17_mod_settings; + this->m_m17_mod_settings_isSet = true; +} + SWGNFMDemodSettings* SWGChannelSettings::getNfmDemodSettings() { return nfm_demod_settings; @@ -1284,6 +1306,9 @@ SWGChannelSettings::isSet(){ if(m17_demod_settings && m17_demod_settings->isSet()){ isObjectUpdated = true; break; } + if(m17_mod_settings && m17_mod_settings->isSet()){ + isObjectUpdated = true; break; + } if(nfm_demod_settings && nfm_demod_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index fbbf7dc5b..bb192cd0b 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -50,6 +50,7 @@ #include "SWGLocalSinkSettings.h" #include "SWGLocalSourceSettings.h" #include "SWGM17DemodSettings.h" +#include "SWGM17ModSettings.h" #include "SWGNFMDemodSettings.h" #include "SWGNFMModSettings.h" #include "SWGNoiseFigureSettings.h" @@ -179,6 +180,9 @@ public: SWGM17DemodSettings* getM17DemodSettings(); void setM17DemodSettings(SWGM17DemodSettings* m17_demod_settings); + SWGM17ModSettings* getM17ModSettings(); + void setM17ModSettings(SWGM17ModSettings* m17_mod_settings); + SWGNFMDemodSettings* getNfmDemodSettings(); void setNfmDemodSettings(SWGNFMDemodSettings* nfm_demod_settings); @@ -336,6 +340,9 @@ private: SWGM17DemodSettings* m17_demod_settings; bool m_m17_demod_settings_isSet; + SWGM17ModSettings* m17_mod_settings; + bool m_m17_mod_settings_isSet; + SWGNFMDemodSettings* nfm_demod_settings; bool m_nfm_demod_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGM17ModReport.cpp b/swagger/sdrangel/code/qt5/client/SWGM17ModReport.cpp new file mode 100644 index 000000000..1d29b5035 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGM17ModReport.cpp @@ -0,0 +1,154 @@ +/** + * 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: 7.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 "SWGM17ModReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGM17ModReport::SWGM17ModReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGM17ModReport::SWGM17ModReport() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +SWGM17ModReport::~SWGM17ModReport() { + this->cleanup(); +} + +void +SWGM17ModReport::init() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + audio_sample_rate = 0; + m_audio_sample_rate_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +void +SWGM17ModReport::cleanup() { + + + +} + +SWGM17ModReport* +SWGM17ModReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGM17ModReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); + + ::SWGSDRangel::setValue(&audio_sample_rate, pJson["audioSampleRate"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", ""); + +} + +QString +SWGM17ModReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGM17ModReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_power_db_isSet){ + obj->insert("channelPowerDB", QJsonValue(channel_power_db)); + } + if(m_audio_sample_rate_isSet){ + obj->insert("audioSampleRate", QJsonValue(audio_sample_rate)); + } + if(m_channel_sample_rate_isSet){ + obj->insert("channelSampleRate", QJsonValue(channel_sample_rate)); + } + + return obj; +} + +float +SWGM17ModReport::getChannelPowerDb() { + return channel_power_db; +} +void +SWGM17ModReport::setChannelPowerDb(float channel_power_db) { + this->channel_power_db = channel_power_db; + this->m_channel_power_db_isSet = true; +} + +qint32 +SWGM17ModReport::getAudioSampleRate() { + return audio_sample_rate; +} +void +SWGM17ModReport::setAudioSampleRate(qint32 audio_sample_rate) { + this->audio_sample_rate = audio_sample_rate; + this->m_audio_sample_rate_isSet = true; +} + +qint32 +SWGM17ModReport::getChannelSampleRate() { + return channel_sample_rate; +} +void +SWGM17ModReport::setChannelSampleRate(qint32 channel_sample_rate) { + this->channel_sample_rate = channel_sample_rate; + this->m_channel_sample_rate_isSet = true; +} + + +bool +SWGM17ModReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_power_db_isSet){ + isObjectUpdated = true; break; + } + if(m_audio_sample_rate_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_sample_rate_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGM17ModReport.h b/swagger/sdrangel/code/qt5/client/SWGM17ModReport.h new file mode 100644 index 000000000..4a87b7dc4 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGM17ModReport.h @@ -0,0 +1,70 @@ +/** + * 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: 7.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. + */ + +/* + * SWGM17ModReport.h + * + * NFMMod + */ + +#ifndef SWGM17ModReport_H_ +#define SWGM17ModReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGM17ModReport: public SWGObject { +public: + SWGM17ModReport(); + SWGM17ModReport(QString* json); + virtual ~SWGM17ModReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGM17ModReport* fromJson(QString &jsonString) override; + + float getChannelPowerDb(); + void setChannelPowerDb(float channel_power_db); + + qint32 getAudioSampleRate(); + void setAudioSampleRate(qint32 audio_sample_rate); + + qint32 getChannelSampleRate(); + void setChannelSampleRate(qint32 channel_sample_rate); + + + virtual bool isSet() override; + +private: + float channel_power_db; + bool m_channel_power_db_isSet; + + qint32 audio_sample_rate; + bool m_audio_sample_rate_isSet; + + qint32 channel_sample_rate; + bool m_channel_sample_rate_isSet; + +}; + +} + +#endif /* SWGM17ModReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.cpp new file mode 100644 index 000000000..1ff287761 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.cpp @@ -0,0 +1,532 @@ +/** + * 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: 7.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 "SWGM17ModSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGM17ModSettings::SWGM17ModSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGM17ModSettings::SWGM17ModSettings() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + fm_deviation = 0.0f; + m_fm_deviation_isSet = false; + tone_frequency = 0.0f; + m_tone_frequency_isSet = false; + volume_factor = 0.0f; + m_volume_factor_isSet = false; + channel_mute = 0; + m_channel_mute_isSet = false; + play_loop = 0; + m_play_loop_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + audio_device_name = nullptr; + m_audio_device_name_isSet = false; + mod_af_input = 0; + m_mod_af_input_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; + channel_marker = nullptr; + m_channel_marker_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGM17ModSettings::~SWGM17ModSettings() { + this->cleanup(); +} + +void +SWGM17ModSettings::init() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + fm_deviation = 0.0f; + m_fm_deviation_isSet = false; + tone_frequency = 0.0f; + m_tone_frequency_isSet = false; + volume_factor = 0.0f; + m_volume_factor_isSet = false; + channel_mute = 0; + m_channel_mute_isSet = false; + play_loop = 0; + m_play_loop_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + audio_device_name = new QString(""); + m_audio_device_name_isSet = false; + mod_af_input = 0; + m_mod_af_input_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; + channel_marker = new SWGChannelMarker(); + m_channel_marker_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGM17ModSettings::cleanup() { + + + + + + + + + if(title != nullptr) { + delete title; + } + if(audio_device_name != nullptr) { + delete audio_device_name; + } + + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(channel_marker != nullptr) { + delete channel_marker; + } + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGM17ModSettings* +SWGM17ModSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGM17ModSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + + ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", ""); + + ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "float", ""); + + ::SWGSDRangel::setValue(&tone_frequency, pJson["toneFrequency"], "float", ""); + + ::SWGSDRangel::setValue(&volume_factor, pJson["volumeFactor"], "float", ""); + + ::SWGSDRangel::setValue(&channel_mute, pJson["channelMute"], "qint32", ""); + + ::SWGSDRangel::setValue(&play_loop, pJson["playLoop"], "qint32", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString"); + + ::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", ""); + + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGM17ModSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGM17ModSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_input_frequency_offset_isSet){ + obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); + } + if(m_rf_bandwidth_isSet){ + obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); + } + if(m_fm_deviation_isSet){ + obj->insert("fmDeviation", QJsonValue(fm_deviation)); + } + if(m_tone_frequency_isSet){ + obj->insert("toneFrequency", QJsonValue(tone_frequency)); + } + if(m_volume_factor_isSet){ + obj->insert("volumeFactor", QJsonValue(volume_factor)); + } + if(m_channel_mute_isSet){ + obj->insert("channelMute", QJsonValue(channel_mute)); + } + if(m_play_loop_isSet){ + obj->insert("playLoop", QJsonValue(play_loop)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(audio_device_name != nullptr && *audio_device_name != QString("")){ + toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString")); + } + if(m_mod_af_input_isSet){ + obj->insert("modAFInput", QJsonValue(mod_af_input)); + } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } + if((channel_marker != nullptr) && (channel_marker->isSet())){ + toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +qint64 +SWGM17ModSettings::getInputFrequencyOffset() { + return input_frequency_offset; +} +void +SWGM17ModSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { + this->input_frequency_offset = input_frequency_offset; + this->m_input_frequency_offset_isSet = true; +} + +float +SWGM17ModSettings::getRfBandwidth() { + return rf_bandwidth; +} +void +SWGM17ModSettings::setRfBandwidth(float rf_bandwidth) { + this->rf_bandwidth = rf_bandwidth; + this->m_rf_bandwidth_isSet = true; +} + +float +SWGM17ModSettings::getFmDeviation() { + return fm_deviation; +} +void +SWGM17ModSettings::setFmDeviation(float fm_deviation) { + this->fm_deviation = fm_deviation; + this->m_fm_deviation_isSet = true; +} + +float +SWGM17ModSettings::getToneFrequency() { + return tone_frequency; +} +void +SWGM17ModSettings::setToneFrequency(float tone_frequency) { + this->tone_frequency = tone_frequency; + this->m_tone_frequency_isSet = true; +} + +float +SWGM17ModSettings::getVolumeFactor() { + return volume_factor; +} +void +SWGM17ModSettings::setVolumeFactor(float volume_factor) { + this->volume_factor = volume_factor; + this->m_volume_factor_isSet = true; +} + +qint32 +SWGM17ModSettings::getChannelMute() { + return channel_mute; +} +void +SWGM17ModSettings::setChannelMute(qint32 channel_mute) { + this->channel_mute = channel_mute; + this->m_channel_mute_isSet = true; +} + +qint32 +SWGM17ModSettings::getPlayLoop() { + return play_loop; +} +void +SWGM17ModSettings::setPlayLoop(qint32 play_loop) { + this->play_loop = play_loop; + this->m_play_loop_isSet = true; +} + +qint32 +SWGM17ModSettings::getRgbColor() { + return rgb_color; +} +void +SWGM17ModSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGM17ModSettings::getTitle() { + return title; +} +void +SWGM17ModSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +QString* +SWGM17ModSettings::getAudioDeviceName() { + return audio_device_name; +} +void +SWGM17ModSettings::setAudioDeviceName(QString* audio_device_name) { + this->audio_device_name = audio_device_name; + this->m_audio_device_name_isSet = true; +} + +qint32 +SWGM17ModSettings::getModAfInput() { + return mod_af_input; +} +void +SWGM17ModSettings::setModAfInput(qint32 mod_af_input) { + this->mod_af_input = mod_af_input; + this->m_mod_af_input_isSet = true; +} + +qint32 +SWGM17ModSettings::getStreamIndex() { + return stream_index; +} +void +SWGM17ModSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGM17ModSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGM17ModSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGM17ModSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGM17ModSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGM17ModSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGM17ModSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGM17ModSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGM17ModSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGM17ModSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGM17ModSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + +SWGChannelMarker* +SWGM17ModSettings::getChannelMarker() { + return channel_marker; +} +void +SWGM17ModSettings::setChannelMarker(SWGChannelMarker* channel_marker) { + this->channel_marker = channel_marker; + this->m_channel_marker_isSet = true; +} + +SWGRollupState* +SWGM17ModSettings::getRollupState() { + return rollup_state; +} +void +SWGM17ModSettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGM17ModSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_input_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(m_rf_bandwidth_isSet){ + isObjectUpdated = true; break; + } + if(m_fm_deviation_isSet){ + isObjectUpdated = true; break; + } + if(m_tone_frequency_isSet){ + isObjectUpdated = true; break; + } + if(m_volume_factor_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_mute_isSet){ + isObjectUpdated = true; break; + } + if(m_play_loop_isSet){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(audio_device_name && *audio_device_name != QString("")){ + isObjectUpdated = true; break; + } + if(m_mod_af_input_isSet){ + isObjectUpdated = true; break; + } + if(m_stream_index_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_device_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_channel_index_isSet){ + isObjectUpdated = true; break; + } + if(channel_marker && channel_marker->isSet()){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.h b/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.h new file mode 100644 index 000000000..2226d218b --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGM17ModSettings.h @@ -0,0 +1,169 @@ +/** + * 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: 7.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. + */ + +/* + * SWGM17ModSettings.h + * + * M17Mod + */ + +#ifndef SWGM17ModSettings_H_ +#define SWGM17ModSettings_H_ + +#include + + +#include "SWGChannelMarker.h" +#include "SWGRollupState.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGM17ModSettings: public SWGObject { +public: + SWGM17ModSettings(); + SWGM17ModSettings(QString* json); + virtual ~SWGM17ModSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGM17ModSettings* fromJson(QString &jsonString) override; + + qint64 getInputFrequencyOffset(); + void setInputFrequencyOffset(qint64 input_frequency_offset); + + float getRfBandwidth(); + void setRfBandwidth(float rf_bandwidth); + + float getFmDeviation(); + void setFmDeviation(float fm_deviation); + + float getToneFrequency(); + void setToneFrequency(float tone_frequency); + + float getVolumeFactor(); + void setVolumeFactor(float volume_factor); + + qint32 getChannelMute(); + void setChannelMute(qint32 channel_mute); + + qint32 getPlayLoop(); + void setPlayLoop(qint32 play_loop); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + QString* getAudioDeviceName(); + void setAudioDeviceName(QString* audio_device_name); + + qint32 getModAfInput(); + void setModAfInput(qint32 mod_af_input); + + 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); + + SWGChannelMarker* getChannelMarker(); + void setChannelMarker(SWGChannelMarker* channel_marker); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + qint64 input_frequency_offset; + bool m_input_frequency_offset_isSet; + + float rf_bandwidth; + bool m_rf_bandwidth_isSet; + + float fm_deviation; + bool m_fm_deviation_isSet; + + float tone_frequency; + bool m_tone_frequency_isSet; + + float volume_factor; + bool m_volume_factor_isSet; + + qint32 channel_mute; + bool m_channel_mute_isSet; + + qint32 play_loop; + bool m_play_loop_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + QString* audio_device_name; + bool m_audio_device_name_isSet; + + qint32 mod_af_input; + bool m_mod_af_input_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; + + SWGChannelMarker* channel_marker; + bool m_channel_marker_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGM17ModSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index ac6e46663..a254b851e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -185,6 +185,8 @@ #include "SWGLoggingInfo.h" #include "SWGM17DemodReport.h" #include "SWGM17DemodSettings.h" +#include "SWGM17ModReport.h" +#include "SWGM17ModSettings.h" #include "SWGMapActions.h" #include "SWGMapAnimation.h" #include "SWGMapCoordinate.h" @@ -1192,6 +1194,16 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGM17ModReport").compare(type) == 0) { + SWGM17ModReport *obj = new SWGM17ModReport(); + obj->init(); + return obj; + } + if(QString("SWGM17ModSettings").compare(type) == 0) { + SWGM17ModSettings *obj = new SWGM17ModSettings(); + obj->init(); + return obj; + } if(QString("SWGMapActions").compare(type) == 0) { SWGMapActions *obj = new SWGMapActions(); obj->init();