SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
wfmmod.cpp
Go to the documentation of this file.
1 // Copyright (C) 2016 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #include <QTime>
19 #include <QDebug>
20 #include <QMutexLocker>
21 #include <QNetworkAccessManager>
22 #include <QNetworkReply>
23 #include <QBuffer>
24 
25 #include <stdio.h>
26 #include <complex.h>
27 #include <algorithm>
28 
29 #include "SWGChannelSettings.h"
30 #include "SWGChannelReport.h"
31 #include "SWGAMModReport.h"
32 
33 #include "dsp/upchannelizer.h"
34 #include "dsp/dspengine.h"
36 #include "dsp/dspcommands.h"
37 #include "device/deviceapi.h"
38 #include "util/db.h"
39 
40 #include "wfmmod.h"
41 
49 
50 const QString WFMMod::m_channelIdURI = "sdrangel.channeltx.modwfm";
51 const QString WFMMod::m_channelId = "WFMMod";
52 const int WFMMod::m_levelNbSamples = 480; // every 10ms
53 const int WFMMod::m_rfFilterFFTLength = 1024;
54 
56  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
57  m_deviceAPI(deviceAPI),
58  m_basebandSampleRate(384000),
59  m_outputSampleRate(384000),
60  m_inputFrequencyOffset(0),
61  m_modPhasor(0.0f),
62  m_audioFifo(4800),
63  m_settingsMutex(QMutex::Recursive),
64  m_fileSize(0),
65  m_recordLength(0),
66  m_sampleRate(48000),
67  m_levelCalcCount(0),
68  m_peakLevel(0.0f),
69  m_levelSum(0.0f)
70 {
71  setObjectName(m_channelId);
72 
73  m_rfFilter = new fftfilt(-62500.0 / 384000.0, 62500.0 / 384000.0, m_rfFilterFFTLength);
76  //memset(m_rfFilterBuffer, 0, sizeof(Complex)*(m_rfFilterFFTLength));
78 
79  m_audioBuffer.resize(1<<14);
81 
82  m_magsq = 0.0;
83 
86 
89  m_cwKeyer.reset();
90 
93 
94  m_channelizer = new UpChannelizer(this);
98 
99  m_networkManager = new QNetworkAccessManager();
100  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
101 }
102 
104 {
105  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
106  delete m_networkManager;
110  delete m_threadedChannelizer;
111  delete m_channelizer;
112  delete m_rfFilter;
113  delete[] m_rfFilterBuffer;
114 }
115 
116 void WFMMod::pull(Sample& sample)
117 {
119  {
120  sample.m_real = 0.0f;
121  sample.m_imag = 0.0f;
122  return;
123  }
124 
125  Complex ci, ri;
126  fftfilt::cmplx *rf;
127  int rf_out;
128 
129  m_settingsMutex.lock();
130 
133  {
135  {
137  calculateLevel(m_modSample.real());
139  }
140 
142  }
143  else
144  {
145  pullAF(ri);
146  }
147 
148  m_modPhasor += (m_settings.m_fmDeviation / (float) m_outputSampleRate) * ri.real() * M_PI * 2.0f;
149  ci.real(cos(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); // -1 dB
150  ci.imag(sin(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF);
151 
152  // RF filtering
153  rf_out = m_rfFilter->runFilt(ci, &rf);
154 
155  if (rf_out > 0)
156  {
157  memcpy((void *) m_rfFilterBuffer, (const void *) rf, rf_out*sizeof(Complex));
159 
160  }
161 
162  ci = m_rfFilterBuffer[m_rfFilterBufferIndex] * m_carrierNco.nextIQ(); // shift to carrier frequency
164 
165  m_settingsMutex.unlock();
166 
167  double magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
168  magsq /= (SDR_TX_SCALED*SDR_TX_SCALED);
169  m_movingAverage(magsq);
171 
172  sample.m_real = (FixReal) ci.real();
173  sample.m_imag = (FixReal) ci.imag();
174 }
175 
176 void WFMMod::pullAudio(int nbSamples)
177 {
178  unsigned int nbSamplesAudio = nbSamples * ((Real) m_audioSampleRate / (Real) m_basebandSampleRate);
179 
180  if (nbSamplesAudio > m_audioBuffer.size())
181  {
182  m_audioBuffer.resize(nbSamplesAudio);
183  }
184 
185  m_audioFifo.read(reinterpret_cast<quint8*>(&m_audioBuffer[0]), nbSamplesAudio);
186  m_audioBufferFill = 0;
187 }
188 
189 void WFMMod::pullAF(Complex& sample)
190 {
191  switch (m_settings.m_modAFInput)
192  {
194  sample.real(m_toneNcoRF.next() * m_settings.m_volumeFactor);
195  sample.imag(0.0f);
196  break;
198  // sox f4exb_call.wav --encoding float --endian little f4exb_call.raw
199  // ffplay -f f32le -ar 48k -ac 1 f4exb_call.raw
200  if (m_ifstream.is_open())
201  {
202  if (m_ifstream.eof())
203  {
205  {
206  m_ifstream.clear();
207  m_ifstream.seekg(0, std::ios::beg);
208  }
209  }
210 
211  if (m_ifstream.eof())
212  {
213  sample.real(0.0f);
214  sample.imag(0.0f);
215  }
216  else
217  {
218  Real s;
219  m_ifstream.read(reinterpret_cast<char*>(&s), sizeof(Real));
220  sample.real(s * m_settings.m_volumeFactor);
221  sample.imag(0.0f);
222  }
223  }
224  else
225  {
226  sample.real(0.0f);
227  sample.imag(0.0f);
228  }
229  break;
231  {
233  sample.imag(0.0f);
234  }
235  break;
237  Real fadeFactor;
238 
239  if (m_cwKeyer.getSample())
240  {
241  m_cwKeyer.getCWSmoother().getFadeSample(true, fadeFactor);
242  sample.real(m_toneNcoRF.next() * m_settings.m_volumeFactor * fadeFactor);
243  sample.imag(0.0f);
244  }
245  else
246  {
247  if (m_cwKeyer.getCWSmoother().getFadeSample(false, fadeFactor))
248  {
249  sample.real(m_toneNcoRF.next() * m_settings.m_volumeFactor * fadeFactor);
250  sample.imag(0.0f);
251  }
252  else
253  {
254  sample.real(0.0f);
255  sample.imag(0.0f);
257  }
258  }
259  break;
261  default:
262  sample.real(0.0f);
263  sample.imag(0.0f);
264  break;
265  }
266 }
267 
268 void WFMMod::calculateLevel(const Real& sample)
269 {
271  {
272  m_peakLevel = std::max(std::fabs(m_peakLevel), sample);
273  m_levelSum += sample * sample;
275  }
276  else
277  {
278  qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples);
279  //qDebug("WFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel);
280  emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples);
281  m_peakLevel = 0.0f;
282  m_levelSum = 0.0f;
283  m_levelCalcCount = 0;
284  }
285 }
286 
288 {
289  qDebug() << "WFMMod::start: m_outputSampleRate: " << m_outputSampleRate
290  << " m_inputFrequencyOffset: " << m_inputFrequencyOffset;
291 
292  m_audioFifo.clear();
293  applyChannelSettings(m_basebandSampleRate, m_outputSampleRate, m_inputFrequencyOffset, true);
294 }
295 
297 {
298 }
299 
301 {
303  {
305  qDebug() << "WFMMod::handleMessage: MsgChannelizerNotification";
306 
308 
309  return true;
310  }
311  else if (MsgConfigureChannelizer::match(cmd))
312  {
314  qDebug() << "WFMMod::handleMessage: MsgConfigureChannelizer:"
315  << " getSampleRate: " << cfg.getSampleRate()
316  << " getCenterFrequency: " << cfg.getCenterFrequency();
317 
319  cfg.getSampleRate(),
320  cfg.getCenterFrequency());
321 
322  return true;
323  }
324  else if (MsgConfigureWFMMod::match(cmd))
325  {
327  qDebug() << "NFWFMMod::handleMessage: MsgConfigureWFMMod";
328 
329  WFMModSettings settings = cfg.getSettings();
330 
331  applySettings(cfg.getSettings(), cfg.getForce());
332 
333  return true;
334  }
335  else if (MsgConfigureFileSourceName::match(cmd))
336  {
338  m_fileName = conf.getFileName();
339  openFileStream();
340  return true;
341  }
342  else if (MsgConfigureFileSourceSeek::match(cmd))
343  {
345  int seekPercentage = conf.getPercentage();
346  seekFileStream(seekPercentage);
347 
348  return true;
349  }
351  {
352  std::size_t samplesCount;
353 
354  if (m_ifstream.eof()) {
355  samplesCount = m_fileSize / sizeof(Real);
356  } else {
357  samplesCount = m_ifstream.tellg() / sizeof(Real);
358  }
359 
361  report = MsgReportFileSourceStreamTiming::create(samplesCount);
362  getMessageQueueToGUI()->push(report);
363 
364  return true;
365  }
367  {
369 
372  }
373 
374  return true;
375  }
376  else if (DSPConfigureAudio::match(cmd))
377  {
378  DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd;
379  uint32_t sampleRate = cfg.getSampleRate();
380 
381  qDebug() << "WFMMod::handleMessage: DSPConfigureAudio:"
382  << " sampleRate: " << sampleRate;
383 
384  if (sampleRate != m_audioSampleRate) {
385  applyAudioSampleRate(sampleRate);
386  }
387 
388  return true;
389  }
390  else if (DSPSignalNotification::match(cmd))
391  {
392  return true;
393  }
394  else
395  {
396  return false;
397  }
398 }
399 
401 {
402  if (m_ifstream.is_open()) {
403  m_ifstream.close();
404  }
405 
406  m_ifstream.open(m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate);
407  m_fileSize = m_ifstream.tellg();
408  m_ifstream.seekg(0,std::ios_base::beg);
409 
410  m_sampleRate = 48000; // fixed rate
411  m_recordLength = m_fileSize / (sizeof(Real) * m_sampleRate);
412 
413  qDebug() << "WFMMod::openFileStream: " << m_fileName.toStdString().c_str()
414  << " fileSize: " << m_fileSize << "bytes"
415  << " length: " << m_recordLength << " seconds";
416 
418  report = MsgReportFileSourceStreamData::create(m_sampleRate, m_recordLength);
419  getMessageQueueToGUI()->push(report);
420 }
421 
422 void WFMMod::seekFileStream(int seekPercentage)
423 {
424  QMutexLocker mutexLocker(&m_settingsMutex);
425 
426  if (m_ifstream.is_open())
427  {
428  int seekPoint = ((m_recordLength * seekPercentage) / 100) * m_sampleRate;
429  seekPoint *= sizeof(Real);
430  m_ifstream.clear();
431  m_ifstream.seekg(seekPoint, std::ios::beg);
432  }
433 }
434 
435 void WFMMod::applyAudioSampleRate(int sampleRate)
436 {
437  qDebug("WFMMod::applyAudioSampleRate: %d", sampleRate);
438 
439  m_settingsMutex.lock();
441  m_interpolatorConsumed = false;
443  m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0);
444  m_settingsMutex.unlock();
445 
446  m_audioSampleRate = sampleRate;
447 }
448 
449 void WFMMod::applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force)
450 {
451  qDebug() << "WFMMod::applyChannelSettings:"
452  << " basebandSampleRate: " << basebandSampleRate
453  << " outputSampleRate: " << outputSampleRate
454  << " inputFrequencyOffset: " << inputFrequencyOffset;
455 
456  if ((inputFrequencyOffset != m_inputFrequencyOffset) ||
457  (outputSampleRate != m_outputSampleRate) || force)
458  {
459  m_settingsMutex.lock();
460  m_carrierNco.setFreq(inputFrequencyOffset, outputSampleRate);
461  m_settingsMutex.unlock();
462  }
463 
464  if ((outputSampleRate != m_outputSampleRate) || force)
465  {
466  m_settingsMutex.lock();
468  m_interpolatorConsumed = false;
469  m_interpolatorDistance = (Real) m_audioSampleRate / (Real) outputSampleRate;
471  Real lowCut = -(m_settings.m_rfBandwidth / 2.0) / outputSampleRate;
472  Real hiCut = (m_settings.m_rfBandwidth / 2.0) / outputSampleRate;
473  m_rfFilter->create_filter(lowCut, hiCut);
474  m_toneNcoRF.setFreq(m_settings.m_toneFrequency, outputSampleRate);
475  m_cwKeyer.setSampleRate(outputSampleRate);
476  m_cwKeyer.reset();
477  m_settingsMutex.unlock();
478  }
479 
480  m_basebandSampleRate = basebandSampleRate;
481  m_outputSampleRate = outputSampleRate;
482  m_inputFrequencyOffset = inputFrequencyOffset;
483 }
484 
485 void WFMMod::applySettings(const WFMModSettings& settings, bool force)
486 {
487  qDebug() << "WFMMod::applySettings:"
488  << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
489  << " m_rfBandwidth: " << settings.m_rfBandwidth
490  << " m_afBandwidth: " << settings.m_afBandwidth
491  << " m_fmDeviation: " << settings.m_fmDeviation
492  << " m_volumeFactor: " << settings.m_volumeFactor
493  << " m_toneFrequency: " << settings.m_toneFrequency
494  << " m_channelMute: " << settings.m_channelMute
495  << " m_playLoop: " << settings.m_playLoop
496  << " m_modAFInput: " << settings.m_modAFInput
497  << " m_audioDeviceName: " << settings.m_audioDeviceName
498  << " m_useReverseAPI: " << settings.m_useReverseAPI
499  << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
500  << " m_reverseAPIPort: " << settings.m_reverseAPIPort
501  << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
502  << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
503  << " force: " << force;
504 
505  QList<QString> reverseAPIKeys;
506 
507  if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
508  reverseAPIKeys.append("inputFrequencyOffset");
509  }
510  if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) {
511  reverseAPIKeys.append("fmDeviation");
512  }
513  if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) {
514  reverseAPIKeys.append("volumeFactor");
515  }
516  if ((settings.m_channelMute != m_settings.m_channelMute) || force) {
517  reverseAPIKeys.append("channelMute");
518  }
519  if ((settings.m_playLoop != m_settings.m_playLoop) || force) {
520  reverseAPIKeys.append("playLoop");
521  }
522  if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) {
523  reverseAPIKeys.append("modAFInput");
524  }
525 
526  if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force)
527  {
528  reverseAPIKeys.append("afBandwidth");
529  m_settingsMutex.lock();
531  m_interpolatorConsumed = false;
533  m_interpolator.create(48, m_audioSampleRate, settings.m_rfBandwidth / 2.2, 3.0);
534  m_settingsMutex.unlock();
535  }
536 
537  if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
538  {
539  reverseAPIKeys.append("rfBandwidth");
540  m_settingsMutex.lock();
541  Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_outputSampleRate;
542  Real hiCut = (settings.m_rfBandwidth / 2.0) / m_outputSampleRate;
543  m_rfFilter->create_filter(lowCut, hiCut);
544  m_settingsMutex.unlock();
545  }
546 
547  if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force)
548  {
549  reverseAPIKeys.append("toneFrequency");
550  m_settingsMutex.lock();
552  m_settingsMutex.unlock();
553  }
554 
555  if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
556  {
557  reverseAPIKeys.append("audioDeviceName");
559  int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName);
560  audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex);
561  uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex);
562 
563  if (m_audioSampleRate != audioSampleRate) {
564  applyAudioSampleRate(audioSampleRate);
565  }
566  }
567 
568  if (settings.m_useReverseAPI)
569  {
570  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
575  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
576  }
577 
578  m_settings = settings;
579 }
580 
581 QByteArray WFMMod::serialize() const
582 {
583  return m_settings.serialize();
584 }
585 
586 bool WFMMod::deserialize(const QByteArray& data)
587 {
588  if (m_settings.deserialize(data))
589  {
592  return true;
593  }
594  else
595  {
599  return false;
600  }
601 }
602 
605  QString& errorMessage)
606 {
607  (void) errorMessage;
609  response.getWfmModSettings()->init();
611  return 200;
612 }
613 
615  bool force,
616  const QStringList& channelSettingsKeys,
618  QString& errorMessage)
619 {
620  (void) errorMessage;
621  WFMModSettings settings = m_settings;
622  bool channelizerChange = false;
623 
624  if (channelSettingsKeys.contains("channelMute")) {
625  settings.m_channelMute = response.getWfmModSettings()->getChannelMute() != 0;
626  }
627  if (channelSettingsKeys.contains("inputFrequencyOffset"))
628  {
630  channelizerChange = true;
631  }
632  if (channelSettingsKeys.contains("modAFInput")) {
634  }
635  if (channelSettingsKeys.contains("playLoop")) {
636  settings.m_playLoop = response.getWfmModSettings()->getPlayLoop() != 0;
637  }
638  if (channelSettingsKeys.contains("rfBandwidth")) {
639  settings.m_rfBandwidth = response.getWfmModSettings()->getRfBandwidth();
640  channelizerChange = true;
641  }
642  if (channelSettingsKeys.contains("afBandwidth")) {
643  settings.m_afBandwidth = response.getWfmModSettings()->getAfBandwidth();
644  }
645  if (channelSettingsKeys.contains("rgbColor")) {
646  settings.m_rgbColor = response.getWfmModSettings()->getRgbColor();
647  }
648  if (channelSettingsKeys.contains("title")) {
649  settings.m_title = *response.getWfmModSettings()->getTitle();
650  }
651  if (channelSettingsKeys.contains("toneFrequency")) {
652  settings.m_toneFrequency = response.getWfmModSettings()->getToneFrequency();
653  }
654  if (channelSettingsKeys.contains("volumeFactor")) {
655  settings.m_volumeFactor = response.getWfmModSettings()->getVolumeFactor();
656  }
657  if (channelSettingsKeys.contains("fmDeviation")) {
658  settings.m_fmDeviation = response.getWfmModSettings()->getFmDeviation();
659  }
660  if (channelSettingsKeys.contains("useReverseAPI")) {
661  settings.m_useReverseAPI = response.getWfmModSettings()->getUseReverseApi() != 0;
662  }
663  if (channelSettingsKeys.contains("reverseAPIAddress")) {
665  }
666  if (channelSettingsKeys.contains("reverseAPIPort")) {
667  settings.m_reverseAPIPort = response.getWfmModSettings()->getReverseApiPort();
668  }
669  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
671  }
672  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
674  }
675 
676  if (channelSettingsKeys.contains("cwKeyer"))
677  {
678  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getWfmModSettings()->getCwKeyer();
679  CWKeyerSettings cwKeyerSettings = m_cwKeyer.getSettings();
680  m_cwKeyer.webapiSettingsPutPatch(channelSettingsKeys, cwKeyerSettings, apiCwKeyerSettings);
681 
682  CWKeyer::MsgConfigureCWKeyer *msgCwKeyer = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
683  m_cwKeyer.getInputMessageQueue()->push(msgCwKeyer);
684 
685  if (m_guiMessageQueue) // forward to GUI if any
686  {
687  CWKeyer::MsgConfigureCWKeyer *msgCwKeyerToGUI = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
688  m_guiMessageQueue->push(msgCwKeyerToGUI);
689  }
690  }
691 
692  if (channelizerChange)
693  {
695  settings.m_rfBandwidth, settings.m_inputFrequencyOffset);
696  m_inputMessageQueue.push(msgChan);
697  }
698 
699  MsgConfigureWFMMod *msg = MsgConfigureWFMMod::create(settings, force);
701 
702  if (m_guiMessageQueue) // forward to GUI if any
703  {
704  MsgConfigureWFMMod *msgToGUI = MsgConfigureWFMMod::create(settings, force);
705  m_guiMessageQueue->push(msgToGUI);
706  }
707 
708  webapiFormatChannelSettings(response, settings);
709 
710  return 200;
711 }
712 
715  QString& errorMessage)
716 {
717  (void) errorMessage;
719  response.getWfmModReport()->init();
720  webapiFormatChannelReport(response);
721  return 200;
722 }
723 
725 {
726  response.getWfmModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0);
728  response.getWfmModSettings()->setModAfInput((int) settings.m_modAFInput);
729  response.getWfmModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0);
730  response.getWfmModSettings()->setRfBandwidth(settings.m_rfBandwidth);
731  response.getWfmModSettings()->setAfBandwidth(settings.m_afBandwidth);
732  response.getWfmModSettings()->setFmDeviation(settings.m_fmDeviation);
733  response.getWfmModSettings()->setRgbColor(settings.m_rgbColor);
734 
735  if (response.getWfmModSettings()->getTitle()) {
736  *response.getWfmModSettings()->getTitle() = settings.m_title;
737  } else {
738  response.getWfmModSettings()->setTitle(new QString(settings.m_title));
739  }
740 
741  response.getWfmModSettings()->setToneFrequency(settings.m_toneFrequency);
742  response.getWfmModSettings()->setVolumeFactor(settings.m_volumeFactor);
743 
744  if (!response.getWfmModSettings()->getCwKeyer()) {
746  }
747 
748  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getWfmModSettings()->getCwKeyer();
749  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
750  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
751 
752  if (response.getWfmModSettings()->getAudioDeviceName()) {
753  *response.getWfmModSettings()->getAudioDeviceName() = settings.m_audioDeviceName;
754  } else {
755  response.getWfmModSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
756  }
757 
758  response.getWfmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
759 
760  if (response.getWfmModSettings()->getReverseApiAddress()) {
762  } else {
763  response.getWfmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
764  }
765 
769 }
770 
772 {
776 }
777 
778 void WFMMod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const WFMModSettings& settings, bool force)
779 {
781  swgChannelSettings->setDirection(1); // single source (Tx)
782  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
783  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
784  swgChannelSettings->setChannelType(new QString("WFMMod"));
785  swgChannelSettings->setWfmModSettings(new SWGSDRangel::SWGWFMModSettings());
786  SWGSDRangel::SWGWFMModSettings *swgWFMModSettings = swgChannelSettings->getWfmModSettings();
787 
788  // transfer data that has been modified. When force is on transfer all data except reverse API data
789 
790  if (channelSettingsKeys.contains("channelMute") || force) {
791  swgWFMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0);
792  }
793  if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
794  swgWFMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
795  }
796  if (channelSettingsKeys.contains("modAFInput") || force) {
797  swgWFMModSettings->setModAfInput((int) settings.m_modAFInput);
798  }
799  if (channelSettingsKeys.contains("playLoop") || force) {
800  swgWFMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0);
801  }
802  if (channelSettingsKeys.contains("rfBandwidth") || force) {
803  swgWFMModSettings->setRfBandwidth(settings.m_rfBandwidth);
804  }
805  if (channelSettingsKeys.contains("afBandwidth") || force) {
806  swgWFMModSettings->setAfBandwidth(settings.m_afBandwidth);
807  }
808  if (channelSettingsKeys.contains("rgbColor") || force) {
809  swgWFMModSettings->setRgbColor(settings.m_rgbColor);
810  }
811  if (channelSettingsKeys.contains("title") || force) {
812  swgWFMModSettings->setTitle(new QString(settings.m_title));
813  }
814  if (channelSettingsKeys.contains("toneFrequency") || force) {
815  swgWFMModSettings->setToneFrequency(settings.m_toneFrequency);
816  }
817  if (channelSettingsKeys.contains("volumeFactor") || force) {
818  swgWFMModSettings->setVolumeFactor(settings.m_volumeFactor);
819  }
820  if (channelSettingsKeys.contains("fmDeviation")) {
821  swgWFMModSettings->setFmDeviation(settings.m_fmDeviation);
822  }
823  if (channelSettingsKeys.contains("audioDeviceName") || force) {
824  swgWFMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
825  }
826 
827  if (force)
828  {
829  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
830  swgWFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
831  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgWFMModSettings->getCwKeyer();
832  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
833  }
834 
835  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
836  .arg(settings.m_reverseAPIAddress)
837  .arg(settings.m_reverseAPIPort)
838  .arg(settings.m_reverseAPIDeviceIndex)
839  .arg(settings.m_reverseAPIChannelIndex);
840  m_networkRequest.setUrl(QUrl(channelSettingsURL));
841  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
842 
843  QBuffer *buffer=new QBuffer();
844  buffer->open((QBuffer::ReadWrite));
845  buffer->write(swgChannelSettings->asJson().toUtf8());
846  buffer->seek(0);
847 
848  // Always use PATCH to avoid passing reverse API settings
849  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
850 
851  delete swgChannelSettings;
852 }
853 
855 {
857  swgChannelSettings->setDirection(1); // single source (Tx)
858  swgChannelSettings->setChannelType(new QString("WFMMod"));
859  swgChannelSettings->setWfmModSettings(new SWGSDRangel::SWGWFMModSettings());
860  SWGSDRangel::SWGWFMModSettings *swgWFMModSettings = swgChannelSettings->getWfmModSettings();
861 
862  swgWFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
863  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgWFMModSettings->getCwKeyer();
864  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
865 
866  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
871  m_networkRequest.setUrl(QUrl(channelSettingsURL));
872  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
873 
874  QBuffer *buffer=new QBuffer();
875  buffer->open((QBuffer::ReadWrite));
876  buffer->write(swgChannelSettings->asJson().toUtf8());
877  buffer->seek(0);
878 
879  // Always use PATCH to avoid passing reverse API settings
880  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
881 
882  delete swgChannelSettings;
883 }
884 
885 void WFMMod::networkManagerFinished(QNetworkReply *reply)
886 {
887  QNetworkReply::NetworkError replyError = reply->error();
888 
889  if (replyError)
890  {
891  qWarning() << "WFMMod::networkManagerFinished:"
892  << " error(" << (int) replyError
893  << "): " << replyError
894  << ": " << reply->errorString();
895  return;
896  }
897 
898  QString answer = reply->readAll();
899  answer.chop(1); // remove last \n
900  qDebug("WFMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
901 }
double m_magsq
Definition: wfmmod.h:292
ThreadedBasebandSampleSource * m_threadedChannelizer
Definition: wfmmod.h:270
void setOriginatorChannelIndex(qint32 originator_channel_index)
static const int m_levelNbSamples
Definition: wfmmod.h:317
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
UpChannelizer * m_channelizer
Definition: wfmmod.h:271
CWKeyer m_cwKeyer
Definition: wfmmod.h:312
Complex nextIQ()
Return next complex sample.
Definition: nco.cpp:61
void push(Message *message, bool emitSignal=true)
Push message onto queue.
Fixed< IntType, IntBits > cos(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2271
uint16_t m_reverseAPIChannelIndex
void removeChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:181
static double dbPower(double magsq, double floor=1e-12)
Definition: db.cpp:22
QNetworkAccessManager * m_networkManager
Definition: wfmmod.h:314
DeviceAPI * m_deviceAPI
Definition: wfmmod.h:269
int getInputSampleRate(int inputDeviceIndex=-1)
void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase=4.5)
bool interpolate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:53
fftfilt::cmplx * m_rfFilterBuffer
Definition: wfmmod.h:289
void setRfBandwidth(float rf_bandwidth)
NCOF m_toneNcoRF
Definition: wfmmod.h:279
Complex m_modSample
Definition: wfmmod.h:281
void clear()
Definition: audiofifo.cpp:156
fftfilt * m_rfFilter
Definition: wfmmod.h:287
int getDeviceSetIndex() const
Definition: channelapi.h:89
void applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force=false)
Definition: wfmmod.cpp:449
void setWfmModSettings(SWGWFMModSettings *wfm_mod_settings)
int m_sampleRate
Definition: wfmmod.h:307
void calculateLevel(const Real &sample)
Definition: wfmmod.cpp:268
void seekFileStream(int seekPercentage)
Definition: wfmmod.cpp:422
static MsgConfigureCWKeyer * create(const CWKeyerSettings &settings, bool force)
Definition: cwkeyer.h:63
void removeChannelSource(ThreadedBasebandSampleSource *sink, int streamIndex=0)
Remove a channel source (Tx)
Definition: deviceapi.cpp:147
int runFilt(const cmplx &in, cmplx **out)
Definition: fftfilt.cpp:260
bool m_interpolatorConsumed
Definition: wfmmod.h:285
Real m_interpolatorDistance
Definition: wfmmod.h:283
virtual void pullAudio(int nbSamples)
Definition: wfmmod.cpp:176
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
WFMModInputAF m_modAFInput
static MsgReportFileSourceStreamData * create(int sampleRate, quint32 recordLength)
Definition: wfmmod.h:185
void pullAF(Complex &sample)
Definition: wfmmod.cpp:189
WFMMod(DeviceAPI *deviceAPI)
Definition: wfmmod.cpp:55
#define M_PI
Definition: rdsdemod.cpp:27
static MsgConfigureChannelizer * create(int sampleRate, int centerFrequency)
Definition: wfmmod.h:82
void setChannelType(QString *channel_type)
void addChannelSource(ThreadedBasebandSampleSource *sink, int streamIndex=0)
Add a channel source (Tx)
Definition: deviceapi.cpp:138
void setChannelMute(qint32 channel_mute)
QString m_fileName
Definition: wfmmod.h:304
void setOriginatorDeviceSetIndex(qint32 originator_device_set_index)
AudioVector m_audioBuffer
Definition: wfmmod.h:296
static const QString m_channelIdURI
Definition: wfmmod.h:250
quint32 m_rgbColor
std::complex< float > cmplx
Definition: fftfilt.h:21
static MsgConfigureWFMMod * create(const WFMModSettings &settings, bool force)
Definition: wfmmod.h:59
unsigned int uint32_t
Definition: rtptypes_win.h:46
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void addAudioSource(AudioFifo *audioFifo, MessageQueue *sampleSourceMessageQueue, int inputDeviceIndex=-1)
Add an audio source.
void setUseReverseApi(qint32 use_reverse_api)
virtual void pull(Sample &sample)
Definition: wfmmod.cpp:116
quint64 m_fileSize
raw file size (bytes)
Definition: wfmmod.h:305
quint32 m_levelCalcCount
Definition: wfmmod.h:309
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
Definition: cwkeyer.h:104
void setPhase(Real phase)
Definition: ncof.h:42
void openFileStream()
Definition: wfmmod.cpp:400
double getMagSq() const
Definition: wfmmod.h:246
static const QString m_channelId
Definition: wfmmod.h:251
#define SDR_TX_SCALEF
Definition: dsptypes.h:39
bool deserialize(const QByteArray &data)
SWGCWKeyerSettings * getCwKeyer()
void setReverseApiPort(qint32 reverse_api_port)
virtual void stop()
Definition: wfmmod.cpp:296
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
bool getFadeSample(bool on, float &sample)
Definition: cwkeyer.cpp:487
void setInputFrequencyOffset(qint64 input_frequency_offset)
void create_filter(float f1, float f2)
Definition: fftfilt.cpp:107
int getSampleRate() const
Definition: dspcommands.h:390
void setPlayLoop(qint32 play_loop)
static DSPEngine * instance()
Definition: dspengine.cpp:51
CWSmoother & getCWSmoother()
Definition: cwkeyer.h:111
virtual void start()
Definition: wfmmod.cpp:287
static MsgReportFileSourceStreamTiming * create(std::size_t samplesCount)
Definition: wfmmod.h:164
int m_rfFilterBufferIndex
Definition: wfmmod.h:290
FixReal m_real
Definition: dsptypes.h:64
void setWfmModReport(SWGWFMModReport *wfm_mod_report)
QNetworkRequest m_networkRequest
Definition: wfmmod.h:315
void setAudioDeviceName(QString *audio_device_name)
Fixed< IntType, IntBits > sin(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2265
QString m_audioDeviceName
This is the audio device you get the audio samples from.
void setCwKeyer(SWGCWKeyerSettings *cw_keyer)
static bool match(const Message *message)
Definition: message.cpp:45
Real m_peakLevel
Definition: wfmmod.h:310
void setChannelPowerDb(float channel_power_db)
void setFreq(Real freq, Real sampleRate)
Definition: nco.cpp:49
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
Definition: wfmmod.cpp:300
quint32 m_audioSampleRate
Definition: wfmmod.h:295
~WFMMod()
Definition: wfmmod.cpp:103
void applySettings(const WFMModSettings &settings, bool force=false)
Definition: wfmmod.cpp:485
Fixed< IntType, IntBits > sqrt(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2283
SWGWFMModReport * getWfmModReport()
uint32_t read(quint8 *data, uint32_t numSamples)
Definition: audiofifo.cpp:103
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
AudioFifo m_audioFifo
Definition: wfmmod.h:298
virtual QByteArray serialize() const
Definition: wfmmod.cpp:581
virtual int webapiReportGet(SWGSDRangel::SWGChannelReport &response, QString &errorMessage)
Definition: wfmmod.cpp:713
virtual QString asJson() override
const QString & getFileName() const
Definition: wfmmod.h:103
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: wfmmod.cpp:603
static void webapiFormatChannelSettings(SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings, const CWKeyerSettings &cwKeyerSettings)
Definition: cwkeyer.cpp:637
MovingAverageUtil< double, double, 16 > m_movingAverage
Definition: wfmmod.h:293
int getSample()
Definition: cwkeyer.cpp:194
int getCenterFrequency() const
Definition: wfmmod.h:80
AudioDeviceManager * getAudioDeviceManager()
Definition: dspengine.h:55
SWGWFMModSettings * getWfmModSettings()
std::ifstream m_ifstream
Definition: wfmmod.h:303
void setReverseApiAddress(QString *reverse_api_address)
QByteArray serialize() const
uint16_t m_reverseAPIPort
static const int m_rfFilterFFTLength
Definition: wfmmod.h:288
bool getForce() const
Definition: wfmmod.h:57
MessageQueue * getMessageQueueToGUI()
static void webapiSettingsPutPatch(const QStringList &channelSettingsKeys, CWKeyerSettings &cwKeyerSettings, SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings)
Definition: cwkeyer.cpp:599
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
void setChannelSampleRate(qint32 channel_sample_rate)
void networkManagerFinished(QNetworkReply *reply)
Definition: wfmmod.cpp:885
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport &response)
Definition: wfmmod.cpp:771
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: wfmmod.cpp:614
Real m_interpolatorDistanceRemain
Definition: wfmmod.h:284
FixReal m_imag
Definition: dsptypes.h:65
void setAudioSampleRate(qint32 audio_sample_rate)
int getInputDeviceIndex(const QString &deviceName) const
void setFmDeviation(float fm_deviation)
quint32 m_recordLength
record length in seconds computed from file size
Definition: wfmmod.h:306
void addChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:174
Real m_levelSum
Definition: wfmmod.h:311
void applyAudioSampleRate(int sampleRate)
Definition: wfmmod.cpp:435
QString m_reverseAPIAddress
int m_basebandSampleRate
Definition: wfmmod.h:273
void setAfBandwidth(float af_bandwidth)
WFMModSettings m_settings
Definition: wfmmod.h:276
int m_outputSampleRate
Definition: wfmmod.h:274
void reset()
Definition: cwkeyer.h:109
Interpolator m_interpolator
Definition: wfmmod.h:282
const CWKeyerSettings & getSettings() const
Definition: cwkeyer.h:107
int getIndexInDeviceSet() const
Definition: channelapi.h:87
const WFMModSettings & getSettings() const
Definition: wfmmod.h:56
Real next()
Return next real sample.
Definition: ncof.cpp:57
std::complex< Real > Complex
Definition: dsptypes.h:43
double asDouble() const
Definition: movingaverage.h:57
float m_modPhasor
baseband modulator phasor
Definition: wfmmod.h:280
void webapiReverseSendCWSettings(const CWKeyerSettings &settings)
Definition: wfmmod.cpp:854
float Real
Definition: dsptypes.h:42
#define SDR_TX_SCALED
Definition: dsptypes.h:40
void setToneFrequency(float tone_frequency)
NCO m_carrierNco
Definition: wfmmod.h:278
void setModAfInput(qint32 mod_af_input)
void setSampleRate(int sampleRate)
Definition: cwkeyer.cpp:186
qint64 m_inputFrequencyOffset
QMutex m_settingsMutex
Definition: wfmmod.h:301
int m_inputFrequencyOffset
Definition: wfmmod.h:275
uint16_t m_reverseAPIDeviceIndex
qint16 FixReal
Definition: dsptypes.h:35
virtual bool deserialize(const QByteArray &data)
Definition: wfmmod.cpp:586
T max(const T &x, const T &y)
Definition: framework.h:446
const CWKeyerSettings & getSettings() const
Definition: cwkeyer.h:60
void setVolumeFactor(float volume_factor)
void removeAudioSource(AudioFifo *audioFifo)
Remove an audio source.
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const WFMModSettings &settings)
Definition: wfmmod.cpp:724
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const WFMModSettings &settings, bool force)
Definition: wfmmod.cpp:778
void setRgbColor(qint32 rgb_color)
uint m_audioBufferFill
Definition: wfmmod.h:297
void configure(MessageQueue *messageQueue, int sampleRate, int centerFrequency)