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.
nfmmod.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 "SWGChannelSettings.h"
26 #include "SWGCWKeyerSettings.h"
27 #include "SWGChannelReport.h"
28 #include "SWGNFMModReport.h"
29 
30 #include <stdio.h>
31 #include <complex.h>
32 #include <algorithm>
33 
34 #include "dsp/upchannelizer.h"
35 #include "dsp/dspengine.h"
36 #include "dsp/dspcommands.h"
37 #include "device/deviceapi.h"
39 #include "util/db.h"
40 
41 #include "nfmmod.h"
42 
50 
51 const QString NFMMod::m_channelIdURI = "sdrangel.channeltx.modnfm";
52 const QString NFMMod::m_channelId = "NFMMod";
53 const int NFMMod::m_levelNbSamples = 480; // every 10ms
54 
56  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
57  m_deviceAPI(deviceAPI),
58  m_basebandSampleRate(48000),
59  m_outputSampleRate(48000),
60  m_inputFrequencyOffset(0),
61  m_modPhasor(0.0f),
62  m_audioFifo(4800),
63  m_feedbackAudioFifo(48000),
64  m_settingsMutex(QMutex::Recursive),
65  m_fileSize(0),
66  m_recordLength(0),
67  m_sampleRate(48000),
68  m_levelCalcCount(0),
69  m_peakLevel(0.0f),
70  m_levelSum(0.0f)
71 {
72  setObjectName(m_channelId);
73 
74  m_audioBuffer.resize(1<<14);
76 
77  m_feedbackAudioBuffer.resize(1<<14);
79 
80  m_magsq = 0.0;
81 
84 
88 
89  m_lowpass.create(301, m_audioSampleRate, 250.0);
93  m_cwKeyer.reset();
94 
97 
98  m_channelizer = new UpChannelizer(this);
102 
103  m_networkManager = new QNetworkAccessManager();
104  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
105 }
106 
108 {
109  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
110  delete m_networkManager;
115  delete m_threadedChannelizer;
116  delete m_channelizer;
117 }
118 
119 void NFMMod::pull(Sample& sample)
120 {
122  {
123  sample.m_real = 0.0f;
124  sample.m_imag = 0.0f;
125  return;
126  }
127 
128  Complex ci;
129 
130  m_settingsMutex.lock();
131 
132  if (m_interpolatorDistance > 1.0f) // decimate
133  {
134  modulateSample();
135 
137  {
138  modulateSample();
139  }
140  }
141  else
142  {
144  {
145  modulateSample();
146  }
147  }
148 
150 
151  ci *= m_carrierNco.nextIQ(); // shift to carrier frequency
152 
153  m_settingsMutex.unlock();
154 
155  double magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
156  magsq /= (SDR_TX_SCALED*SDR_TX_SCALED);
157  m_movingAverage(magsq);
159 
160  sample.m_real = (FixReal) ci.real();
161  sample.m_imag = (FixReal) ci.imag();
162 }
163 
164 void NFMMod::pullAudio(int nbSamples)
165 {
166  unsigned int nbSamplesAudio = nbSamples * ((Real) m_audioSampleRate / (Real) m_basebandSampleRate);
167 
168  if (nbSamplesAudio > m_audioBuffer.size())
169  {
170  m_audioBuffer.resize(nbSamplesAudio);
171  }
172 
173  m_audioFifo.read(reinterpret_cast<quint8*>(&m_audioBuffer[0]), nbSamplesAudio);
174  m_audioBufferFill = 0;
175 }
176 
178 {
179  Real t;
180 
181  pullAF(t);
182 
185  }
186 
187  calculateLevel(t);
189 
190  if (m_settings.m_ctcssOn)
191  {
192  m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * (0.85f * m_bandpass.filter(t) + 0.15f * 378.0f * m_ctcssNco.next()) * (M_PI / 378.0f);
193  }
194  else
195  {
196  // 378 = 302 * 1.25; 302 = number of filter taps (established experimentally)
198  }
199 
200  m_modSample.real(cos(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); // -1 dB
201  m_modSample.imag(sin(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF);
202 }
203 
204 void NFMMod::pullAF(Real& sample)
205 {
206  switch (m_settings.m_modAFInput)
207  {
209  sample = m_toneNco.next();
210  break;
212  // sox f4exb_call.wav --encoding float --endian little f4exb_call.raw
213  // ffplay -f f32le -ar 48k -ac 1 f4exb_call.raw
214  if (m_ifstream.is_open())
215  {
216  if (m_ifstream.eof())
217  {
219  {
220  m_ifstream.clear();
221  m_ifstream.seekg(0, std::ios::beg);
222  }
223  }
224 
225  if (m_ifstream.eof())
226  {
227  sample = 0.0f;
228  }
229  else
230  {
231  m_ifstream.read(reinterpret_cast<char*>(&sample), sizeof(Real));
232  sample *= m_settings.m_volumeFactor;
233  }
234  }
235  else
236  {
237  sample = 0.0f;
238  }
239  break;
242  break;
244  Real fadeFactor;
245 
246  if (m_cwKeyer.getSample())
247  {
248  m_cwKeyer.getCWSmoother().getFadeSample(true, fadeFactor);
249  sample = m_toneNco.next() * fadeFactor;
250  }
251  else
252  {
253  if (m_cwKeyer.getCWSmoother().getFadeSample(false, fadeFactor))
254  {
255  sample = m_toneNco.next() * fadeFactor;
256  }
257  else
258  {
259  sample = 0.0f;
260  m_toneNco.setPhase(0);
261  }
262  }
263  break;
265  default:
266  sample = 0.0f;
267  break;
268  }
269 }
270 
272 {
273  Complex c(sample, sample);
274  Complex ci;
275 
276  if (m_feedbackInterpolatorDistance < 1.0f) // interpolate
277  {
279  {
280  processOneSample(ci);
282  }
283  }
284  else // decimate
285  {
287  {
288  processOneSample(ci);
290  }
291  }
292 }
293 
295 {
299 
301  {
303 
304  if (res != m_feedbackAudioBufferFill)
305  {
306  qDebug("AMDemod::pushFeedback: %u/%u audio samples written m_feedbackInterpolatorDistance: %f",
309  }
310 
312  }
313 }
314 
316 {
318  {
319  m_peakLevel = std::max(std::fabs(m_peakLevel), sample);
320  m_levelSum += sample * sample;
322  }
323  else
324  {
325  qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples);
326  //qDebug("NFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel);
327  emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples);
328  m_peakLevel = 0.0f;
329  m_levelSum = 0.0f;
330  m_levelCalcCount = 0;
331  }
332 }
333 
335 {
336  qDebug() << "NFMMod::start: m_outputSampleRate: " << m_outputSampleRate
337  << " m_inputFrequencyOffset: " << m_inputFrequencyOffset;
338 
339  m_audioFifo.clear();
340  applyChannelSettings(m_basebandSampleRate, m_outputSampleRate, m_inputFrequencyOffset, true);
341 }
342 
344 {
345 }
346 
348 {
350  {
352  qDebug() << "NFMMod::handleMessage: UpChannelizer::MsgChannelizerNotification";
353 
355 
356  return true;
357  }
358  else if (MsgConfigureChannelizer::match(cmd))
359  {
361  qDebug() << "NFMMod::handleMessage: MsgConfigureChannelizer:"
362  << " getSampleRate: " << cfg.getSampleRate()
363  << " getCenterFrequency: " << cfg.getCenterFrequency();
364 
366  cfg.getSampleRate(),
367  cfg.getCenterFrequency());
368 
369  return true;
370  }
371  else if (MsgConfigureNFMMod::match(cmd))
372  {
374  qDebug() << "NFMMod::handleMessage: MsgConfigureNFMMod";
375 
376  applySettings(cfg.getSettings(), cfg.getForce());
377 
378  return true;
379  }
380  else if (MsgConfigureFileSourceName::match(cmd))
381  {
383  m_fileName = conf.getFileName();
384  openFileStream();
385  qDebug() << "NFMMod::handleMessage: MsgConfigureFileSourceName:"
386  << " m_fileName: " << m_fileName;
387  return true;
388  }
389  else if (MsgConfigureFileSourceSeek::match(cmd))
390  {
392  int seekPercentage = conf.getPercentage();
393  seekFileStream(seekPercentage);
394  qDebug() << "NFMMod::handleMessage: MsgConfigureFileSourceSeek:"
395  << " seekPercentage: " << seekPercentage;
396 
397  return true;
398  }
400  {
401  std::size_t samplesCount;
402 
403  if (m_ifstream.eof()) {
404  samplesCount = m_fileSize / sizeof(Real);
405  } else {
406  samplesCount = m_ifstream.tellg() / sizeof(Real);
407  }
408 
410  report = MsgReportFileSourceStreamTiming::create(samplesCount);
411  getMessageQueueToGUI()->push(report);
412 
413  return true;
414  }
416  {
418 
421  }
422 
423  return true;
424  }
425  else if (DSPConfigureAudio::match(cmd))
426  {
427  DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd;
428  uint32_t sampleRate = cfg.getSampleRate();
429  DSPConfigureAudio::AudioType audioType = cfg.getAudioType();
430 
431  qDebug() << "NFMMod::handleMessage: DSPConfigureAudio:"
432  << " sampleRate: " << sampleRate
433  << " audioType: " << audioType;
434 
435  if (audioType == DSPConfigureAudio::AudioInput)
436  {
437  if (sampleRate != m_audioSampleRate) {
438  applyAudioSampleRate(sampleRate);
439  }
440  }
441  else if (audioType == DSPConfigureAudio::AudioOutput)
442  {
443  if (sampleRate != m_audioSampleRate) {
444  applyFeedbackAudioSampleRate(sampleRate);
445  }
446  }
447 
448  return true;
449  }
450  else if (DSPSignalNotification::match(cmd))
451  {
452  return true;
453  }
454  else
455  {
456  return false;
457  }
458 }
459 
461 {
462  if (m_ifstream.is_open()) {
463  m_ifstream.close();
464  }
465 
466  m_ifstream.open(m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate);
467  m_fileSize = m_ifstream.tellg();
468  m_ifstream.seekg(0,std::ios_base::beg);
469 
470  m_sampleRate = 48000; // fixed rate
471  m_recordLength = m_fileSize / (sizeof(Real) * m_sampleRate);
472 
473  qDebug() << "NFMMod::openFileStream: " << m_fileName.toStdString().c_str()
474  << " fileSize: " << m_fileSize << "bytes"
475  << " length: " << m_recordLength << " seconds";
476 
478  report = MsgReportFileSourceStreamData::create(m_sampleRate, m_recordLength);
479  getMessageQueueToGUI()->push(report);
480 }
481 
482 void NFMMod::seekFileStream(int seekPercentage)
483 {
484  QMutexLocker mutexLocker(&m_settingsMutex);
485 
486  if (m_ifstream.is_open())
487  {
488  int seekPoint = ((m_recordLength * seekPercentage) / 100) * m_sampleRate;
489  seekPoint *= sizeof(Real);
490  m_ifstream.clear();
491  m_ifstream.seekg(seekPoint, std::ios::beg);
492  }
493 }
494 
495 void NFMMod::applyAudioSampleRate(int sampleRate)
496 {
497  qDebug("NFMMod::applyAudioSampleRate: %d", sampleRate);
498 
500  sampleRate, m_settings.m_inputFrequencyOffset);
501  m_inputMessageQueue.push(channelConfigMsg);
502 
503  m_settingsMutex.lock();
504 
506  m_interpolatorConsumed = false;
508  m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0);
509  m_lowpass.create(301, sampleRate, 250.0);
510  m_bandpass.create(301, sampleRate, 300.0, m_settings.m_afBandwidth);
513  m_cwKeyer.setSampleRate(sampleRate);
514 
515  m_settingsMutex.unlock();
516 
517  m_audioSampleRate = sampleRate;
519 }
520 
521 void NFMMod::applyFeedbackAudioSampleRate(unsigned int sampleRate)
522 {
523  qDebug("NFMMod::applyFeedbackAudioSampleRate: %u", sampleRate);
524 
525  m_settingsMutex.lock();
526 
530  Real cutoff = std::min(sampleRate, m_audioSampleRate) / 2.2f;
531  m_feedbackInterpolator.create(48, sampleRate, cutoff, 3.0);
532 
533  m_settingsMutex.unlock();
534 
535  m_feedbackAudioSampleRate = sampleRate;
536 }
537 
538 void NFMMod::applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force)
539 {
540  qDebug() << "NFMMod::applyChannelSettings:"
541  << " basebandSampleRate: " << basebandSampleRate
542  << " outputSampleRate: " << outputSampleRate
543  << " inputFrequencyOffset: " << inputFrequencyOffset;
544 
545  if ((inputFrequencyOffset != m_inputFrequencyOffset) ||
546  (outputSampleRate != m_outputSampleRate) || force)
547  {
548  m_settingsMutex.lock();
549  m_carrierNco.setFreq(inputFrequencyOffset, outputSampleRate);
550  m_settingsMutex.unlock();
551  }
552 
553  if ((outputSampleRate != m_outputSampleRate) || force)
554  {
555  m_settingsMutex.lock();
557  m_interpolatorConsumed = false;
558  m_interpolatorDistance = (Real) m_audioSampleRate / (Real) outputSampleRate;
560  m_settingsMutex.unlock();
561  }
562 
563  m_basebandSampleRate = basebandSampleRate;
564  m_outputSampleRate = outputSampleRate;
565  m_inputFrequencyOffset = inputFrequencyOffset;
566 }
567 
568 void NFMMod::applySettings(const NFMModSettings& settings, bool force)
569 {
570  qDebug() << "NFMMod::applySettings:"
571  << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
572  << " m_rfBandwidth: " << settings.m_rfBandwidth
573  << " m_afBandwidth: " << settings.m_afBandwidth
574  << " m_fmDeviation: " << settings.m_fmDeviation
575  << " m_volumeFactor: " << settings.m_volumeFactor
576  << " m_toneFrequency: " << settings.m_toneFrequency
577  << " m_ctcssIndex: " << settings.m_ctcssIndex
578  << " m_ctcssOn: " << settings.m_ctcssOn
579  << " m_channelMute: " << settings.m_channelMute
580  << " m_playLoop: " << settings.m_playLoop
581  << " m_modAFInput " << settings.m_modAFInput
582  << " m_audioDeviceName: " << settings.m_audioDeviceName
583  << " m_useReverseAPI: " << settings.m_useReverseAPI
584  << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
585  << " m_reverseAPIAddress: " << settings.m_reverseAPIPort
586  << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
587  << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
588  << " force: " << force;
589 
590  QList<QString> reverseAPIKeys;
591 
592  if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
593  reverseAPIKeys.append("inputFrequencyOffset");
594  }
595 
596  if ((settings.m_fmDeviation != m_settings.m_fmDeviation) || force) {
597  reverseAPIKeys.append("fmDeviation");
598  }
599 
600  if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) {
601  reverseAPIKeys.append("volumeFactor");
602  }
603 
604  if ((settings.m_ctcssOn != m_settings.m_ctcssOn) || force) {
605  reverseAPIKeys.append("ctcssOn");
606  }
607 
608  if ((settings.m_channelMute != m_settings.m_channelMute) || force) {
609  reverseAPIKeys.append("channelMute");
610  }
611 
612  if ((settings.m_playLoop != m_settings.m_playLoop) || force) {
613  reverseAPIKeys.append("playLoop");
614  }
615 
616  if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) {
617  reverseAPIKeys.append("modAFInput");
618  }
619 
620  if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
621  {
622  reverseAPIKeys.append("rfBandwidth");
623  m_settingsMutex.lock();
625  m_interpolatorConsumed = false;
627  m_interpolator.create(48, m_audioSampleRate, settings.m_rfBandwidth / 2.2, 3.0);
628  m_settingsMutex.unlock();
629  }
630 
631  if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force)
632  {
633  reverseAPIKeys.append("afBandwidth");
634  m_settingsMutex.lock();
635  m_lowpass.create(301, m_audioSampleRate, 250.0);
636  m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_afBandwidth);
637  m_settingsMutex.unlock();
638  }
639 
640  if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force)
641  {
642  reverseAPIKeys.append("toneFrequency");
643  m_settingsMutex.lock();
645  m_settingsMutex.unlock();
646  }
647 
648  if ((settings.m_ctcssIndex != m_settings.m_ctcssIndex) || force)
649  {
650  reverseAPIKeys.append("ctcssIndex");
651  m_settingsMutex.lock();
653  m_settingsMutex.unlock();
654  }
655 
656  if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
657  {
658  reverseAPIKeys.append("audioDeviceName");
660  int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName);
661  audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex);
662  uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex);
663 
664  if (m_audioSampleRate != audioSampleRate) {
665  reverseAPIKeys.append("audioSampleRate");
666  applyAudioSampleRate(audioSampleRate);
667  }
668  }
669 
671  {
672  reverseAPIKeys.append("feedbackAudioDeviceName");
674  int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName);
675  audioDeviceManager->addAudioSink(&m_feedbackAudioFifo, getInputMessageQueue(), audioDeviceIndex);
676  uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
677 
678  if (m_feedbackAudioSampleRate != audioSampleRate) {
679  reverseAPIKeys.append("feedbackAudioSampleRate");
680  applyFeedbackAudioSampleRate(audioSampleRate);
681  }
682  }
683 
684  if (settings.m_useReverseAPI)
685  {
686  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
691  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
692  }
693 
694  m_settings = settings;
695 }
696 
697 QByteArray NFMMod::serialize() const
698 {
699  return m_settings.serialize();
700 }
701 
702 bool NFMMod::deserialize(const QByteArray& data)
703 {
704  bool success = true;
705 
706  if (!m_settings.deserialize(data))
707  {
709  success = false;
710  }
711 
714  m_inputMessageQueue.push(msgChan);
715 
718 
719  return success;
720 }
721 
724  QString& errorMessage)
725 {
726  (void) errorMessage;
728  response.getNfmModSettings()->init();
730  return 200;
731 }
732 
734  bool force,
735  const QStringList& channelSettingsKeys,
737  QString& errorMessage)
738 {
739  (void) errorMessage;
740  NFMModSettings settings = m_settings;
741  bool frequencyOffsetChanged = false;
742 
743 // for (int i = 0; i < channelSettingsKeys.size(); i++) {
744 // qDebug("NFMMod::webapiSettingsPutPatch: settingKey: %s", qPrintable(channelSettingsKeys.at(i)));
745 // }
746 
747  if (channelSettingsKeys.contains("afBandwidth")) {
748  settings.m_afBandwidth = response.getNfmModSettings()->getAfBandwidth();
749  }
750  if (channelSettingsKeys.contains("channelMute")) {
751  settings.m_channelMute = response.getNfmModSettings()->getChannelMute() != 0;
752  }
753  if (channelSettingsKeys.contains("ctcssIndex")) {
754  settings.m_ctcssIndex = response.getNfmModSettings()->getCtcssIndex();
755  }
756  if (channelSettingsKeys.contains("ctcssOn")) {
757  settings.m_ctcssOn = response.getNfmModSettings()->getCtcssOn() != 0;
758  }
759  if (channelSettingsKeys.contains("fmDeviation")) {
760  settings.m_fmDeviation = response.getNfmModSettings()->getFmDeviation();
761  }
762  if (channelSettingsKeys.contains("inputFrequencyOffset"))
763  {
765  frequencyOffsetChanged = true;
766  }
767  if (channelSettingsKeys.contains("modAFInput")) {
769  }
770  if (channelSettingsKeys.contains("playLoop")) {
771  settings.m_playLoop = response.getNfmModSettings()->getPlayLoop() != 0;
772  }
773  if (channelSettingsKeys.contains("rfBandwidth")) {
774  settings.m_rfBandwidth = response.getNfmModSettings()->getRfBandwidth();
775  }
776  if (channelSettingsKeys.contains("rgbColor")) {
777  settings.m_rgbColor = response.getNfmModSettings()->getRgbColor();
778  }
779  if (channelSettingsKeys.contains("title")) {
780  settings.m_title = *response.getNfmModSettings()->getTitle();
781  }
782  if (channelSettingsKeys.contains("toneFrequency")) {
783  settings.m_toneFrequency = response.getNfmModSettings()->getToneFrequency();
784  }
785  if (channelSettingsKeys.contains("volumeFactor")) {
786  settings.m_volumeFactor = response.getNfmModSettings()->getVolumeFactor();
787  }
788  if (channelSettingsKeys.contains("useReverseAPI")) {
789  settings.m_useReverseAPI = response.getNfmModSettings()->getUseReverseApi() != 0;
790  }
791  if (channelSettingsKeys.contains("reverseAPIAddress")) {
793  }
794  if (channelSettingsKeys.contains("reverseAPIPort")) {
795  settings.m_reverseAPIPort = response.getNfmModSettings()->getReverseApiPort();
796  }
797  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
799  }
800  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
802  }
803 
804  if (channelSettingsKeys.contains("cwKeyer"))
805  {
806  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getNfmModSettings()->getCwKeyer();
807  CWKeyerSettings cwKeyerSettings = m_cwKeyer.getSettings();
808  m_cwKeyer.webapiSettingsPutPatch(channelSettingsKeys, cwKeyerSettings, apiCwKeyerSettings);
809 
810  CWKeyer::MsgConfigureCWKeyer *msgCwKeyer = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
811  m_cwKeyer.getInputMessageQueue()->push(msgCwKeyer);
812 
813  if (m_guiMessageQueue) // forward to GUI if any
814  {
815  CWKeyer::MsgConfigureCWKeyer *msgCwKeyerToGUI = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
816  m_guiMessageQueue->push(msgCwKeyerToGUI);
817  }
818  }
819 
820  if (frequencyOffsetChanged)
821  {
824  m_inputMessageQueue.push(msgChan);
825  }
826 
827  MsgConfigureNFMMod *msg = MsgConfigureNFMMod::create(settings, force);
829 
830  if (m_guiMessageQueue) // forward to GUI if any
831  {
832  MsgConfigureNFMMod *msgToGUI = MsgConfigureNFMMod::create(settings, force);
833  m_guiMessageQueue->push(msgToGUI);
834  }
835 
836  webapiFormatChannelSettings(response, settings);
837 
838  return 200;
839 }
840 
843  QString& errorMessage)
844 {
845  (void) errorMessage;
847  response.getNfmModReport()->init();
848  webapiFormatChannelReport(response);
849  return 200;
850 }
851 
853 {
854  response.getNfmModSettings()->setAfBandwidth(settings.m_afBandwidth);
855  response.getNfmModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0);
856  response.getNfmModSettings()->setCtcssIndex(settings.m_ctcssIndex);
857  response.getNfmModSettings()->setCtcssOn(settings.m_ctcssOn ? 1 : 0);
858  response.getNfmModSettings()->setFmDeviation(settings.m_fmDeviation);
860  response.getNfmModSettings()->setModAfInput((int) settings.m_modAFInput);
861  response.getNfmModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0);
862  response.getNfmModSettings()->setRfBandwidth(settings.m_rfBandwidth);
863  response.getNfmModSettings()->setRgbColor(settings.m_rgbColor);
864 
865  if (response.getNfmModSettings()->getTitle()) {
866  *response.getNfmModSettings()->getTitle() = settings.m_title;
867  } else {
868  response.getNfmModSettings()->setTitle(new QString(settings.m_title));
869  }
870 
871  response.getNfmModSettings()->setToneFrequency(settings.m_toneFrequency);
872  response.getNfmModSettings()->setVolumeFactor(settings.m_volumeFactor);
873 
874  if (!response.getNfmModSettings()->getCwKeyer()) {
876  }
877 
878  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getNfmModSettings()->getCwKeyer();
879  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
880  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
881 
882  if (response.getNfmModSettings()->getAudioDeviceName()) {
883  *response.getNfmModSettings()->getAudioDeviceName() = settings.m_audioDeviceName;
884  } else {
885  response.getNfmModSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
886  }
887 
888  apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm);
889 
890  response.getNfmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
891 
892  if (response.getNfmModSettings()->getReverseApiAddress()) {
894  } else {
895  response.getNfmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
896  }
897 
901 }
902 
904 {
908 }
909 
910 void NFMMod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const NFMModSettings& settings, bool force)
911 {
913  swgChannelSettings->setDirection(1); // single source (Tx)
914  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
915  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
916  swgChannelSettings->setChannelType(new QString("NFMMod"));
917  swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings());
918  SWGSDRangel::SWGNFMModSettings *swgNFMModSettings = swgChannelSettings->getNfmModSettings();
919 
920  // transfer data that has been modified. When force is on transfer all data except reverse API data
921 
922  if (channelSettingsKeys.contains("channelMute") || force) {
923  swgNFMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0);
924  }
925  if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
926  swgNFMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
927  }
928  if (channelSettingsKeys.contains("modAFInput") || force) {
929  swgNFMModSettings->setModAfInput((int) settings.m_modAFInput);
930  }
931  if (channelSettingsKeys.contains("audioDeviceName") || force) {
932  swgNFMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
933  }
934  if (channelSettingsKeys.contains("playLoop") || force) {
935  swgNFMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0);
936  }
937  if (channelSettingsKeys.contains("afBandwidth") || force) {
938  swgNFMModSettings->setAfBandwidth(settings.m_afBandwidth);
939  }
940  if (channelSettingsKeys.contains("fmDeviation") || force) {
941  swgNFMModSettings->setFmDeviation(settings.m_fmDeviation);
942  }
943  if (channelSettingsKeys.contains("rfBandwidth") || force) {
944  swgNFMModSettings->setRfBandwidth(settings.m_rfBandwidth);
945  }
946  if (channelSettingsKeys.contains("rgbColor") || force) {
947  swgNFMModSettings->setRgbColor(settings.m_rgbColor);
948  }
949  if (channelSettingsKeys.contains("title") || force) {
950  swgNFMModSettings->setTitle(new QString(settings.m_title));
951  }
952  if (channelSettingsKeys.contains("toneFrequency") || force) {
953  swgNFMModSettings->setToneFrequency(settings.m_toneFrequency);
954  }
955  if (channelSettingsKeys.contains("volumeFactor") || force) {
956  swgNFMModSettings->setVolumeFactor(settings.m_volumeFactor);
957  }
958  if (channelSettingsKeys.contains("ctcssOn") || force) {
959  swgNFMModSettings->setCtcssOn(settings.m_ctcssOn ? 1 : 0);
960  }
961  if (channelSettingsKeys.contains("ctcssIndex") || force) {
962  swgNFMModSettings->setCtcssIndex(settings.m_ctcssIndex);
963  }
964 
965  if (force)
966  {
967  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
968  swgNFMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
969  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFMModSettings->getCwKeyer();
970  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
971  }
972 
973  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
974  .arg(settings.m_reverseAPIAddress)
975  .arg(settings.m_reverseAPIPort)
976  .arg(settings.m_reverseAPIDeviceIndex)
977  .arg(settings.m_reverseAPIChannelIndex);
978  m_networkRequest.setUrl(QUrl(channelSettingsURL));
979  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
980 
981  QBuffer *buffer=new QBuffer();
982  buffer->open((QBuffer::ReadWrite));
983  buffer->write(swgChannelSettings->asJson().toUtf8());
984  buffer->seek(0);
985 
986  // Always use PATCH to avoid passing reverse API settings
987  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
988 
989  delete swgChannelSettings;
990 }
991 
993 {
995  swgChannelSettings->setDirection(1); // single source (Tx)
996  swgChannelSettings->setChannelType(new QString("NFMMod"));
997  swgChannelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings());
998  SWGSDRangel::SWGNFMModSettings *swgNFModSettings = swgChannelSettings->getNfmModSettings();
999 
1000  swgNFModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
1001  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgNFModSettings->getCwKeyer();
1002  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
1003 
1004  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
1009  m_networkRequest.setUrl(QUrl(channelSettingsURL));
1010  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1011 
1012  QBuffer *buffer=new QBuffer();
1013  buffer->open((QBuffer::ReadWrite));
1014  buffer->write(swgChannelSettings->asJson().toUtf8());
1015  buffer->seek(0);
1016 
1017  // Always use PATCH to avoid passing reverse API settings
1018  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
1019 
1020  delete swgChannelSettings;
1021 }
1022 
1023 void NFMMod::networkManagerFinished(QNetworkReply *reply)
1024 {
1025  QNetworkReply::NetworkError replyError = reply->error();
1026 
1027  if (replyError)
1028  {
1029  qWarning() << "NFMMod::networkManagerFinished:"
1030  << " error(" << (int) replyError
1031  << "): " << replyError
1032  << ": " << reply->errorString();
1033  return;
1034  }
1035 
1036  QString answer = reply->readAll();
1037  answer.chop(1); // remove last \n
1038  qDebug("NFMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
1039 }
1040 
void setFmDeviation(float fm_deviation)
void setAudioSampleRate(qint32 audio_sample_rate)
NCOF m_toneNco
Definition: nfmmod.h:280
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const NFMModSettings &settings, bool force)
Definition: nfmmod.cpp:910
void processOneSample(Complex &ci)
Definition: nfmmod.cpp:294
void setOriginatorChannelIndex(qint32 originator_channel_index)
static const int m_levelNbSamples
Definition: nfmmod.h:329
void addAudioSink(AudioFifo *audioFifo, MessageQueue *sampleSinkMessageQueue, int outputDeviceIndex=-1)
Add the audio sink.
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport &response)
Definition: nfmmod.cpp:903
void calculateLevel(Real &sample)
Definition: nfmmod.cpp:315
static const QString m_channelIdURI
Definition: nfmmod.h:251
void setVolumeFactor(float volume_factor)
uint m_feedbackAudioBufferFill
Definition: nfmmod.h:308
int getOutputSampleRate(int outputDeviceIndex=-1)
SWGCWKeyerSettings * getCwKeyer()
void setChannelMute(qint32 channel_mute)
bool deserialize(const QByteArray &data)
Complex nextIQ()
Return next complex sample.
Definition: nco.cpp:61
bool decimate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:38
void push(Message *message, bool emitSignal=true)
Push message onto queue.
Fixed< IntType, IntBits > cos(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2271
void networkManagerFinished(QNetworkReply *reply)
Definition: nfmmod.cpp:1023
void pushFeedback(Real sample)
Definition: nfmmod.cpp:271
void applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force=false)
Definition: nfmmod.cpp:538
double m_magsq
Definition: nfmmod.h:298
void removeChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:181
void pullAF(Real &sample)
Definition: nfmmod.cpp:204
CWKeyer m_cwKeyer
Definition: nfmmod.h:324
void create(int nTaps, double sampleRate, double lowCutoff, double highCutoff)
Definition: bandpass.h:15
DeviceAPI * m_deviceAPI
Definition: nfmmod.h:270
static double dbPower(double magsq, double floor=1e-12)
Definition: db.cpp:22
void setRgbColor(qint32 rgb_color)
void setReverseApiAddress(QString *reverse_api_address)
int getInputSampleRate(int inputDeviceIndex=-1)
void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase=4.5)
void setNfmModSettings(SWGNFMModSettings *nfm_mod_settings)
void webapiReverseSendCWSettings(const CWKeyerSettings &settings)
Definition: nfmmod.cpp:992
bool interpolate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:53
Bandpass< Real > m_bandpass
Definition: nfmmod.h:296
void clear()
Definition: audiofifo.cpp:156
void create(int nTaps, double sampleRate, double cutoff)
Definition: lowpass.h:15
Interpolator m_feedbackInterpolator
Definition: nfmmod.h:290
int getDeviceSetIndex() const
Definition: channelapi.h:89
Real m_peakLevel
Definition: nfmmod.h:322
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
int getOutputDeviceIndex(const QString &deviceName) const
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
bool m_feedbackAudioEnable
void removeAudioSink(AudioFifo *audioFifo)
Remove the audio sink.
int getCenterFrequency() const
Definition: nfmmod.h:81
void setInputFrequencyOffset(qint64 input_frequency_offset)
Type filter(Type sample)
Definition: bandpass.h:77
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const NFMModSettings &settings)
Definition: nfmmod.cpp:852
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
Interpolator m_interpolator
Definition: nfmmod.h:285
SWGNFMModReport * getNfmModReport()
static float getCTCSSFreq(int index)
Real m_levelSum
Definition: nfmmod.h:323
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: nfmmod.cpp:722
#define M_PI
Definition: rdsdemod.cpp:27
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: nfmmod.cpp:733
void setChannelType(QString *channel_type)
void addChannelSource(ThreadedBasebandSampleSource *sink, int streamIndex=0)
Add a channel source (Tx)
Definition: deviceapi.cpp:138
void setOriginatorDeviceSetIndex(qint32 originator_device_set_index)
virtual void start()
Definition: nfmmod.cpp:334
static MsgReportFileSourceStreamData * create(int sampleRate, quint32 recordLength)
Definition: nfmmod.h:186
static MsgConfigureChannelizer * create(int sampleRate, int centerFrequency)
Definition: nfmmod.h:83
int m_sampleRate
Definition: nfmmod.h:318
quint32 m_rgbColor
unsigned int uint32_t
Definition: rtptypes_win.h:46
void addAudioSource(AudioFifo *audioFifo, MessageQueue *sampleSourceMessageQueue, int inputDeviceIndex=-1)
Add an audio source.
AudioType getAudioType() const
Definition: dspcommands.h:391
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
Definition: cwkeyer.h:104
virtual bool deserialize(const QByteArray &data)
Definition: nfmmod.cpp:702
QString m_fileName
Definition: nfmmod.h:315
void setPhase(Real phase)
Definition: ncof.h:42
AudioFifo m_audioFifo
Definition: nfmmod.h:304
#define SDR_TX_SCALEF
Definition: dsptypes.h:39
qint64 m_inputFrequencyOffset
void setChannelPowerDb(float channel_power_db)
AudioVector m_audioBuffer
Definition: nfmmod.h:302
void setChannelSampleRate(qint32 channel_sample_rate)
void setToneFrequency(float tone_frequency)
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
bool getFadeSample(bool on, float &sample)
Definition: cwkeyer.cpp:487
void setReverseApiPort(qint32 reverse_api_port)
int getSampleRate() const
Definition: dspcommands.h:390
virtual void pullAudio(int nbSamples)
Definition: nfmmod.cpp:164
static DSPEngine * instance()
Definition: dspengine.cpp:51
CWSmoother & getCWSmoother()
Definition: cwkeyer.h:111
quint32 m_recordLength
record length in seconds computed from file size
Definition: nfmmod.h:317
Real m_interpolatorDistanceRemain
Definition: nfmmod.h:287
AudioVector m_feedbackAudioBuffer
Definition: nfmmod.h:307
void setCwKeyer(SWGCWKeyerSettings *cw_keyer)
FixReal m_real
Definition: dsptypes.h:64
virtual void pull(Sample &sample)
Definition: nfmmod.cpp:119
MovingAverageUtil< double, double, 16 > m_movingAverage
Definition: nfmmod.h:299
static MsgReportFileSourceStreamTiming * create(std::size_t samplesCount)
Definition: nfmmod.h:165
virtual void stop()
Definition: nfmmod.cpp:343
quint32 m_feedbackAudioSampleRate
Definition: nfmmod.h:306
Fixed< IntType, IntBits > sin(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2265
virtual QByteArray serialize() const
Definition: nfmmod.cpp:697
UpChannelizer * m_channelizer
Definition: nfmmod.h:272
static bool match(const Message *message)
Definition: message.cpp:45
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
NFMModSettings m_settings
Definition: nfmmod.h:277
NCOF m_ctcssNco
Definition: nfmmod.h:281
Real m_feedbackInterpolatorDistance
Definition: nfmmod.h:291
void setFreq(Real freq, Real sampleRate)
Definition: nco.cpp:49
QNetworkRequest m_networkRequest
Definition: nfmmod.h:327
quint32 m_levelCalcCount
Definition: nfmmod.h:321
Fixed< IntType, IntBits > sqrt(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2283
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
Definition: nfmmod.cpp:347
uint32_t read(quint8 *data, uint32_t numSamples)
Definition: audiofifo.cpp:103
static MsgConfigureNFMMod * create(const NFMModSettings &settings, bool force)
Definition: nfmmod.h:60
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void setCtcssIndex(qint32 ctcss_index)
void seekFileStream(int seekPercentage)
Definition: nfmmod.cpp:482
virtual QString asJson() override
bool m_interpolatorConsumed
Definition: nfmmod.h:288
uint16_t m_reverseAPIDeviceIndex
static void webapiFormatChannelSettings(SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings, const CWKeyerSettings &cwKeyerSettings)
Definition: cwkeyer.cpp:637
int getSample()
Definition: cwkeyer.cpp:194
void openFileStream()
Definition: nfmmod.cpp:460
float m_feedbackVolumeFactor
int m_basebandSampleRate
Definition: nfmmod.h:274
uint m_audioBufferFill
Definition: nfmmod.h:303
Complex m_modSample
Definition: nfmmod.h:283
AudioDeviceManager * getAudioDeviceManager()
Definition: dspengine.h:55
void setUseReverseApi(qint32 use_reverse_api)
void applySettings(const NFMModSettings &settings, bool force=false)
Definition: nfmmod.cpp:568
void modulateSample()
Definition: nfmmod.cpp:177
void setRfBandwidth(float rf_bandwidth)
quint32 m_audioSampleRate
Definition: nfmmod.h:301
Lowpass< Real > m_lowpass
Definition: nfmmod.h:295
QNetworkAccessManager * m_networkManager
Definition: nfmmod.h:326
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.
NFMMod(DeviceAPI *deviceAPI)
Definition: nfmmod.cpp:55
QByteArray serialize() const
void applyFeedbackAudioSampleRate(unsigned int sampleRate)
Definition: nfmmod.cpp:521
const QString & getFileName() const
Definition: nfmmod.h:104
static const QString m_channelId
Definition: nfmmod.h:252
ThreadedBasebandSampleSource * m_threadedChannelizer
Definition: nfmmod.h:271
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
NFMModInputAF m_modAFInput
quint64 m_fileSize
raw file size (bytes)
Definition: nfmmod.h:316
float m_modPhasor
baseband modulator phasor
Definition: nfmmod.h:282
~NFMMod()
Definition: nfmmod.cpp:107
QString m_audioDeviceName
This is the audio device you get the audio samples from.
FixReal m_imag
Definition: dsptypes.h:65
int getInputDeviceIndex(const QString &deviceName) const
AudioFifo m_feedbackAudioFifo
Definition: nfmmod.h:309
bool m_feedbackInterpolatorConsumed
Definition: nfmmod.h:293
bool getForce() const
Definition: nfmmod.h:58
const NFMModSettings & getSettings() const
Definition: nfmmod.h:57
void addChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:174
SWGNFMModSettings * getNfmModSettings()
virtual int webapiReportGet(SWGSDRangel::SWGChannelReport &response, QString &errorMessage)
Definition: nfmmod.cpp:841
NCO m_carrierNco
Definition: nfmmod.h:279
std::ifstream m_ifstream
Definition: nfmmod.h:314
void setAudioDeviceName(QString *audio_device_name)
double getMagSq() const
Definition: nfmmod.h:247
void setNfmModReport(SWGNFMModReport *nfm_mod_report)
void setPlayLoop(qint32 play_loop)
void reset()
Definition: cwkeyer.h:109
int m_outputSampleRate
Definition: nfmmod.h:275
const CWKeyerSettings & getSettings() const
Definition: cwkeyer.h:107
QString m_reverseAPIAddress
int getIndexInDeviceSet() const
Definition: channelapi.h:87
Real next()
Return next real sample.
Definition: ncof.cpp:57
std::complex< Real > Complex
Definition: dsptypes.h:43
void setModAfInput(qint32 mod_af_input)
double asDouble() const
Definition: movingaverage.h:57
uint16_t m_reverseAPIPort
QString m_feedbackAudioDeviceName
This is the audio device you send the audio samples to for audio feedback.
float Real
Definition: dsptypes.h:42
#define SDR_TX_SCALED
Definition: dsptypes.h:40
void setSampleRate(int sampleRate)
Definition: cwkeyer.cpp:186
void setAfBandwidth(float af_bandwidth)
void applyAudioSampleRate(int sampleRate)
Definition: nfmmod.cpp:495
QMutex m_settingsMutex
Definition: nfmmod.h:312
int m_inputFrequencyOffset
Definition: nfmmod.h:276
Real m_interpolatorDistance
Definition: nfmmod.h:286
qint16 FixReal
Definition: dsptypes.h:35
T max(const T &x, const T &y)
Definition: framework.h:446
uint32_t write(const quint8 *data, uint32_t numSamples)
Definition: audiofifo.cpp:66
const CWKeyerSettings & getSettings() const
Definition: cwkeyer.h:60
Real m_feedbackInterpolatorDistanceRemain
Definition: nfmmod.h:292
void removeAudioSource(AudioFifo *audioFifo)
Remove an audio source.
uint16_t m_reverseAPIChannelIndex
void configure(MessageQueue *messageQueue, int sampleRate, int centerFrequency)
T min(const T &x, const T &y)
Definition: framework.h:440