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.
freedvdemod.cpp
Go to the documentation of this file.
1 // Copyright (C) 2019 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 <stdio.h>
19 
20 #include <QTime>
21 #include <QDebug>
22 #include <QNetworkAccessManager>
23 #include <QNetworkReply>
24 #include <QBuffer>
25 
26 #include "codec2/freedv_api.h"
27 #include "codec2/modem_stats.h"
28 
29 #include "SWGChannelSettings.h"
30 #include "SWGFreeDVDemodSettings.h"
31 #include "SWGChannelReport.h"
32 #include "SWGFreeDVDemodReport.h"
33 
34 #include "audio/audiooutput.h"
35 #include "dsp/dspengine.h"
36 #include "dsp/downchannelizer.h"
38 #include "dsp/dspcommands.h"
39 #include "device/deviceapi.h"
40 #include "util/db.h"
41 
42 #include "freedvdemod.h"
43 
48 
49 const QString FreeDVDemod::m_channelIdURI = "sdrangel.channel.freedvdemod";
50 const QString FreeDVDemod::m_channelId = "FreeDVDemod";
51 
53 {
54  init();
55 }
56 
58 {
59  m_sync = false;
60  m_snrEst = -20;
61  m_clockOffset = 0;
62  m_freqOffset = 0;
63  m_syncMetric = 0;
64  m_totalBitErrors = 0;
66  m_ber = 0;
67  m_frameCount = 0;
68  m_berFrameCount = 0;
69  m_fps = 1;
70 }
71 
72 void FreeDVDemod::FreeDVStats::collect(struct freedv *freeDV)
73 {
74  struct MODEM_STATS stats;
75 
76  freedv_get_modem_extended_stats(freeDV, &stats);
77  m_totalBitErrors = freedv_get_total_bit_errors(freeDV);
78  m_clockOffset = stats.clock_offset;
79  m_freqOffset = stats.foff;
80  m_syncMetric = stats.sync_metric;
81  m_sync = stats.sync != 0;
82  m_snrEst = stats.snr_est;
83 
84  if (m_berFrameCount >= m_fps)
85  {
87  m_ber = m_ber < 0 ? 0 : m_ber;
88  m_berFrameCount = 0;
89  m_lastTotalBitErrors = m_totalBitErrors;
90 // qDebug("FreeDVStats::collect: demod sync: %s sync metric: %f demod snr: %3.2f dB BER: %d clock offset: %f freq offset: %f",
91 // m_sync ? "ok" : "ko", m_syncMetric, m_snrEst, m_ber, m_clockOffset, m_freqOffset);
92  }
93 
95  m_frameCount++;
96 }
97 
99 {
100  m_sum = 0.0f;
101  m_peak = 0.0f;
102  m_n = 0;
103  m_reset = true;
104 }
105 
107 {
108  if (m_reset)
109  {
110  m_sum = CalcDb::powerFromdB(snrdB);
111  m_peak = snrdB;
112  m_n = 1;
113  m_reset = false;
114  }
115  else
116  {
117  m_sum += CalcDb::powerFromdB(snrdB);
118  m_peak = std::max(m_peak, snrdB);
119  m_n++;
120  }
121 }
122 
124 {
125  m_sum = 0.0f;
126  m_peak = 0.0f;
127  m_n = 0;
128  m_reset = true;
129 }
130 
132 {
133  if (m_reset)
134  {
135  m_sum = level * level;
136  m_peak = std::fabs(level);
137  m_n = 1;
138  m_reset = false;
139  }
140  else
141  {
142  m_sum += level * level;
143  m_peak = std::max(m_peak, std::fabs(level));
144  m_n++;
145  }
146 }
147 
150  m_deviceAPI(deviceAPI),
151  m_hiCutoff(6000),
152  m_lowCutoff(0),
153  m_volume(2),
154  m_spanLog2(3),
155  m_sum(fftfilt::cmplx{0,0}),
156  m_inputSampleRate(48000),
157  m_modemSampleRate(48000),
158  m_speechSampleRate(8000), // fixed 8 kS/s
160  m_audioMute(false),
161  m_simpleAGC(0.003f, 0.0f, 1e-6f),
162  m_agcActive(false),
163  m_squelchDelayLine(2*48000),
164  m_audioActive(false),
165  m_sampleSink(0),
166  m_audioFifo(24000),
167  m_freeDV(0),
168  m_nSpeechSamples(0),
170  m_iSpeech(0),
171  m_iModem(0),
172  m_speechOut(0),
173  m_modIn(0),
174  m_levelInNbSamples(480), // 10ms @ 48 kS/s
175  m_settingsMutex(QMutex::Recursive)
176 {
177  setObjectName(m_channelId);
178 
181  applyAudioSampleRate(audioSampleRate);
182 
183  m_audioBuffer.resize(1<<14);
184  m_audioBufferFill = 0;
185  m_undersampleCount = 0;
186 
187  m_magsq = 0.0f;
188  m_magsqSum = 0.0f;
189  m_magsqPeak = 0.0f;
190  m_magsqCount = 0;
191 
193 
195 
197  applySettings(m_settings, true);
198 
199  m_channelizer = new DownChannelizer(this);
203 
204  m_networkManager = new QNetworkAccessManager();
205  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
206 }
207 
209 {
210  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
211  delete m_networkManager;
213 
216  delete m_threadedChannelizer;
217  delete m_channelizer;
218  delete SSBFilter;
219 }
220 
222  Real Bandwidth,
223  Real LowCutoff,
224  Real volume,
225  int spanLog2,
226  bool audioBinaural,
227  bool audioFlipChannel,
228  bool dsb,
229  bool audioMute,
230  bool agc,
231  bool agcClamping,
232  int agcTimeLog2,
233  int agcPowerThreshold,
234  int agcThresholdGate)
235 {
237  Bandwidth,
238  LowCutoff,
239  volume,
240  spanLog2,
241  audioBinaural,
242  audioFlipChannel,
243  dsb,
244  audioMute,
245  agc,
246  agcClamping,
247  agcTimeLog2,
248  agcPowerThreshold,
249  agcThresholdGate);
250  messageQueue->push(cmd);
251 }
252 
253 void FreeDVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly)
254 {
255  (void) positiveOnly;
256  Complex ci;
257  fftfilt::cmplx *sideband;
258  int n_out;
259 
260  m_settingsMutex.lock();
261 
262  int decim = 1<<(m_spanLog2 - 1);
263  unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
264 
265  for(SampleVector::const_iterator it = begin; it < end; ++it)
266  {
267  Complex c(it->real(), it->imag());
268  c *= m_nco.nextIQ();
269 
270  if (m_interpolatorDistance < 1.0f) // interpolate
271  {
273  {
274  processOneSample(ci);
276  }
277  }
278  else
279  {
281  {
282  processOneSample(ci);
284  }
285  }
286  }
287 
288  uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
289 
290  if (res != m_audioBufferFill)
291  {
292  qDebug("FreeDVDemod::feed: %u/%u tail samples written", res, m_audioBufferFill);
293  }
294 
295  m_audioBufferFill = 0;
296 
297  if (m_sampleSink != 0)
298  {
299  m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true);
300  }
301 
302  m_sampleBuffer.clear();
303 
304  m_settingsMutex.unlock();
305 }
306 
308 {
309  fftfilt::cmplx *sideband;
310  int n_out = 0;
311  int decim = 1<<(m_spanLog2 - 1);
312  unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
313 
314  n_out = SSBFilter->runSSB(ci, &sideband, true); // always USB side
315 
316  for (int i = 0; i < n_out; i++)
317  {
318  // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
319  // smart decimation with bit gain using float arithmetic (23 bits significand)
320 
321  m_sum += sideband[i];
322 
323  if (!(m_undersampleCount++ & decim_mask))
324  {
325  Real avgr = m_sum.real() / decim;
326  Real avgi = m_sum.imag() / decim;
327  m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED);
328 
329  m_magsqSum += m_magsq;
330 
331  if (m_magsq > m_magsqPeak)
332  {
334  }
335 
336  m_magsqCount++;
337  m_sampleBuffer.push_back(Sample(avgr, avgi));
338  m_sum.real(0.0);
339  m_sum.imag(0.0);
340  }
341 
342  // fftfilt::cmplx z = sideband[i];
343  // Real demod = (z.real() + z.imag()) * 0.7;
344  Real demod = sideband[i].real(); // works as good
345 
346  if (m_agcActive)
347  {
348  m_simpleAGC.feed(demod);
349  demod *= (m_settings.m_volumeIn * 3276.8f) / m_simpleAGC.getValue(); // provision for peak to average ratio (here 10) compensated by m_volumeIn
350  // if (i == 0) {
351  // qDebug("FreeDVDemod::feed: m_simpleAGC: %f", m_simpleAGC.getValue());
352  // }
353  }
354  else
355  {
356  demod *= m_settings.m_volumeIn;
357  }
358 
359  pushSampleToDV((qint16) demod);
360  }
361 }
362 
364 {
366 }
367 
369 {
370 }
371 
373 {
375  {
377  qDebug("FreeDVDemod::handleMessage: MsgChannelizerNotification: m_sampleRate");
378 
380 
381  return true;
382  }
383  else if (MsgConfigureChannelizer::match(cmd))
384  {
386  qDebug() << "FreeDVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << cfg.getSampleRate()
387  << " centerFrequency: " << cfg.getCenterFrequency();
388 
390  cfg.getSampleRate(),
391  cfg.getCenterFrequency());
392 
393  return true;
394  }
395  else if (MsgConfigureFreeDVDemod::match(cmd))
396  {
398  qDebug("FreeDVDemod::handleMessage: MsgConfigureFreeDVDemod");
399 
400  applySettings(cfg.getSettings(), cfg.getForce());
401 
402  return true;
403  }
404  else if (MsgResyncFreeDVDemod::match(cmd))
405  {
406  qDebug("FreeDVDemod::handleMessage: MsgResyncFreeDVDemod");
407  m_settingsMutex.lock();
408  freedv_set_sync(m_freeDV, FREEDV_SYNC_UNSYNC);
409  m_settingsMutex.unlock();
410  return true;
411  }
413  {
415  const QThread *thread = cfg.getThread();
416  qDebug("FreeDVDemod::handleMessage: BasebandSampleSink::MsgThreadedSink: %p", thread);
417  return true;
418  }
419  else if (DSPConfigureAudio::match(cmd))
420  {
421  DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd;
422  uint32_t sampleRate = cfg.getSampleRate();
423 
424  qDebug() << "FreeDVDemod::handleMessage: DSPConfigureAudio:"
425  << " sampleRate: " << sampleRate;
426 
427  if (sampleRate != m_audioSampleRate) {
428  applyAudioSampleRate(sampleRate);
429  }
430 
431  return true;
432  }
433  else if (DSPSignalNotification::match(cmd))
434  {
435  return true;
436  }
437  else
438  {
439  if(m_sampleSink != 0)
440  {
441  return m_sampleSink->handleMessage(cmd);
442  }
443  else
444  {
445  return false;
446  }
447  }
448 }
449 
451 {
452  qint16 audioSample;
453 
455  {
456  qreal rmsLevel = sqrt(m_levelIn.m_sum / m_levelInNbSamples);
457  // qDebug("FreeDVDemod::pushSampleToDV: rmsLevel: %f m_peak: %f m_levelInNbSamples: %d",
458  // rmsLevel, m_levelIn.m_peak, m_levelInNbSamples);
460  m_levelIn.m_reset = true;
461  }
462 
463  m_levelIn.accumulate(sample/29491.2f); // scale on 90% (0.9 * 32768.0)
464 
465  if (m_iModem == m_nin)
466  {
467  int nout = freedv_rx(m_freeDV, m_speechOut, m_modIn);
470 
472  {
473  for (uint32_t i = 0; i < nout * m_audioResampler.getDecimation(); i++) {
475  }
476  }
477  else
478  {
479  for (int i = 0; i < nout; i++)
480  {
481  while (!m_audioResampler.upSample(m_speechOut[i], audioSample)) {
482  pushSampleToAudio(audioSample);
483  }
484 
485  pushSampleToAudio(audioSample);
486  }
487  }
488 
489  m_iModem = 0;
490  m_iSpeech = 0;
491  }
492 
493  m_modIn[m_iModem++] = sample;
494 }
495 
497 {
501 
502  if (m_audioBufferFill >= m_audioBuffer.size())
503  {
504  uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
505 
506  if (res != m_audioBufferFill) {
507  qDebug("FreeDVDemod::pushSampleToAudio: %u/%u samples written", res, m_audioBufferFill);
508  }
509 
510  m_audioBufferFill = 0;
511  }
512 }
513 
514 void FreeDVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force)
515 {
516  qDebug() << "FreeDVDemod::applyChannelSettings:"
517  << " inputSampleRate: " << inputSampleRate
518  << " inputFrequencyOffset: " << inputFrequencyOffset;
519 
520  if ((m_inputFrequencyOffset != inputFrequencyOffset) ||
521  (m_inputSampleRate != inputSampleRate) || force)
522  {
523  m_nco.setFreq(-inputFrequencyOffset, inputSampleRate);
524  }
525 
526  if ((m_inputSampleRate != inputSampleRate) || force)
527  {
528  m_settingsMutex.lock();
529  m_interpolator.create(16, inputSampleRate, m_hiCutoff * 1.5f, 2.0f);
531  m_interpolatorDistance = (Real) inputSampleRate / (Real) m_modemSampleRate;
532  m_settingsMutex.unlock();
533  }
534 
535  m_inputSampleRate = inputSampleRate;
536  m_inputFrequencyOffset = inputFrequencyOffset;
537 }
538 
540 {
541  qDebug("FreeDVDemod::applyAudioSampleRate: %d", sampleRate);
542 
543  m_settingsMutex.lock();
544  m_audioFifo.setSize(sampleRate);
546  m_audioResampler.setAudioFilters(sampleRate, sampleRate, 250, 3300, 4.0f);
547  m_settingsMutex.unlock();
548 
549  m_audioSampleRate = sampleRate;
550 }
551 
553 {
556  uint32_t modemSampleRate = FreeDVDemodSettings::getModSampleRate(mode);
557 
558  m_settingsMutex.lock();
559  SSBFilter->create_filter(m_lowCutoff / (float) modemSampleRate, m_hiCutoff / (float) modemSampleRate);
560 
561  // baseband interpolator
562  if (modemSampleRate != m_modemSampleRate)
563  {
565  modemSampleRate, m_settings.m_inputFrequencyOffset);
566  m_inputMessageQueue.push(channelConfigMsg);
567 
569  //m_interpolatorConsumed = false;
570  m_interpolatorDistance = (Real) m_inputSampleRate / (Real) modemSampleRate;
572  m_modemSampleRate = modemSampleRate;
573 
574  m_simpleAGC.resizeNew(modemSampleRate/10, 0.003);
575  m_levelInNbSamples = m_modemSampleRate / 100; // 10ms
576 
577  if (getMessageQueueToGUI())
578  {
580  getMessageQueueToGUI()->push(cfg);
581  }
582  }
583 
584  // FreeDV object
585 
586  if (m_freeDV) {
587  freedv_close(m_freeDV);
588  }
589 
590  int fdv_mode = -1;
591 
592  switch(mode)
593  {
595  fdv_mode = FREEDV_MODE_700C;
596  break;
598  fdv_mode = FREEDV_MODE_700D;
599  break;
601  fdv_mode = FREEDV_MODE_800XA;
602  break;
604  fdv_mode = FREEDV_MODE_1600;
605  break;
607  default:
608  fdv_mode = FREEDV_MODE_2400A;
609  break;
610  }
611 
612  if (fdv_mode == FREEDV_MODE_700D)
613  {
614  struct freedv_advanced adv;
615  adv.interleave_frames = 1;
616  m_freeDV = freedv_open_advanced(fdv_mode, &adv);
617  }
618  else
619  {
620  m_freeDV = freedv_open(fdv_mode);
621  }
622 
623  if (m_freeDV)
624  {
625  freedv_set_test_frames(m_freeDV, 0);
626  freedv_set_snr_squelch_thresh(m_freeDV, -100.0);
627  freedv_set_squelch_en(m_freeDV, 0);
628  freedv_set_clip(m_freeDV, 0);
629  freedv_set_ext_vco(m_freeDV, 0);
630  freedv_set_sync(m_freeDV, FREEDV_SYNC_MANUAL);
631 
632  freedv_set_callback_txt(m_freeDV, nullptr, nullptr, nullptr);
633  freedv_set_callback_protocol(m_freeDV, nullptr, nullptr, nullptr);
634  freedv_set_callback_data(m_freeDV, nullptr, nullptr, nullptr);
635 
636  int nSpeechSamples = freedv_get_n_speech_samples(m_freeDV);
637  int nMaxModemSamples = freedv_get_n_max_modem_samples(m_freeDV);
638  int Fs = freedv_get_modem_sample_rate(m_freeDV);
639  int Rs = freedv_get_modem_symbol_rate(m_freeDV);
641 
642  if (nSpeechSamples != m_nSpeechSamples)
643  {
644  if (m_speechOut) {
645  delete[] m_speechOut;
646  }
647 
648  m_speechOut = new int16_t[nSpeechSamples];
649  m_nSpeechSamples = nSpeechSamples;
650  }
651 
652  if (nMaxModemSamples != m_nMaxModemSamples)
653  {
654  if (m_modIn) {
655  delete[] m_modIn;
656  }
657 
658  m_modIn = new int16_t[nMaxModemSamples];
659  m_nMaxModemSamples = nMaxModemSamples;
660  }
661 
662  m_iSpeech = 0;
663  m_iModem = 0;
664  m_nin = freedv_nin(m_freeDV);
665 
666  if (m_nin > 0) {
668  }
669 
670  qDebug() << "FreeDVMod::applyFreeDVMode:"
671  << " fdv_mode: " << fdv_mode
672  << " m_modemSampleRate: " << m_modemSampleRate
673  << " m_lowCutoff: " << m_lowCutoff
674  << " m_hiCutoff: " << m_hiCutoff
675  << " Fs: " << Fs
676  << " Rs: " << Rs
677  << " m_nSpeechSamples: " << m_nSpeechSamples
678  << " m_nMaxModemSamples: " << m_nMaxModemSamples
679  << " m_nin: " << m_nin
680  << " FPS: " << m_freeDVStats.m_fps;
681  }
682 
683  m_settingsMutex.unlock();
684 }
685 
686 void FreeDVDemod::applySettings(const FreeDVDemodSettings& settings, bool force)
687 {
688  qDebug() << "FreeDVDemod::applySettings:"
689  << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
690  << " m_volume: " << settings.m_volume
691  << " m_volumeIn: " << settings.m_volumeIn
692  << " m_spanLog2: " << settings.m_spanLog2
693  << " m_audioMute: " << settings.m_audioMute
694  << " m_agcActive: " << settings.m_agc
695  << " m_audioDeviceName: " << settings.m_audioDeviceName
696  << " m_useReverseAPI: " << settings.m_useReverseAPI
697  << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
698  << " m_reverseAPIPort: " << settings.m_reverseAPIPort
699  << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
700  << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
701  << " force: " << force;
702 
703  QList<QString> reverseAPIKeys;
704 
705  if((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) {
706  reverseAPIKeys.append("inputFrequencyOffset");
707  }
708 
709  if ((m_settings.m_volume != settings.m_volume) || force)
710  {
711  reverseAPIKeys.append("volume");
712  m_volume = settings.m_volume;
713  m_volume /= 4.0; // for 3276.8
714  }
715 
716  if ((m_settings.m_volumeIn != settings.m_volumeIn) || force)
717  {
718  reverseAPIKeys.append("volumeIn");
719  }
720 
721  if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
722  {
723  reverseAPIKeys.append("audioDeviceName");
725  int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName);
726  audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex);
727  uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
728 
729  if (m_audioSampleRate != audioSampleRate) {
730  applyAudioSampleRate(audioSampleRate);
731  }
732  }
733 
734  if ((m_settings.m_spanLog2 != settings.m_spanLog2) || force) {
735  reverseAPIKeys.append("spanLog2");
736  }
737  if ((m_settings.m_audioMute != settings.m_audioMute) || force) {
738  reverseAPIKeys.append("audioMute");
739  }
740  if ((m_settings.m_agc != settings.m_agc) || force) {
741  reverseAPIKeys.append("agc");
742  }
743 
744  if ((settings.m_freeDVMode != m_settings.m_freeDVMode) || force) {
745  applyFreeDVMode(settings.m_freeDVMode);
746  }
747 
748  m_spanLog2 = settings.m_spanLog2;
749  m_audioMute = settings.m_audioMute;
750  m_agcActive = settings.m_agc;
751 
752  if (settings.m_useReverseAPI)
753  {
754  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
759  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
760  }
761 
762  m_settings = settings;
763 }
764 
765 void FreeDVDemod::getSNRLevels(double& avg, double& peak, int& nbSamples)
766 {
767  if (m_freeDVSNR.m_n > 0)
768  {
770  peak = m_freeDVSNR.m_peak;
771  nbSamples = m_freeDVSNR.m_n;
772  m_freeDVSNR.m_reset = true;
773  }
774  else
775  {
776  avg = 0.0;
777  peak = 0.0;
778  nbSamples = 1;
779  }
780 }
781 
782 QByteArray FreeDVDemod::serialize() const
783 {
784  return m_settings.serialize();
785 }
786 
787 bool FreeDVDemod::deserialize(const QByteArray& data)
788 {
789  if (m_settings.deserialize(data))
790  {
793  return true;
794  }
795  else
796  {
800  return false;
801  }
802 }
803 
806  QString& errorMessage)
807 {
808  (void) errorMessage;
810  response.getFreeDvDemodSettings()->init();
812  return 200;
813 }
814 
816  bool force,
817  const QStringList& channelSettingsKeys,
819  QString& errorMessage)
820 {
821  (void) errorMessage;
822  FreeDVDemodSettings settings = m_settings;
823  bool frequencyOffsetChanged = false;
824 
825  if (channelSettingsKeys.contains("inputFrequencyOffset"))
826  {
828  frequencyOffsetChanged = true;
829  }
830  if (channelSettingsKeys.contains("freeDVMode")) {
832  }
833  if (channelSettingsKeys.contains("volume")) {
834  settings.m_volume = response.getFreeDvDemodSettings()->getVolume();
835  }
836  if (channelSettingsKeys.contains("volumeIn")) {
837  settings.m_volumeIn = response.getFreeDvDemodSettings()->getVolumeIn();
838  }
839  if (channelSettingsKeys.contains("spanLog2")) {
840  settings.m_spanLog2 = response.getFreeDvDemodSettings()->getSpanLog2();
841  }
842  if (channelSettingsKeys.contains("audioMute")) {
843  settings.m_audioMute = response.getFreeDvDemodSettings()->getAudioMute() != 0;
844  }
845  if (channelSettingsKeys.contains("agc")) {
846  settings.m_agc = response.getFreeDvDemodSettings()->getAgc() != 0;
847  }
848  if (channelSettingsKeys.contains("rgbColor")) {
849  settings.m_rgbColor = response.getFreeDvDemodSettings()->getRgbColor();
850  }
851  if (channelSettingsKeys.contains("title")) {
852  settings.m_title = *response.getFreeDvDemodSettings()->getTitle();
853  }
854  if (channelSettingsKeys.contains("audioDeviceName")) {
856  }
857  if (channelSettingsKeys.contains("useReverseAPI")) {
858  settings.m_useReverseAPI = response.getFreeDvDemodSettings()->getUseReverseApi() != 0;
859  }
860  if (channelSettingsKeys.contains("reverseAPIAddress")) {
862  }
863  if (channelSettingsKeys.contains("reverseAPIPort")) {
865  }
866  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
868  }
869  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
871  }
872 
873  if (frequencyOffsetChanged)
874  {
877  m_inputMessageQueue.push(channelConfigMsg);
878  }
879 
882 
883  qDebug("FreeDVDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
884  if (m_guiMessageQueue) // forward to GUI if any
885  {
886  MsgConfigureFreeDVDemod *msgToGUI = MsgConfigureFreeDVDemod::create(settings, force);
887  m_guiMessageQueue->push(msgToGUI);
888  }
889 
890  webapiFormatChannelSettings(response, settings);
891 
892  return 200;
893 }
894 
897  QString& errorMessage)
898 {
899  (void) errorMessage;
901  response.getFreeDvDemodReport()->init();
902  webapiFormatChannelReport(response);
903  return 200;
904 }
905 
907 {
908  response.getFreeDvDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0);
910  response.getFreeDvDemodSettings()->setVolume(settings.m_volume);
911  response.getFreeDvDemodSettings()->setVolumeIn(settings.m_volumeIn);
912  response.getFreeDvDemodSettings()->setSpanLog2(settings.m_spanLog2);
913  response.getFreeDvDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0);
914  response.getFreeDvDemodSettings()->setAgc(settings.m_agc ? 1 : 0);
915  response.getFreeDvDemodSettings()->setRgbColor(settings.m_rgbColor);
916  response.getFreeDvDemodSettings()->setFreeDvMode((int) settings.m_freeDVMode);
917 
918  if (response.getFreeDvDemodSettings()->getTitle()) {
919  *response.getFreeDvDemodSettings()->getTitle() = settings.m_title;
920  } else {
921  response.getFreeDvDemodSettings()->setTitle(new QString(settings.m_title));
922  }
923 
924  if (response.getFreeDvDemodSettings()->getAudioDeviceName()) {
926  } else {
927  response.getFreeDvDemodSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
928  }
929 
930  response.getFreeDvDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
931 
932  if (response.getFreeDvDemodSettings()->getReverseApiAddress()) {
934  } else {
935  response.getFreeDvDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
936  }
937 
941 }
942 
944 {
945  double magsqAvg, magsqPeak;
946  int nbMagsqSamples;
947  getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
948 
950  response.getFreeDvDemodReport()->setSquelch(m_audioActive ? 1 : 0);
953 }
954 
955 void FreeDVDemod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const FreeDVDemodSettings& settings, bool force)
956 {
958  swgChannelSettings->setDirection(0); // single sink (Rx)
959  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
960  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
961  swgChannelSettings->setChannelType(new QString("SSBDemod"));
963  SWGSDRangel::SWGFreeDVDemodSettings *swgFreeDVDemodSettings = swgChannelSettings->getFreeDvDemodSettings();
964 
965  // transfer data that has been modified. When force is on transfer all data except reverse API data
966 
967  if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
968  swgFreeDVDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
969  }
970  if (channelSettingsKeys.contains("freeDVMode") || force) {
971  swgFreeDVDemodSettings->setFreeDvMode((int) settings.m_freeDVMode);
972  }
973  if (channelSettingsKeys.contains("volume") || force) {
974  swgFreeDVDemodSettings->setVolume(settings.m_volume);
975  }
976  if (channelSettingsKeys.contains("volumeIn") || force) {
977  swgFreeDVDemodSettings->setVolumeIn(settings.m_volumeIn);
978  }
979  if (channelSettingsKeys.contains("spanLog2") || force) {
980  swgFreeDVDemodSettings->setSpanLog2(settings.m_spanLog2);
981  }
982  if (channelSettingsKeys.contains("audioMute") || force) {
983  swgFreeDVDemodSettings->setAudioMute(settings.m_audioMute ? 1 : 0);
984  }
985  if (channelSettingsKeys.contains("agc") || force) {
986  swgFreeDVDemodSettings->setAgc(settings.m_agc ? 1 : 0);
987  }
988  if (channelSettingsKeys.contains("rgbColor") || force) {
989  swgFreeDVDemodSettings->setRgbColor(settings.m_rgbColor);
990  }
991  if (channelSettingsKeys.contains("title") || force) {
992  swgFreeDVDemodSettings->setTitle(new QString(settings.m_title));
993  }
994  if (channelSettingsKeys.contains("audioDeviceName") || force) {
995  swgFreeDVDemodSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
996  }
997 
998  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
999  .arg(settings.m_reverseAPIAddress)
1000  .arg(settings.m_reverseAPIPort)
1001  .arg(settings.m_reverseAPIDeviceIndex)
1002  .arg(settings.m_reverseAPIChannelIndex);
1003  m_networkRequest.setUrl(QUrl(channelSettingsURL));
1004  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1005 
1006  QBuffer *buffer=new QBuffer();
1007  buffer->open((QBuffer::ReadWrite));
1008  buffer->write(swgChannelSettings->asJson().toUtf8());
1009  buffer->seek(0);
1010 
1011  // Always use PATCH to avoid passing reverse API settings
1012  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
1013 
1014  delete swgChannelSettings;
1015 }
1016 
1017 void FreeDVDemod::networkManagerFinished(QNetworkReply *reply)
1018 {
1019  QNetworkReply::NetworkError replyError = reply->error();
1020 
1021  if (replyError)
1022  {
1023  qWarning() << "FreeDVDemod::networkManagerFinished:"
1024  << " error(" << (int) replyError
1025  << "): " << replyError
1026  << ": " << reply->errorString();
1027  return;
1028  }
1029 
1030  QString answer = reply->readAll();
1031  answer.chop(1); // remove last \n
1032  qDebug("FreeDVDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
1033 }
int16_t * m_speechOut
Definition: freedvdemod.h:402
short int16_t
Definition: rtptypes_win.h:43
QMutex m_settingsMutex
Definition: freedvdemod.h:410
void setOriginatorChannelIndex(qint32 originator_channel_index)
void addAudioSink(AudioFifo *audioFifo, MessageQueue *sampleSinkMessageQueue, int outputDeviceIndex=-1)
Add the audio sink.
DeviceAPI * m_deviceAPI
Definition: freedvdemod.h:353
Real m_lowCutoff
Definition: freedvdemod.h:359
void setAudioDeviceName(QString *audio_device_name)
Interpolator m_interpolator
Definition: freedvdemod.h:381
int getOutputSampleRate(int outputDeviceIndex=-1)
void getSNRLevels(double &avg, double &peak, int &nbSamples)
bool decimate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:38
void configure(MessageQueue *messageQueue, int sampleRate, int centerFrequency)
void configure(MessageQueue *messageQueue, Real Bandwidth, Real LowCutoff, Real volume, int spanLog2, bool audioBinaural, bool audioFlipChannels, bool dsb, bool audioMute, bool agc, bool agcClamping, int agcTimeLog2, int agcPowerThreshold, int agcThresholdGate)
bool upSample(qint16 sampleIn, qint16 &sampleOut)
virtual bool deserialize(const QByteArray &data)
void push(Message *message, bool emitSignal=true)
Push message onto queue.
AudioFifo m_audioFifo
Definition: freedvdemod.h:391
void pushSampleToAudio(int16_t sample)
bool m_agcActive
Definition: freedvdemod.h:376
void removeChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:163
static MsgConfigureFreeDVDemod * create(const FreeDVDemodSettings &settings, bool force)
Definition: freedvdemod.h:60
static double dbPower(double magsq, double floor=1e-12)
Definition: db.cpp:22
void levelInChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
void addChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:156
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force=false)
void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase=4.5)
AudioResampler m_audioResampler
Definition: freedvdemod.h:404
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
void setAudioFilters(int srHigh, int srLow, int fcLow, int fcHigh, float gain=1.0f)
bool interpolate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:53
FreeDVStats m_freeDVStats
Definition: freedvdemod.h:405
void pushSampleToDV(int16_t sample)
int getDeviceSetIndex() const
Definition: channelapi.h:89
void setDecimation(uint32_t decimation)
virtual int webapiReportGet(SWGSDRangel::SWGChannelReport &response, QString &errorMessage)
int getOutputDeviceIndex(const QString &deviceName) const
int16_t * m_modIn
Definition: freedvdemod.h:403
uint32_t getDecimation() const
void applyAudioSampleRate(int sampleRate)
void applySettings(const FreeDVDemodSettings &settings, bool force=false)
static const QString m_channelId
Definition: freedvdemod.h:197
int m_inputSampleRate
Definition: freedvdemod.h:364
virtual ~FreeDVDemod()
fftfilt::cmplx m_sum
Definition: freedvdemod.h:362
int m_ber
estimated BER (b/s)
Definition: freedvdemod.h:232
int m_magsqCount
Definition: freedvdemod.h:373
BasebandSampleSink * m_sampleSink
Definition: freedvdemod.h:386
void removeAudioSink(AudioFifo *audioFifo)
Remove the audio sink.
AudioVector m_audioBuffer
Definition: freedvdemod.h:389
uint32_t m_modemSampleRate
Definition: freedvdemod.h:365
std::complex< float > cmplx
Definition: complex.h:31
void collect(struct freedv *freedv)
Definition: freedvdemod.cpp:72
FreeDVDemod(DeviceAPI *deviceAPI)
static const QString m_channelIdURI
Definition: freedvdemod.h:196
void setChannelPowerDb(float channel_power_db)
QNetworkAccessManager * m_networkManager
Definition: freedvdemod.h:393
void setChannelType(QString *channel_type)
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool positiveOnly)=0
void setOriginatorDeviceSetIndex(qint32 originator_device_set_index)
DoubleBufferFIFO< fftfilt::cmplx > m_squelchDelayLine
Definition: freedvdemod.h:377
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
Real getValue()
Definition: agc.h:107
std::complex< float > cmplx
Definition: fftfilt.h:21
Complex nextIQ()
Return next complex sample.
Definition: ncof.cpp:63
void getMagSqLevels(double &avg, double &peak, int &nbSamples)
Definition: freedvdemod.h:159
unsigned int uint32_t
Definition: rtptypes_win.h:46
double m_magsqSum
Definition: freedvdemod.h:371
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const FreeDVDemodSettings &settings)
FreeDVDemodSettings m_settings
Definition: freedvdemod.h:356
uint32_t m_audioSampleRate
Definition: freedvdemod.h:367
double m_magsq
Definition: freedvdemod.h:370
void feed(Real value)
Definition: agc.h:116
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
ThreadedBasebandSampleSink * m_threadedChannelizer
Definition: freedvdemod.h:354
virtual bool handleMessage(const Message &cmd)=0
Processing of a message. Returns true if message has actually been processed.
Real m_interpolatorDistanceRemain
Definition: freedvdemod.h:383
void applyFreeDVMode(FreeDVDemodSettings::FreeDVMode mode)
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
void create_filter(float f1, float f2)
Definition: fftfilt.cpp:107
int getSampleRate() const
Definition: dspcommands.h:390
static DSPEngine * instance()
Definition: dspengine.cpp:51
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport &response)
static MsgConfigureChannelizer * create(int sampleRate, int centerFrequency)
Definition: freedvdemod.h:96
int m_levelInNbSamples
Definition: freedvdemod.h:408
int m_nSpeechSamples
Definition: freedvdemod.h:397
bool m_audioActive
True if an audio signal is produced (no AGC or AGC and above threshold)
Definition: freedvdemod.h:378
uint m_audioBufferFill
Definition: freedvdemod.h:390
void setReverseApiPort(qint32 reverse_api_port)
int m_nMaxModemSamples
Definition: freedvdemod.h:398
const FreeDVDemodSettings & getSettings() const
Definition: freedvdemod.h:57
int32_t i
Definition: decimators.h:244
bool deserialize(const QByteArray &data)
FreeDVSNR m_freeDVSNR
Definition: freedvdemod.h:406
static bool match(const Message *message)
Definition: message.cpp:45
Real m_interpolatorDistance
Definition: freedvdemod.h:382
uint32_t m_berFrameCount
count of frames for BER estimation
Definition: freedvdemod.h:234
int m_inputFrequencyOffset
Definition: freedvdemod.h:368
bool m_audioMute
Definition: freedvdemod.h:369
void setInputFrequencyOffset(qint64 input_frequency_offset)
void removeChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Remove a channel sink (Rx)
Definition: deviceapi.cpp:127
void setReverseApiAddress(QString *reverse_api_address)
Fixed< IntType, IntBits > sqrt(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2283
QNetworkRequest m_networkRequest
Definition: freedvdemod.h:394
virtual void start()
virtual void stop()
void setAudioSampleRate(qint32 audio_sample_rate)
MessageQueue * getMessageQueueToGUI()
double m_magsqPeak
Definition: freedvdemod.h:372
void accumulate(float snrdB)
virtual QString asJson() override
LevelRMS m_levelIn
Definition: freedvdemod.h:407
uint32_t m_speechSampleRate
Definition: freedvdemod.h:366
Exposes a single sink stream (input, Rx)
Definition: channelapi.h:41
void setFreeDvDemodReport(SWGFreeDVDemodReport *free_dv_demod_report)
int m_undersampleCount
Definition: freedvdemod.h:363
AudioDeviceManager * getAudioDeviceManager()
Definition: dspengine.h:55
void addChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Add a channel sink (Rx)
Definition: deviceapi.cpp:118
bool setSize(uint32_t numSamples)
Definition: audiofifo.cpp:59
void setFreeDvDemodSettings(SWGFreeDVDemodSettings *free_dv_demod_settings)
void resizeNew(uint32_t newSize, Real initial, Real cutoff=0, Real clip=0)
Definition: agc.h:95
SWGFreeDVDemodSettings * getFreeDvDemodSettings()
virtual QByteArray serialize() const
int runSSB(const cmplx &in, cmplx **out, bool usb, bool getDC=true)
Definition: fftfilt.cpp:284
struct freedv * m_freeDV
Definition: freedvdemod.h:396
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
uint32_t m_fps
frames per second
Definition: freedvdemod.h:235
QByteArray serialize() const
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
SimpleAGC< 4800 > m_simpleAGC
Definition: freedvdemod.h:375
Real m_hiCutoff
Definition: freedvdemod.h:358
void setUseReverseApi(qint32 use_reverse_api)
static MsgConfigureFreeDVDemodPrivate * create(Real Bandwidth, Real LowCutoff, Real volume, int spanLog2, bool audioBinaural, bool audioFlipChannels, bool dsb, bool audioMute, bool agc, bool agcClamping, int agcTimeLog2, int agcPowerThreshold, int agcThresholdGate)
Definition: freedvdemod.h:278
static int getHiCutoff(FreeDVMode freeDVMode)
int getIndexInDeviceSet() const
Definition: channelapi.h:87
void accumulate(float fsample)
std::complex< Real > Complex
Definition: dsptypes.h:43
void setChannelSampleRate(qint32 channel_sample_rate)
SampleVector m_sampleBuffer
Definition: freedvdemod.h:387
#define SDR_RX_SCALED
Definition: dsptypes.h:34
float Real
Definition: dsptypes.h:42
void processOneSample(Complex &ci)
static double powerFromdB(double powerdB)
Definition: db.cpp:36
fftfilt * SSBFilter
Definition: freedvdemod.h:384
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool positiveOnly)
static int getLowCutoff(FreeDVMode freeDVMode)
void networkManagerFinished(QNetworkReply *reply)
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
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
#define ssbFftLen
Definition: chanalyzer.h:38
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
static int getModSampleRate(FreeDVMode freeDVMode)
SWGFreeDVDemodReport * getFreeDvDemodReport()
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const FreeDVDemodSettings &settings, bool force)
DownChannelizer * m_channelizer
Definition: freedvdemod.h:355