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.
ammod.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 <stdio.h>
19 #include <complex.h>
20 
21 #include <QTime>
22 #include <QDebug>
23 #include <QMutexLocker>
24 #include <QNetworkAccessManager>
25 #include <QNetworkReply>
26 #include <QBuffer>
27 
28 #include "SWGChannelSettings.h"
29 #include "SWGChannelReport.h"
30 #include "SWGAMModReport.h"
31 
32 #include "ammod.h"
33 
34 #include "dsp/upchannelizer.h"
35 #include "dsp/dspengine.h"
37 #include "dsp/dspcommands.h"
38 #include "device/deviceapi.h"
39 #include "util/db.h"
40 
48 
49 const QString AMMod::m_channelIdURI = "sdrangel.channeltx.modam";
50 const QString AMMod::m_channelId ="AMMod";
51 const int AMMod::m_levelNbSamples = 480; // every 10ms
52 
53 AMMod::AMMod(DeviceAPI *deviceAPI) :
54  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
55  m_deviceAPI(deviceAPI),
56  m_basebandSampleRate(48000),
57  m_outputSampleRate(48000),
58  m_inputFrequencyOffset(0),
59  m_audioFifo(4800),
60  m_feedbackAudioFifo(48000),
61  m_settingsMutex(QMutex::Recursive),
62  m_fileSize(0),
63  m_recordLength(0),
64  m_sampleRate(48000),
65  m_levelCalcCount(0),
66  m_peakLevel(0.0f),
67  m_levelSum(0.0f)
68 {
69  setObjectName(m_channelId);
70 
71  m_audioBuffer.resize(1<<14);
73 
74  m_feedbackAudioBuffer.resize(1<<14);
76 
77  m_magsq = 0.0;
78 
81 
85 
88  m_cwKeyer.reset();
89 
92 
93  m_channelizer = new UpChannelizer(this);
97 
98  m_networkManager = new QNetworkAccessManager();
99  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
100 }
101 
103 {
104  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
105  delete m_networkManager;
108  delete m_threadedChannelizer;
109  delete m_channelizer;
112 }
113 
114 void AMMod::pull(Sample& sample)
115 {
117  {
118  sample.m_real = 0.0f;
119  sample.m_imag = 0.0f;
120  return;
121  }
122 
123  Complex ci;
124 
125  m_settingsMutex.lock();
126 
127  if (m_interpolatorDistance > 1.0f) // decimate
128  {
129  modulateSample();
130 
132  {
133  modulateSample();
134  }
135  }
136  else
137  {
139  {
140  modulateSample();
141  }
142  }
143 
145 
146  ci *= m_carrierNco.nextIQ(); // shift to carrier frequency
147 
148  m_settingsMutex.unlock();
149 
150  double magsq = ci.real() * ci.real() + ci.imag() * ci.imag();
151  magsq /= (SDR_TX_SCALED*SDR_TX_SCALED);
152  m_movingAverage(magsq);
154 
155  sample.m_real = (FixReal) ci.real();
156  sample.m_imag = (FixReal) ci.imag();
157 }
158 
159 void AMMod::pullAudio(int nbSamples)
160 {
161 // qDebug("AMMod::pullAudio: %d", nbSamples);
162  unsigned int nbAudioSamples = nbSamples * ((Real) m_audioSampleRate / (Real) m_basebandSampleRate);
163 
164  if (nbAudioSamples > m_audioBuffer.size())
165  {
166  m_audioBuffer.resize(nbAudioSamples);
167  }
168 
169  m_audioFifo.read(reinterpret_cast<quint8*>(&m_audioBuffer[0]), nbAudioSamples);
170  m_audioBufferFill = 0;
171 }
172 
174 {
175  Real t;
176 
177  pullAF(t);
178 
181  }
182 
183  calculateLevel(t);
185 
186  m_modSample.real((t*m_settings.m_modFactor + 1.0f) * 16384.0f); // modulate and scale zero frequency carrier
187  m_modSample.imag(0.0f);
188 }
189 
190 void AMMod::pullAF(Real& sample)
191 {
192  switch (m_settings.m_modAFInput)
193  {
195  sample = m_toneNco.next();
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 = 0.0f;
214  }
215  else
216  {
217  m_ifstream.read(reinterpret_cast<char*>(&sample), sizeof(Real));
218  sample *= m_settings.m_volumeFactor;
219  }
220  }
221  else
222  {
223  sample = 0.0f;
224  }
225  break;
228  break;
230  Real fadeFactor;
231 
232  if (m_cwKeyer.getSample())
233  {
234  m_cwKeyer.getCWSmoother().getFadeSample(true, fadeFactor);
235  sample = m_toneNco.next() * fadeFactor;
236  }
237  else
238  {
239  if (m_cwKeyer.getCWSmoother().getFadeSample(false, fadeFactor))
240  {
241  sample = m_toneNco.next() * fadeFactor;
242  }
243  else
244  {
245  sample = 0.0f;
246  m_toneNco.setPhase(0);
247  }
248  }
249  break;
251  default:
252  sample = 0.0f;
253  break;
254  }
255 }
256 
258 {
259  Complex c(sample, sample);
260  Complex ci;
261 
262  if (m_feedbackInterpolatorDistance < 1.0f) // interpolate
263  {
265  {
266  processOneSample(ci);
268  }
269  }
270  else // decimate
271  {
273  {
274  processOneSample(ci);
276  }
277  }
278 }
279 
281 {
285 
287  {
289 
290  if (res != m_feedbackAudioBufferFill)
291  {
292  qDebug("AMDemod::pushFeedback: %u/%u audio samples written m_feedbackInterpolatorDistance: %f",
295  }
296 
298  }
299 }
300 
302 {
304  {
305  m_peakLevel = std::max(std::fabs(m_peakLevel), sample);
306  m_levelSum += sample * sample;
308  }
309  else
310  {
311  qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples);
312  //qDebug("NFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel);
313  emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples);
314  m_peakLevel = 0.0f;
315  m_levelSum = 0.0f;
316  m_levelCalcCount = 0;
317  }
318 }
319 
321 {
322  qDebug() << "AMMod::start: m_outputSampleRate: " << m_outputSampleRate
323  << " m_inputFrequencyOffset: " << m_settings.m_inputFrequencyOffset;
324 
325  m_audioFifo.clear();
327 }
328 
330 {
331 }
332 
334 {
336  {
338  qDebug() << "AMMod::handleMessage: MsgChannelizerNotification:"
339  << " basebandSampleRate: " << notif.getBasebandSampleRate()
340  << " outputSampleRate: " << notif.getSampleRate()
341  << " inputFrequencyOffset: " << notif.getFrequencyOffset();
342 
344 
345  return true;
346  }
347  else if (MsgConfigureChannelizer::match(cmd))
348  {
350  qDebug() << "AMMod::handleMessage: MsgConfigureChannelizer:"
351  << " getSampleRate: " << cfg.getSampleRate()
352  << " getCenterFrequency: " << cfg.getCenterFrequency();
353 
355  cfg.getSampleRate(),
356  cfg.getCenterFrequency());
357 
358  return true;
359  }
360  else if (MsgConfigureAMMod::match(cmd))
361  {
362  MsgConfigureAMMod& cfg = (MsgConfigureAMMod&) cmd;
363  qDebug() << "AMMod::handleMessage: MsgConfigureAMMod";
364 
365  applySettings(cfg.getSettings(), cfg.getForce());
366 
367  return true;
368  }
369  else if (MsgConfigureFileSourceName::match(cmd))
370  {
372  m_fileName = conf.getFileName();
373  openFileStream();
374  return true;
375  }
376  else if (MsgConfigureFileSourceSeek::match(cmd))
377  {
379  int seekPercentage = conf.getPercentage();
380  seekFileStream(seekPercentage);
381 
382  return true;
383  }
385  {
386  std::size_t samplesCount;
387 
388  if (m_ifstream.eof()) {
389  samplesCount = m_fileSize / sizeof(Real);
390  } else {
391  samplesCount = m_ifstream.tellg() / sizeof(Real);
392  }
393 
395  report = MsgReportFileSourceStreamTiming::create(samplesCount);
396  getMessageQueueToGUI()->push(report);
397 
398  return true;
399  }
401  {
403 
406  }
407 
408  return true;
409  }
410  else if (DSPConfigureAudio::match(cmd))
411  {
412  DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd;
413  uint32_t sampleRate = cfg.getSampleRate();
414  DSPConfigureAudio::AudioType audioType = cfg.getAudioType();
415 
416  qDebug() << "AMMod::handleMessage: DSPConfigureAudio:"
417  << " sampleRate: " << sampleRate
418  << " audioType: " << audioType;
419 
420  if (audioType == DSPConfigureAudio::AudioInput)
421  {
422  if (sampleRate != m_audioSampleRate) {
423  applyAudioSampleRate(sampleRate);
424  }
425  }
426  else if (audioType == DSPConfigureAudio::AudioOutput)
427  {
428  if (sampleRate != m_audioSampleRate) {
429  applyFeedbackAudioSampleRate(sampleRate);
430  }
431  }
432 
433  return true;
434  }
435  else if (DSPSignalNotification::match(cmd))
436  {
437  return true;
438  }
439  else
440  {
441  return false;
442  }
443 }
444 
446 {
447  if (m_ifstream.is_open()) {
448  m_ifstream.close();
449  }
450 
451  m_ifstream.open(m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate);
452  m_fileSize = m_ifstream.tellg();
453  m_ifstream.seekg(0,std::ios_base::beg);
454 
455  m_sampleRate = 48000; // fixed rate
456  m_recordLength = m_fileSize / (sizeof(Real) * m_sampleRate);
457 
458  qDebug() << "AMMod::openFileStream: " << m_fileName.toStdString().c_str()
459  << " fileSize: " << m_fileSize << "bytes"
460  << " length: " << m_recordLength << " seconds";
461 
463  report = MsgReportFileSourceStreamData::create(m_sampleRate, m_recordLength);
464  getMessageQueueToGUI()->push(report);
465 }
466 
467 void AMMod::seekFileStream(int seekPercentage)
468 {
469  QMutexLocker mutexLocker(&m_settingsMutex);
470 
471  if (m_ifstream.is_open())
472  {
473  int seekPoint = ((m_recordLength * seekPercentage) / 100) * m_sampleRate;
474  seekPoint *= sizeof(Real);
475  m_ifstream.clear();
476  m_ifstream.seekg(seekPoint, std::ios::beg);
477  }
478 }
479 
480 void AMMod::applyAudioSampleRate(int sampleRate)
481 {
482  qDebug("AMMod::applyAudioSampleRate: %d", sampleRate);
483 
485  sampleRate, m_settings.m_inputFrequencyOffset);
486  m_inputMessageQueue.push(channelConfigMsg);
487 
488  m_settingsMutex.lock();
489 
491  m_interpolatorConsumed = false;
493  m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0);
495  m_cwKeyer.setSampleRate(sampleRate);
496 
497  m_settingsMutex.unlock();
498 
499  m_audioSampleRate = sampleRate;
501 }
502 
503 void AMMod::applyFeedbackAudioSampleRate(unsigned int sampleRate)
504 {
505  qDebug("AMMod::applyFeedbackAudioSampleRate: %u", sampleRate);
506 
507  m_settingsMutex.lock();
508 
512  Real cutoff = std::min(sampleRate, m_audioSampleRate) / 2.2f;
513  m_feedbackInterpolator.create(48, sampleRate, cutoff, 3.0);
514 
515  m_settingsMutex.unlock();
516 
517  m_feedbackAudioSampleRate = sampleRate;
518 }
519 
520 void AMMod::applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force)
521 {
522  qDebug() << "AMMod::applyChannelSettings:"
523  << " basebandSampleRate: " << basebandSampleRate
524  << " outputSampleRate: " << outputSampleRate
525  << " inputFrequencyOffset: " << inputFrequencyOffset;
526 
527  if ((inputFrequencyOffset != m_inputFrequencyOffset) ||
528  (outputSampleRate != m_outputSampleRate) || force)
529  {
530  m_settingsMutex.lock();
531  m_carrierNco.setFreq(inputFrequencyOffset, outputSampleRate);
532  m_settingsMutex.unlock();
533  }
534 
535  if ((outputSampleRate != m_outputSampleRate) || force)
536  {
537  m_settingsMutex.lock();
539  m_interpolatorConsumed = false;
540  m_interpolatorDistance = (Real) m_audioSampleRate / (Real) outputSampleRate;
542  m_settingsMutex.unlock();
543  }
544 
545  m_basebandSampleRate = basebandSampleRate;
546  m_outputSampleRate = outputSampleRate;
547  m_inputFrequencyOffset = inputFrequencyOffset;
548 }
549 
550 void AMMod::applySettings(const AMModSettings& settings, bool force)
551 {
552  qDebug() << "AMMod::applySettings:"
553  << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
554  << " m_rfBandwidth: " << settings.m_rfBandwidth
555  << " m_modFactor: " << settings.m_modFactor
556  << " m_toneFrequency: " << settings.m_toneFrequency
557  << " m_volumeFactor: " << settings.m_volumeFactor
558  << " m_channelMute: " << settings.m_channelMute
559  << " m_playLoop: " << settings.m_playLoop
560  << " m_modAFInput " << settings.m_modAFInput
561  << " m_audioDeviceName: " << settings.m_audioDeviceName
562  << " m_useReverseAPI: " << settings.m_useReverseAPI
563  << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
564  << " m_reverseAPIAddress: " << settings.m_reverseAPIPort
565  << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
566  << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
567  << " force: " << force;
568 
569  QList<QString> reverseAPIKeys;
570 
571  if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
572  reverseAPIKeys.append("inputFrequencyOffset");
573  }
574 
575  if ((settings.m_modFactor != m_settings.m_modFactor) || force) {
576  reverseAPIKeys.append("modFactor");
577  }
578 
579  if ((settings.m_volumeFactor != m_settings.m_volumeFactor) || force) {
580  reverseAPIKeys.append("volumeFactor");
581  }
582 
583  if ((settings.m_channelMute != m_settings.m_channelMute) || force) {
584  reverseAPIKeys.append("channelMute");
585  }
586 
587  if ((settings.m_playLoop != m_settings.m_playLoop) || force) {
588  reverseAPIKeys.append("playLoop");
589  }
590 
591  if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) {
592  reverseAPIKeys.append("modAFInput");
593  }
594 
595  if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
596  {
597  reverseAPIKeys.append("rfBandwidth");
598  m_settingsMutex.lock();
600  m_interpolatorConsumed = false;
602  m_interpolator.create(48, m_audioSampleRate, settings.m_rfBandwidth / 2.2, 3.0);
603  m_settingsMutex.unlock();
604  }
605 
606  if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force)
607  {
608  reverseAPIKeys.append("toneFrequency");
609  m_settingsMutex.lock();
611  m_settingsMutex.unlock();
612  }
613 
614  if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force)
615  {
616  reverseAPIKeys.append("audioDeviceName");
618  int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName);
619  audioDeviceManager->addAudioSource(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex);
620  uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex);
621 
622  if (m_audioSampleRate != audioSampleRate) {
623  reverseAPIKeys.append("audioSampleRate");
624  applyAudioSampleRate(audioSampleRate);
625  }
626  }
627 
629  {
630  reverseAPIKeys.append("feedbackAudioDeviceName");
632  int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName);
633  audioDeviceManager->addAudioSink(&m_feedbackAudioFifo, getInputMessageQueue(), audioDeviceIndex);
634  uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex);
635 
636  if (m_feedbackAudioSampleRate != audioSampleRate) {
637  reverseAPIKeys.append("feedbackAudioSampleRate");
638  applyFeedbackAudioSampleRate(audioSampleRate);
639  }
640  }
641 
642  if (settings.m_useReverseAPI)
643  {
644  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
649  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
650  }
651 
652  m_settings = settings;
653 }
654 
655 QByteArray AMMod::serialize() const
656 {
657  return m_settings.serialize();
658 }
659 
660 bool AMMod::deserialize(const QByteArray& data)
661 {
662  if (m_settings.deserialize(data))
663  {
666  return true;
667  }
668  else
669  {
673  return false;
674  }
675 }
676 
679  QString& errorMessage)
680 {
681  (void) errorMessage;
683  response.getAmModSettings()->init();
685  return 200;
686 }
687 
689  bool force,
690  const QStringList& channelSettingsKeys,
692  QString& errorMessage)
693 {
694  (void) errorMessage;
695  AMModSettings settings = m_settings;
696  bool frequencyOffsetChanged = false;
697 
698  if (channelSettingsKeys.contains("channelMute")) {
699  settings.m_channelMute = response.getAmModSettings()->getChannelMute() != 0;
700  }
701  if (channelSettingsKeys.contains("inputFrequencyOffset"))
702  {
704  frequencyOffsetChanged = true;
705  }
706  if (channelSettingsKeys.contains("modAFInput")) {
708  }
709  if (channelSettingsKeys.contains("audioDeviceName")) {
710  settings.m_audioDeviceName = *response.getAmModSettings()->getAudioDeviceName();
711  }
712  if (channelSettingsKeys.contains("playLoop")) {
713  settings.m_playLoop = response.getAmModSettings()->getPlayLoop() != 0;
714  }
715  if (channelSettingsKeys.contains("rfBandwidth")) {
716  settings.m_rfBandwidth = response.getAmModSettings()->getRfBandwidth();
717  }
718  if (channelSettingsKeys.contains("rgbColor")) {
719  settings.m_rgbColor = response.getAmModSettings()->getRgbColor();
720  }
721  if (channelSettingsKeys.contains("title")) {
722  settings.m_title = *response.getAmModSettings()->getTitle();
723  }
724  if (channelSettingsKeys.contains("toneFrequency")) {
725  settings.m_toneFrequency = response.getAmModSettings()->getToneFrequency();
726  }
727  if (channelSettingsKeys.contains("volumeFactor")) {
728  settings.m_volumeFactor = response.getAmModSettings()->getVolumeFactor();
729  }
730  if (channelSettingsKeys.contains("modFactor")) {
731  settings.m_modFactor = response.getAmModSettings()->getModFactor();
732  }
733  if (channelSettingsKeys.contains("useReverseAPI")) {
734  settings.m_useReverseAPI = response.getAmModSettings()->getUseReverseApi() != 0;
735  }
736  if (channelSettingsKeys.contains("reverseAPIAddress")) {
738  }
739  if (channelSettingsKeys.contains("reverseAPIPort")) {
740  settings.m_reverseAPIPort = response.getAmModSettings()->getReverseApiPort();
741  }
742  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
744  }
745  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
747  }
748 
749  if (channelSettingsKeys.contains("cwKeyer"))
750  {
751  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getAmModSettings()->getCwKeyer();
752  CWKeyerSettings cwKeyerSettings = m_cwKeyer.getSettings();
753  m_cwKeyer.webapiSettingsPutPatch(channelSettingsKeys, cwKeyerSettings, apiCwKeyerSettings);
754 
755  CWKeyer::MsgConfigureCWKeyer *msgCwKeyer = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
756  m_cwKeyer.getInputMessageQueue()->push(msgCwKeyer);
757 
758  if (m_guiMessageQueue) // forward to GUI if any
759  {
760  CWKeyer::MsgConfigureCWKeyer *msgCwKeyerToGUI = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
761  m_guiMessageQueue->push(msgCwKeyerToGUI);
762  }
763  }
764 
765  if (frequencyOffsetChanged)
766  {
769  m_inputMessageQueue.push(msgChan);
770  }
771 
772  MsgConfigureAMMod *msg = MsgConfigureAMMod::create(settings, force);
774 
775  if (m_guiMessageQueue) // forward to GUI if any
776  {
777  MsgConfigureAMMod *msgToGUI = MsgConfigureAMMod::create(settings, force);
778  m_guiMessageQueue->push(msgToGUI);
779  }
780 
781  webapiFormatChannelSettings(response, settings);
782 
783  return 200;
784 }
785 
788  QString& errorMessage)
789 {
790  (void) errorMessage;
792  response.getAmModReport()->init();
793  webapiFormatChannelReport(response);
794  return 200;
795 }
796 
798 {
799  response.getAmModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0);
801  response.getAmModSettings()->setModAfInput((int) settings.m_modAFInput);
802  response.getAmModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0);
803  response.getAmModSettings()->setRfBandwidth(settings.m_rfBandwidth);
804  response.getAmModSettings()->setModFactor(settings.m_modFactor);
805  response.getAmModSettings()->setRgbColor(settings.m_rgbColor);
806 
807  if (response.getAmModSettings()->getTitle()) {
808  *response.getAmModSettings()->getTitle() = settings.m_title;
809  } else {
810  response.getAmModSettings()->setTitle(new QString(settings.m_title));
811  }
812 
813  response.getAmModSettings()->setToneFrequency(settings.m_toneFrequency);
814  response.getAmModSettings()->setVolumeFactor(settings.m_volumeFactor);
815 
816  if (!response.getAmModSettings()->getCwKeyer()) {
818  }
819 
820  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getAmModSettings()->getCwKeyer();
821  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
822  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
823 
824  if (response.getAmModSettings()->getAudioDeviceName()) {
825  *response.getAmModSettings()->getAudioDeviceName() = settings.m_audioDeviceName;
826  } else {
827  response.getAmModSettings()->setAudioDeviceName(new QString(settings.m_audioDeviceName));
828  }
829 
830  response.getAmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
831 
832  if (response.getAmModSettings()->getReverseApiAddress()) {
834  } else {
835  response.getAmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
836  }
837 
841 }
842 
844 {
848 }
849 
850 void AMMod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const AMModSettings& settings, bool force)
851 {
853  swgChannelSettings->setDirection(1); // single source (Tx)
854  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
855  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
856  swgChannelSettings->setChannelType(new QString("AMMod"));
857  swgChannelSettings->setAmModSettings(new SWGSDRangel::SWGAMModSettings());
858  SWGSDRangel::SWGAMModSettings *swgAMModSettings = swgChannelSettings->getAmModSettings();
859 
860  // transfer data that has been modified. When force is on transfer all data except reverse API data
861 
862  if (channelSettingsKeys.contains("channelMute") || force) {
863  swgAMModSettings->setChannelMute(settings.m_channelMute ? 1 : 0);
864  }
865  if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
866  swgAMModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
867  }
868  if (channelSettingsKeys.contains("modAFInput") || force) {
869  swgAMModSettings->setModAfInput((int) settings.m_modAFInput);
870  }
871  if (channelSettingsKeys.contains("audioDeviceName") || force) {
872  swgAMModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
873  }
874  if (channelSettingsKeys.contains("playLoop") || force) {
875  swgAMModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0);
876  }
877  if (channelSettingsKeys.contains("rfBandwidth") || force) {
878  swgAMModSettings->setRfBandwidth(settings.m_rfBandwidth);
879  }
880  if (channelSettingsKeys.contains("rgbColor") || force) {
881  swgAMModSettings->setRgbColor(settings.m_rgbColor);
882  }
883  if (channelSettingsKeys.contains("title") || force) {
884  swgAMModSettings->setTitle(new QString(settings.m_title));
885  }
886  if (channelSettingsKeys.contains("toneFrequency") || force) {
887  swgAMModSettings->setToneFrequency(settings.m_toneFrequency);
888  }
889  if (channelSettingsKeys.contains("volumeFactor") || force) {
890  swgAMModSettings->setVolumeFactor(settings.m_volumeFactor);
891  }
892  if (channelSettingsKeys.contains("modFactor") || force) {
893  swgAMModSettings->setModFactor(settings.m_modFactor);
894  }
895 
896  if (force)
897  {
898  const CWKeyerSettings& cwKeyerSettings = m_cwKeyer.getSettings();
899  swgAMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
900  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgAMModSettings->getCwKeyer();
901  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
902  }
903 
904  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
905  .arg(settings.m_reverseAPIAddress)
906  .arg(settings.m_reverseAPIPort)
907  .arg(settings.m_reverseAPIDeviceIndex)
908  .arg(settings.m_reverseAPIChannelIndex);
909  m_networkRequest.setUrl(QUrl(channelSettingsURL));
910  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
911 
912  QBuffer *buffer=new QBuffer();
913  buffer->open((QBuffer::ReadWrite));
914  buffer->write(swgChannelSettings->asJson().toUtf8());
915  buffer->seek(0);
916 
917  // Always use PATCH to avoid passing reverse API settings
918  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
919 
920  delete swgChannelSettings;
921 }
922 
924 {
926  swgChannelSettings->setDirection(1); // single source (Tx)
927  swgChannelSettings->setChannelType(new QString("AMMod"));
928  swgChannelSettings->setAmModSettings(new SWGSDRangel::SWGAMModSettings());
929  SWGSDRangel::SWGAMModSettings *swgAMModSettings = swgChannelSettings->getAmModSettings();
930 
931  swgAMModSettings->setCwKeyer(new SWGSDRangel::SWGCWKeyerSettings());
932  SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = swgAMModSettings->getCwKeyer();
933  m_cwKeyer.webapiFormatChannelSettings(apiCwKeyerSettings, cwKeyerSettings);
934 
935  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
940  m_networkRequest.setUrl(QUrl(channelSettingsURL));
941  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
942 
943  QBuffer *buffer=new QBuffer();
944  buffer->open((QBuffer::ReadWrite));
945  buffer->write(swgChannelSettings->asJson().toUtf8());
946  buffer->seek(0);
947 
948  // Always use PATCH to avoid passing reverse API settings
949  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
950 
951  delete swgChannelSettings;
952 }
953 
954 void AMMod::networkManagerFinished(QNetworkReply *reply)
955 {
956  QNetworkReply::NetworkError replyError = reply->error();
957 
958  if (replyError)
959  {
960  qWarning() << "AMMod::networkManagerFinished:"
961  << " error(" << (int) replyError
962  << "): " << replyError
963  << ": " << reply->errorString();
964  return;
965  }
966 
967  QString answer = reply->readAll();
968  answer.chop(1); // remove last \n
969  qDebug("AMMod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
970 }
void applySettings(const AMModSettings &settings, bool force=false)
Definition: ammod.cpp:550
void setOriginatorChannelIndex(qint32 originator_channel_index)
void addAudioSink(AudioFifo *audioFifo, MessageQueue *sampleSinkMessageQueue, int outputDeviceIndex=-1)
Add the audio sink.
static const int m_levelNbSamples
Definition: ammod.h:318
void setModFactor(float mod_factor)
int getOutputSampleRate(int outputDeviceIndex=-1)
static const QString m_channelIdURI
Definition: ammod.h:249
void applyChannelSettings(int basebandSampleRate, int outputSampleRate, int inputFrequencyOffset, bool force=false)
Definition: ammod.cpp:520
void setAudioSampleRate(qint32 audio_sample_rate)
void setAudioDeviceName(QString *audio_device_name)
float m_feedbackVolumeFactor
Definition: ammodsettings.h:48
Complex nextIQ()
Return next complex sample.
Definition: nco.cpp:61
bool decimate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:38
int m_sampleRate
Definition: ammod.h:311
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport &response)
Definition: ammod.cpp:843
void setToneFrequency(float tone_frequency)
void push(Message *message, bool emitSignal=true)
Push message onto queue.
AudioVector m_feedbackAudioBuffer
Definition: ammod.h:300
Real m_interpolatorDistanceRemain
Definition: ammod.h:283
bool m_feedbackAudioEnable
Definition: ammodsettings.h:49
uint16_t m_reverseAPIDeviceIndex
Definition: ammodsettings.h:53
void removeChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:181
void setVolumeFactor(float volume_factor)
Real m_feedbackInterpolatorDistanceRemain
Definition: ammod.h:288
static double dbPower(double magsq, double floor=1e-12)
Definition: db.cpp:22
virtual int webapiReportGet(SWGSDRangel::SWGChannelReport &response, QString &errorMessage)
Definition: ammod.cpp:786
virtual void pullAudio(int nbSamples)
Definition: ammod.cpp:159
int getInputSampleRate(int inputDeviceIndex=-1)
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
Definition: ammod.cpp:333
int m_outputSampleRate
Definition: ammod.h:273
void pushFeedback(Real sample)
Definition: ammod.cpp:257
void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase=4.5)
int getSampleRate() const
Definition: ammod.h:78
bool interpolate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:53
const AMModSettings & getSettings() const
Definition: ammod.h:55
void seekFileStream(int seekPercentage)
Definition: ammod.cpp:467
void clear()
Definition: audiofifo.cpp:156
int getDeviceSetIndex() const
Definition: channelapi.h:89
int getOutputDeviceIndex(const QString &deviceName) const
double m_magsq
Definition: ammod.h:291
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
void setChannelPowerDb(float channel_power_db)
virtual void start()
Definition: ammod.cpp:320
void removeAudioSink(AudioFifo *audioFifo)
Remove the audio sink.
virtual bool deserialize(const QByteArray &data)
Definition: ammod.cpp:660
void setModAfInput(qint32 mod_af_input)
Interpolator m_interpolator
Definition: ammod.h:281
quint32 m_audioSampleRate
Definition: ammod.h:294
void setReverseApiAddress(QString *reverse_api_address)
void processOneSample(Complex &ci)
Definition: ammod.cpp:280
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
QByteArray serialize() const
QString m_reverseAPIAddress
Definition: ammodsettings.h:51
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
static MsgReportFileSourceStreamTiming * create(std::size_t samplesCount)
Definition: ammod.h:163
QNetworkRequest m_networkRequest
Definition: ammod.h:321
AudioFifo m_audioFifo
Definition: ammod.h:297
Real m_levelSum
Definition: ammod.h:315
void setChannelType(QString *channel_type)
void addChannelSource(ThreadedBasebandSampleSource *sink, int streamIndex=0)
Add a channel source (Tx)
Definition: deviceapi.cpp:138
const QString & getFileName() const
Definition: ammod.h:102
void setOriginatorDeviceSetIndex(qint32 originator_device_set_index)
Complex m_modSample
Definition: ammod.h:279
SWGCWKeyerSettings * getCwKeyer()
unsigned int uint32_t
Definition: rtptypes_win.h:46
uint m_feedbackAudioBufferFill
Definition: ammod.h:301
virtual QByteArray serialize() const
Definition: ammod.cpp:655
void addAudioSource(AudioFifo *audioFifo, MessageQueue *sampleSourceMessageQueue, int inputDeviceIndex=-1)
Add an audio source.
AudioType getAudioType() const
Definition: dspcommands.h:391
void setUseReverseApi(qint32 use_reverse_api)
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
Definition: cwkeyer.h:104
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void setPhase(Real phase)
Definition: ncof.h:42
double getMagSq() const
Definition: ammod.h:245
uint m_audioBufferFill
Definition: ammod.h:296
bool m_feedbackInterpolatorConsumed
Definition: ammod.h:289
float m_volumeFactor
Definition: ammodsettings.h:40
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
bool getFadeSample(bool on, float &sample)
Definition: cwkeyer.cpp:487
bool deserialize(const QByteArray &data)
std::ifstream m_ifstream
Definition: ammod.h:307
Real m_peakLevel
Definition: ammod.h:314
void resetToDefaults()
int getSampleRate() const
Definition: dspcommands.h:390
void setChannelMute(qint32 channel_mute)
static DSPEngine * instance()
Definition: dspengine.cpp:51
int m_inputFrequencyOffset
Definition: ammod.h:274
static MsgReportFileSourceStreamData * create(int sampleRate, quint32 recordLength)
Definition: ammod.h:184
CWSmoother & getCWSmoother()
Definition: cwkeyer.h:111
void setChannelSampleRate(qint32 channel_sample_rate)
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: ammod.cpp:677
void openFileStream()
Definition: ammod.cpp:445
quint64 m_fileSize
raw file size (bytes)
Definition: ammod.h:309
void pullAF(Real &sample)
Definition: ammod.cpp:190
QMutex m_settingsMutex
Definition: ammod.h:305
QString m_fileName
Definition: ammod.h:308
virtual void pull(Sample &sample)
Definition: ammod.cpp:114
FixReal m_real
Definition: dsptypes.h:64
AudioVector m_audioBuffer
Definition: ammod.h:295
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const AMModSettings &settings)
Definition: ammod.cpp:797
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const AMModSettings &settings, bool force)
Definition: ammod.cpp:850
CWKeyer m_cwKeyer
Definition: ammod.h:316
QNetworkAccessManager * m_networkManager
Definition: ammod.h:320
static bool match(const Message *message)
Definition: message.cpp:45
Real m_interpolatorDistance
Definition: ammod.h:282
void setRgbColor(qint32 rgb_color)
NCO m_carrierNco
Definition: ammod.h:277
void applyFeedbackAudioSampleRate(unsigned int sampleRate)
Definition: ammod.cpp:503
void setFreq(Real freq, Real sampleRate)
Definition: nco.cpp:49
quint32 m_rgbColor
Definition: ammodsettings.h:43
quint32 m_levelCalcCount
Definition: ammod.h:313
QString m_feedbackAudioDeviceName
This is the audio device you send the audio samples to for audio feedback.
Definition: ammodsettings.h:47
Fixed< IntType, IntBits > sqrt(Fixed< IntType, IntBits > const &x)
Definition: fixed.h:2283
static const QString m_channelId
Definition: ammod.h:250
uint32_t read(quint8 *data, uint32_t numSamples)
Definition: audiofifo.cpp:103
void webapiReverseSendCWSettings(const CWKeyerSettings &settings)
Definition: ammod.cpp:923
virtual QString asJson() override
uint16_t m_reverseAPIChannelIndex
Definition: ammodsettings.h:54
static void webapiFormatChannelSettings(SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings, const CWKeyerSettings &cwKeyerSettings)
Definition: cwkeyer.cpp:637
~AMMod()
Definition: ammod.cpp:102
int getSample()
Definition: cwkeyer.cpp:194
void setAmModSettings(SWGAMModSettings *am_mod_settings)
QString m_title
Definition: ammodsettings.h:44
void calculateLevel(Real &sample)
Definition: ammod.cpp:301
qint64 m_inputFrequencyOffset
Definition: ammodsettings.h:36
void setRfBandwidth(float rf_bandwidth)
ThreadedBasebandSampleSource * m_threadedChannelizer
Definition: ammod.h:269
MovingAverageUtil< double, double, 16 > m_movingAverage
Definition: ammod.h:292
AudioDeviceManager * getAudioDeviceManager()
Definition: dspengine.h:55
void networkManagerFinished(QNetworkReply *reply)
Definition: ammod.cpp:954
QString m_audioDeviceName
This is the audio device you get the audio samples from.
Definition: ammodsettings.h:46
virtual void stop()
Definition: ammod.cpp:329
DeviceAPI * m_deviceAPI
Definition: ammod.h:268
quint32 m_recordLength
record length in seconds computed from file size
Definition: ammod.h:310
static MsgConfigureChannelizer * create(int sampleRate, int centerFrequency)
Definition: ammod.h:81
void setInputFrequencyOffset(qint64 input_frequency_offset)
MessageQueue * getMessageQueueToGUI()
static void webapiSettingsPutPatch(const QStringList &channelSettingsKeys, CWKeyerSettings &cwKeyerSettings, SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings)
Definition: cwkeyer.cpp:599
void modulateSample()
Definition: ammod.cpp:173
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
NCOF m_toneNco
Definition: ammod.h:278
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
AMModSettings m_settings
Definition: ammod.h:275
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
AMMod(DeviceAPI *deviceAPI)
Definition: ammod.cpp:53
void applyAudioSampleRate(int sampleRate)
Definition: ammod.cpp:480
FixReal m_imag
Definition: dsptypes.h:65
int getInputDeviceIndex(const QString &deviceName) const
bool m_useReverseAPI
Definition: ammodsettings.h:50
void setCwKeyer(SWGCWKeyerSettings *cw_keyer)
void setPlayLoop(qint32 play_loop)
UpChannelizer * m_channelizer
Definition: ammod.h:270
void addChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:174
Interpolator m_feedbackInterpolator
Definition: ammod.h:286
float m_toneFrequency
Definition: ammodsettings.h:39
quint32 m_feedbackAudioSampleRate
Definition: ammod.h:299
bool m_interpolatorConsumed
Definition: ammod.h:284
int m_basebandSampleRate
Definition: ammod.h:272
void reset()
Definition: cwkeyer.h:109
const CWKeyerSettings & getSettings() const
Definition: cwkeyer.h:107
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
AMModInputAF m_modAFInput
Definition: ammodsettings.h:45
double asDouble() const
Definition: movingaverage.h:57
int getCenterFrequency() const
Definition: ammod.h:79
void setReverseApiPort(qint32 reverse_api_port)
float Real
Definition: dsptypes.h:42
#define SDR_TX_SCALED
Definition: dsptypes.h:40
AudioFifo m_feedbackAudioFifo
Definition: ammod.h:302
bool getForce() const
Definition: ammod.h:56
void setAmModReport(SWGAMModReport *am_mod_report)
void setSampleRate(int sampleRate)
Definition: cwkeyer.cpp:186
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: ammod.cpp:688
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
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
void removeAudioSource(AudioFifo *audioFifo)
Remove an audio source.
Real m_feedbackInterpolatorDistance
Definition: ammod.h:287
static MsgConfigureAMMod * create(const AMModSettings &settings, bool force)
Definition: ammod.h:58
void configure(MessageQueue *messageQueue, int sampleRate, int centerFrequency)
uint16_t m_reverseAPIPort
Definition: ammodsettings.h:52
T min(const T &x, const T &y)
Definition: framework.h:440