diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index 37c2873f1..5a9ea9ecb 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include "SWGChannelSettings.h" #include "SWGChannelReport.h" diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index f51e91276..ae82b756b 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include "SWGChannelSettings.h" #include "SWGCWKeyerSettings.h" @@ -90,10 +93,15 @@ NFMMod::NFMMod(DeviceSinkAPI *deviceAPI) : m_threadedChannelizer = new ThreadedBasebandSampleSource(m_channelizer, this); m_deviceAPI->addThreadedSource(m_threadedChannelizer); m_deviceAPI->addChannelAPI(this); + + m_networkManager = new QNetworkAccessManager(); + connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); } NFMMod::~NFMMod() { + disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); + delete m_networkManager; DSPEngine::instance()->getAudioDeviceManager()->removeAudioSource(&m_audioFifo); m_deviceAPI->removeChannelAPI(this); m_deviceAPI->removeThreadedSource(m_threadedChannelizer); @@ -348,6 +356,16 @@ bool NFMMod::handleMessage(const Message& cmd) return true; } + else if (CWKeyer::MsgConfigureCWKeyer::match(cmd)) + { + const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) cmd; + + if (m_settings.m_useReverseAPI) { + webapiReverseSendCWSettings(cfg.getSettings()); + } + + return true; + } else if (DSPConfigureAudio::match(cmd)) { DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; @@ -475,12 +493,48 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) << " m_ctcssOn: " << settings.m_ctcssOn << " m_channelMute: " << settings.m_channelMute << " m_playLoop: " << settings.m_playLoop - << " m_modAFInout " << settings.m_modAFInput + << " 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_ctcssOn != m_settings.m_ctcssOn) || force) { + reverseAPIKeys.append("ctcssOn"); + } + + 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"); m_settingsMutex.lock(); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -491,6 +545,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { + reverseAPIKeys.append("afBandwidth"); m_settingsMutex.lock(); m_lowpass.create(301, m_audioSampleRate, 250.0); m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_afBandwidth); @@ -499,6 +554,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + reverseAPIKeys.append("toneFrequency"); m_settingsMutex.lock(); m_toneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate); m_settingsMutex.unlock(); @@ -506,6 +562,7 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_ctcssIndex != m_settings.m_ctcssIndex) || force) { + reverseAPIKeys.append("ctcssIndex"); m_settingsMutex.lock(); m_ctcssNco.setFreq(NFMModSettings::getCTCSSFreq(settings.m_ctcssIndex), m_audioSampleRate); m_settingsMutex.unlock(); @@ -513,16 +570,28 @@ void NFMMod::applySettings(const NFMModSettings& settings, bool force) if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { + reverseAPIKeys.append("audioDeviceName"); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (m_audioSampleRate != audioSampleRate) { + reverseAPIKeys.append("audioSampleRate"); applyAudioSampleRate(audioSampleRate); } } + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex) || + (m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + m_settings = settings; } @@ -737,3 +806,141 @@ void NFMMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) response.getNfmModReport()->setAudioSampleRate(m_audioSampleRate); response.getNfmModReport()->setChannelSampleRate(m_outputSampleRate); } + +void NFMMod::webapiReverseSendSettings(QList& channelSettingsKeys, const NFMModSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("NFMMod")); + swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings()); + SWGSDRangel::SWGNFMModSettings *swgNFMModSettings = swgChannelSettings->getNfmModSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("channelMute") || force) { + swgNFMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0); + } + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgNFMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("modAFInput") || force) { + swgNFMModSettings->setModAfInput((int) settings.m_modAFInput); + } + if (channelSettingsKeys.contains("audioDeviceName") || force) { + swgNFMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName)); + } + if (channelSettingsKeys.contains("playLoop") || force) { + swgNFMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0); + } + if (channelSettingsKeys.contains("afBandwidth") || force) { + swgNFMModSettings->setAfBandwidth(settings.m_afBandwidth); + } + if (channelSettingsKeys.contains("fmDeviation") || force) { + swgNFMModSettings->setFmDeviation(settings.m_fmDeviation); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgNFMModSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgNFMModSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgNFMModSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("toneFrequency") || force) { + swgNFMModSettings->setToneFrequency(settings.m_toneFrequency); + } + if (channelSettingsKeys.contains("volumeFactor") || force) { + swgNFMModSettings->setVolumeFactor(settings.m_volumeFactor); + } + if (channelSettingsKeys.contains("ctcssOn") || force) { + swgNFMModSettings->setCtcssOn(settings.m_ctcssOn ? 1 : 0); + } + if (channelSettingsKeys.contains("ctcssIndex") || force) { + swgNFMModSettings->setCtcssIndex(settings.m_ctcssIndex); + } + + if (force) + { + const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings(); + swgNFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFMModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + } + + 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 + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void NFMMod::webapiReverseSendCWSettings(const CWKeyerSettings& cwKeyerSettings) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + swgChannelSettings->setTx(1); + swgChannelSettings->setChannelType(new QString("AMMod")); + swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings()); + SWGSDRangel::SWGNFMModSettings *swgNFModSettings = swgChannelSettings->getNfmModSettings(); + + swgNFModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings()); + SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFModSettings->getCwKeyer(); + apiCwKeyerSettings->setLoop(cwKeyerSettings.m_loop ? 1 : 0); + apiCwKeyerSettings->setMode(cwKeyerSettings.m_mode); + apiCwKeyerSettings->setSampleRate(cwKeyerSettings.m_sampleRate); + apiCwKeyerSettings->setText(new QString(cwKeyerSettings.m_text)); + apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(m_settings.m_reverseAPIAddress) + .arg(m_settings.m_reverseAPIPort) + .arg(m_settings.m_reverseAPIDeviceIndex) + .arg(m_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 + m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + + delete swgChannelSettings; +} + +void NFMMod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "NFMMod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + return; + } + + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("NFMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); +} + diff --git a/plugins/channeltx/modnfm/nfmmod.h b/plugins/channeltx/modnfm/nfmmod.h index 8a42d7a8b..7a757b4b2 100644 --- a/plugins/channeltx/modnfm/nfmmod.h +++ b/plugins/channeltx/modnfm/nfmmod.h @@ -17,11 +17,13 @@ #ifndef PLUGINS_CHANNELTX_MODNFM_NFMMOD_H_ #define PLUGINS_CHANNELTX_MODNFM_NFMMOD_H_ -#include #include #include #include +#include +#include + #include "dsp/basebandsamplesource.h" #include "channel/channelsourceapi.h" #include "dsp/nco.h" @@ -40,6 +42,8 @@ class DeviceSinkAPI; class ThreadedBasebandSampleSource; class UpChannelizer; +class QNetworkAccessManager; +class QNetworkReply; class NFMMod : public BasebandSampleSource, public ChannelSourceAPI { Q_OBJECT @@ -295,6 +299,10 @@ private: Real m_peakLevel; Real m_levelSum; CWKeyer m_cwKeyer; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + static const int m_levelNbSamples; void applyAudioSampleRate(int sampleRate); @@ -307,6 +315,11 @@ private: void seekFileStream(int seekPercentage); void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const NFMModSettings& settings); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + void webapiReverseSendSettings(QList& channelSettingsKeys, const NFMModSettings& settings, bool force); + void webapiReverseSendCWSettings(const CWKeyerSettings& settings); + +private slots: + void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index 37dcba948..506f3834b 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -296,12 +296,22 @@ void NFMModGUI::onWidgetRolled(QWidget* widget, bool rollDown) void NFMModGUI::onMenuDialogCalled(const QPoint &p) { BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); dialog.move(p); dialog.exec(); m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); setWindowTitle(m_settings.m_title); setTitleColor(m_settings.m_rgbColor); diff --git a/plugins/channeltx/modnfm/nfmmodsettings.cpp b/plugins/channeltx/modnfm/nfmmodsettings.cpp index 874c11a6e..7b1ae270e 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.cpp +++ b/plugins/channeltx/modnfm/nfmmodsettings.cpp @@ -59,6 +59,11 @@ void NFMModSettings::resetToDefaults() m_title = "NFM Modulator"; m_modAFInput = NFMModInputAF::NFMModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; } QByteArray NFMModSettings::serialize() const @@ -86,6 +91,11 @@ QByteArray NFMModSettings::serialize() const 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); return s.final(); } @@ -104,6 +114,7 @@ bool NFMModSettings::deserialize(const QByteArray& data) { QByteArray bytetmp; qint32 tmp; + uint32_t utmp; d.readS32(1, &tmp, 0); m_inputFrequencyOffset = tmp; @@ -138,6 +149,21 @@ bool NFMModSettings::deserialize(const QByteArray& data) 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; + return true; } else diff --git a/plugins/channeltx/modnfm/nfmmodsettings.h b/plugins/channeltx/modnfm/nfmmodsettings.h index 83f264be8..8826641d2 100644 --- a/plugins/channeltx/modnfm/nfmmodsettings.h +++ b/plugins/channeltx/modnfm/nfmmodsettings.h @@ -51,6 +51,11 @@ struct NFMModSettings QString m_title; NFMModInputAF m_modAFInput; QString m_audioDeviceName; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; Serializable *m_channelMarker; Serializable *m_cwKeyerGUI;