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.
localsource.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 "localsource.h"
19 
20 #include <boost/crc.hpp>
21 #include <boost/cstdint.hpp>
22 
23 #include <QNetworkAccessManager>
24 #include <QNetworkReply>
25 #include <QBuffer>
26 
27 #include "SWGChannelSettings.h"
28 
29 #include "util/simpleserializer.h"
31 #include "dsp/upchannelizer.h"
32 #include "dsp/dspcommands.h"
34 #include "dsp/dspengine.h"
35 #include "dsp/devicesamplesink.h"
37 #include "device/deviceapi.h"
38 
39 #include "localsourcethread.h"
40 
44 
45 const QString LocalSource::m_channelIdURI = "sdrangel.channel.localsource";
46 const QString LocalSource::m_channelId = "LocalSource";
47 
49  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSource),
50  m_deviceAPI(deviceAPI),
51  m_running(false),
52  m_sinkThread(nullptr),
53  m_localSampleSourceFifo(nullptr),
54  m_chunkSize(0),
55  m_localSamplesIndex(0),
56  m_localSamplesIndexOffset(0),
57  m_centerFrequency(0),
58  m_frequencyOffset(0),
59  m_sampleRate(48000),
60  m_deviceSampleRate(48000),
61  m_settingsMutex(QMutex::Recursive)
62 {
63  setObjectName(m_channelId);
64 
65  m_channelizer = new UpChannelizer(this);
69 
70  m_networkManager = new QNetworkAccessManager();
71  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
72 }
73 
75 {
76  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
77  delete m_networkManager;
80  delete m_threadedChannelizer;
81  delete m_channelizer;
82 }
83 
85 {
87  {
88  QMutexLocker mutexLocker(&m_settingsMutex);
90 
92  {
94  }
95  else
96  {
98 
99  if (m_localSamplesIndexOffset == 0) {
101  } else {
103  }
104 
105  emit pullSamples(m_chunkSize);
106  }
107  }
108  else
109  {
110  sample = Sample{0, 0};
111  }
112 }
113 
115 {
117  {
118  int destOffset = (m_localSamplesIndexOffset == 0 ? m_chunkSize : 0);
119  SampleVector::iterator beginSource;
120  SampleVector::iterator beginDestination = m_localSamples.begin() + destOffset;
121  m_localSampleSourceFifo->setIteratorFromOffset(beginSource, offset);
122  std::copy(beginSource, beginSource + m_chunkSize, beginDestination);
123  }
124 }
125 
126 void LocalSource::pullAudio(int nbSamples)
127 {
128  (void) nbSamples;
129 }
130 
132 {
133  qDebug("LocalSource::start");
134 
135  if (m_running) {
136  stop();
137  }
138 
141 
142  if (deviceSink)
143  {
144  m_localSampleSourceFifo = deviceSink->getSampleFifo();
146  m_localSamples.resize(2*m_chunkSize);
149  }
150  else
151  {
152  m_localSampleSourceFifo = nullptr;
153  }
154 
155  connect(this,
156  SIGNAL(pullSamples(unsigned int)),
157  m_sinkThread,
158  SLOT(pullSamples(unsigned int)),
159  Qt::QueuedConnection);
160 
161  connect(m_sinkThread,
162  SIGNAL(samplesAvailable(int)),
163  this,
164  SLOT(processSamples(int)),
165  Qt::QueuedConnection);
166 
167  m_sinkThread->startStop(true);
168  m_running = true;
169 }
170 
172 {
173  qDebug("LocalSource::stop");
174 
175  if (m_sinkThread != 0)
176  {
177  m_sinkThread->startStop(false);
178  m_sinkThread->deleteLater();
179  m_sinkThread = 0;
180  }
181 
182  m_running = false;
183 }
184 
186 {
188  {
190  int sampleRate = notif.getSampleRate();
191 
192  qDebug() << "LocalSource::handleMessage: MsgChannelizerNotification:"
193  << " channelSampleRate: " << sampleRate
194  << " offsetFrequency: " << notif.getFrequencyOffset();
195 
196  if (sampleRate > 0)
197  {
199  {
200  QMutexLocker mutexLocker(&m_settingsMutex);
201  m_localSampleSourceFifo->resize(sampleRate);
202  m_chunkSize = sampleRate / 8;
205  m_localSamples.resize(2*m_chunkSize);
206  }
207 
208  setSampleRate(sampleRate);
209  }
210 
211  return true;
212  }
213  else if (DSPSignalNotification::match(cmd))
214  {
216 
217  qDebug() << "LocalSource::handleMessage: DSPSignalNotification:"
218  << " inputSampleRate: " << notif.getSampleRate()
219  << " centerFrequency: " << notif.getCenterFrequency();
220 
223  calculateFrequencyOffset(); // This is when device sample rate changes
225 
226  // Redo the channelizer stuff with the new sample rate to re-synchronize everything
230 
231  if (m_guiMessageQueue)
232  {
234  m_guiMessageQueue->push(msg);
235  }
236 
237  return true;
238  }
239  else if (MsgConfigureLocalSource::match(cmd))
240  {
242  qDebug() << "LocalSource::handleMessage: MsgConfigureLocalSink";
243  applySettings(cfg.getSettings(), cfg.getForce());
244 
245  return true;
246  }
247  else if (MsgConfigureChannelizer::match(cmd))
248  {
252 
253  qDebug() << "LocalSource::handleMessage: MsgConfigureChannelizer:"
254  << " log2Interp: " << m_settings.m_log2Interp
255  << " filterChainHash: " << m_settings.m_filterChainHash;
256 
260 
261  calculateFrequencyOffset(); // This is when decimation or filter chain changes
263 
264  return true;
265  }
266  else
267  {
268  return false;
269  }
270 }
271 
272 QByteArray LocalSource::serialize() const
273 {
274  return m_settings.serialize();
275 }
276 
277 bool LocalSource::deserialize(const QByteArray& data)
278 {
279  (void) data;
280  if (m_settings.deserialize(data))
281  {
284  return true;
285  }
286  else
287  {
291  return false;
292  }
293 }
294 
295 void LocalSource::getLocalDevices(std::vector<uint32_t>& indexes)
296 {
297  indexes.clear();
298  DSPEngine *dspEngine = DSPEngine::instance();
299 
300  for (uint32_t i = 0; i < dspEngine->getDeviceSinkEnginesNumber(); i++)
301  {
302  DSPDeviceSinkEngine *deviceSinkEngine = dspEngine->getDeviceSinkEngineByIndex(i);
303  DeviceSampleSink *deviceSink = deviceSinkEngine->getSink();
304 
305  if (deviceSink->getDeviceDescription() == "LocalOutput") {
306  indexes.push_back(i);
307  }
308  }
309 }
310 
312 {
313  DSPEngine *dspEngine = DSPEngine::instance();
314 
315  if (index < dspEngine->getDeviceSinkEnginesNumber())
316  {
317  DSPDeviceSinkEngine *deviceSinkEngine = dspEngine->getDeviceSinkEngineByIndex(index);
318  DeviceSampleSink *deviceSink = deviceSinkEngine->getSink();
319 
320  if (deviceSink->getDeviceDescription() == "LocalOutput")
321  {
322  if (!getDeviceAPI()) {
323  qDebug("LocalSource::getLocalDevice: the parent device is unset");
324  } else if (getDeviceAPI()->getDeviceUID() == deviceSinkEngine->getUID()) {
325  qDebug("LocalSource::getLocalDevice: sink device at index %u is the parent device", index);
326  } else {
327  return deviceSink;
328  }
329  }
330  else
331  {
332  qDebug("LocalSource::getLocalDevice: sink device at index %u is not a Local Output source", index);
333  }
334  }
335  else
336  {
337  qDebug("LocalSource::getLocalDevice: non existent sink device index: %u", index);
338  }
339 
340  return nullptr;
341 }
342 
344 {
345  DeviceSampleSink *deviceSink = getLocalDevice(index);
346 
347  if (deviceSink)
348  {
351  }
352 }
353 
354 void LocalSource::applySettings(const LocalSourceSettings& settings, bool force)
355 {
356  qDebug() << "LocalSource::applySettings:"
357  << " m_localDeviceIndex: " << settings.m_localDeviceIndex
358  << " force: " << force;
359 
360  QList<QString> reverseAPIKeys;
361 
362  if ((settings.m_localDeviceIndex != m_settings.m_localDeviceIndex) || force)
363  {
364  reverseAPIKeys.append("localDeviceIndex");
365  DeviceSampleSink *deviceSink = getLocalDevice(settings.m_localDeviceIndex);
366 
367  if (deviceSink)
368  {
369  if (m_sinkThread) {
370  m_sinkThread->setSampleFifo(deviceSink->getSampleFifo());
371  }
372 
374  }
375  else
376  {
377  qWarning("LocalSource::applySettings: invalid local device for index %u", settings.m_localDeviceIndex);
378  }
379  }
380 
381  if ((settings.m_useReverseAPI) && (reverseAPIKeys.size() != 0))
382  {
383  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
388  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
389  }
390 
391  m_settings = settings;
392 }
393 
395 {
396  unsigned int s = 1;
397 
398  for (unsigned int i = 0; i < settings.m_log2Interp; i++) {
399  s *= 3;
400  }
401 
402  settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash;
403 }
404 
406 {
408  m_frequencyOffset = m_deviceSampleRate * shiftFactor;
409 }
410 
413  QString& errorMessage)
414 {
415  (void) errorMessage;
417  response.getLocalSourceSettings()->init();
419  return 200;
420 }
421 
423  bool force,
424  const QStringList& channelSettingsKeys,
426  QString& errorMessage)
427 {
428  (void) errorMessage;
429  LocalSourceSettings settings = m_settings;
430 
431  if (channelSettingsKeys.contains("localDeviceIndex")) {
433  }
434  if (channelSettingsKeys.contains("rgbColor")) {
435  settings.m_rgbColor = response.getLocalSourceSettings()->getRgbColor();
436  }
437  if (channelSettingsKeys.contains("title")) {
438  settings.m_title = *response.getLocalSourceSettings()->getTitle();
439  }
440  if (channelSettingsKeys.contains("log2Interp")) {
441  settings.m_log2Interp = response.getLocalSourceSettings()->getLog2Interp();
442  }
443 
444  if (channelSettingsKeys.contains("filterChainHash"))
445  {
447  validateFilterChainHash(settings);
448  }
449 
450  if (channelSettingsKeys.contains("useReverseAPI")) {
451  settings.m_useReverseAPI = response.getLocalSourceSettings()->getUseReverseApi() != 0;
452  }
453  if (channelSettingsKeys.contains("reverseAPIAddress")) {
455  }
456  if (channelSettingsKeys.contains("reverseAPIPort")) {
458  }
459  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
461  }
462  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
464  }
465 
468 
469  if ((settings.m_log2Interp != m_settings.m_log2Interp) || (settings.m_filterChainHash != m_settings.m_filterChainHash) || force)
470  {
473  }
474 
475  qDebug("LocalSource::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
476  if (m_guiMessageQueue) // forward to GUI if any
477  {
478  MsgConfigureLocalSource *msgToGUI = MsgConfigureLocalSource::create(settings, force);
479  m_guiMessageQueue->push(msgToGUI);
480  }
481 
482  webapiFormatChannelSettings(response, settings);
483 
484  return 200;
485 }
486 
488 {
490  response.getLocalSourceSettings()->setRgbColor(settings.m_rgbColor);
491 
492  if (response.getLocalSourceSettings()->getTitle()) {
493  *response.getLocalSourceSettings()->getTitle() = settings.m_title;
494  } else {
495  response.getLocalSourceSettings()->setTitle(new QString(settings.m_title));
496  }
497 
498  response.getLocalSourceSettings()->setLog2Interp(settings.m_log2Interp);
500  response.getLocalSourceSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
501 
502  if (response.getLocalSourceSettings()->getReverseApiAddress()) {
504  } else {
505  response.getLocalSourceSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
506  }
507 
511 }
512 
513 void LocalSource::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const LocalSourceSettings& settings, bool force)
514 {
516  swgChannelSettings->setDirection(1); // single source (Tx)
517  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
518  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
519  swgChannelSettings->setChannelType(new QString("LocalSource"));
521  SWGSDRangel::SWGLocalSourceSettings *swgLocalSourceSettings = swgChannelSettings->getLocalSourceSettings();
522 
523  // transfer data that has been modified. When force is on transfer all data except reverse API data
524 
525  if (channelSettingsKeys.contains("localDeviceIndex") || force) {
526  swgLocalSourceSettings->setLocalDeviceIndex(settings.m_localDeviceIndex);
527  }
528  if (channelSettingsKeys.contains("rgbColor") || force) {
529  swgLocalSourceSettings->setRgbColor(settings.m_rgbColor);
530  }
531  if (channelSettingsKeys.contains("title") || force) {
532  swgLocalSourceSettings->setTitle(new QString(settings.m_title));
533  }
534  if (channelSettingsKeys.contains("log2Interp") || force) {
535  swgLocalSourceSettings->setLog2Interp(settings.m_log2Interp);
536  }
537  if (channelSettingsKeys.contains("filterChainHash") || force) {
538  swgLocalSourceSettings->setFilterChainHash(settings.m_filterChainHash);
539  }
540 
541  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
542  .arg(settings.m_reverseAPIAddress)
543  .arg(settings.m_reverseAPIPort)
544  .arg(settings.m_reverseAPIDeviceIndex)
545  .arg(settings.m_reverseAPIChannelIndex);
546  m_networkRequest.setUrl(QUrl(channelSettingsURL));
547  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
548 
549  QBuffer *buffer=new QBuffer();
550  buffer->open((QBuffer::ReadWrite));
551  buffer->write(swgChannelSettings->asJson().toUtf8());
552  buffer->seek(0);
553 
554  // Always use PATCH to avoid passing reverse API settings
555  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
556 
557  delete swgChannelSettings;
558 }
559 
560 void LocalSource::networkManagerFinished(QNetworkReply *reply)
561 {
562  QNetworkReply::NetworkError replyError = reply->error();
563 
564  if (replyError)
565  {
566  qWarning() << "LocalSource::networkManagerFinished:"
567  << " error(" << (int) replyError
568  << "): " << replyError
569  << ": " << reply->errorString();
570  return;
571  }
572 
573  QString answer = reply->readAll();
574  answer.chop(1); // remove last \n
575  qDebug("LocalSource::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
576 }
uint32_t size() const
void setOriginatorChannelIndex(qint32 originator_channel_index)
void pullSamples(unsigned int count)
void processSamples(int offset)
virtual void start()
DeviceSampleSink * getSink()
SWGLocalSourceSettings * getLocalSourceSettings()
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
static MsgConfigureChannelizer * create(unsigned int m_log2Interp, unsigned int m_filterChainHash)
Definition: localsource.h:91
void push(Message *message, bool emitSignal=true)
Push message onto queue.
static const QString m_channelId
Definition: localsource.h:153
ThreadedBasebandSampleSource * m_threadedChannelizer
Definition: localsource.h:160
SampleSourceFifo * m_localSampleSourceFifo
Definition: localsource.h:166
void removeChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:181
uint64_t m_centerFrequency
Definition: localsource.h:172
const LocalSourceSettings & getSettings() const
Definition: localsource.h:45
virtual bool deserialize(const QByteArray &data)
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
int getDeviceSetIndex() const
Definition: channelapi.h:89
static MsgConfigureLocalSource * create(const LocalSourceSettings &settings, bool force)
Definition: localsource.h:48
void networkManagerFinished(QNetworkReply *reply)
void removeChannelSource(ThreadedBasebandSampleSource *sink, int streamIndex=0)
Remove a channel source (Tx)
Definition: deviceapi.cpp:147
QNetworkAccessManager * m_networkManager
Definition: localsource.h:177
void setSampleRate(uint32_t sampleRate)
Definition: localsource.h:147
static const QString m_channelIdURI
Definition: localsource.h:152
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
void setLocalDeviceIndex(qint32 local_device_index)
QNetworkRequest m_networkRequest
Definition: localsource.h:178
void getLocalDevices(std::vector< uint32_t > &indexes)
int m_localSamplesIndex
Definition: localsource.h:169
virtual void setCenterFrequency(qint64 centerFrequency)=0
LocalSource(DeviceAPI *deviceAPI)
Definition: localsource.cpp:48
void calculateFrequencyOffset()
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)
static MsgSampleRateNotification * create(int sampleRate)
Definition: localsource.h:68
void startStop(bool start)
void set(MessageQueue *messageQueue, unsigned int log2Interp, unsigned int filterChainHash)
void setSampleFifo(SampleSourceFifo *sampleFifo)
void setUseReverseApi(qint32 use_reverse_api)
unsigned int uint32_t
Definition: rtptypes_win.h:46
virtual QByteArray serialize() const
virtual void pull(Sample &sample)
Definition: localsource.cpp:84
qint64 getCenterFrequency() const
Definition: dspcommands.h:329
int64_t m_frequencyOffset
Definition: localsource.h:173
QByteArray serialize() const
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
void validateFilterChainHash(LocalSourceSettings &settings)
uint32_t m_deviceSampleRate
Definition: localsource.h:175
static DSPEngine * instance()
Definition: dspengine.cpp:51
SampleSourceFifo * getSampleFifo()
DeviceSampleSink * getLocalDevice(uint32_t index)
virtual void pullAudio(int nbSamples)
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
uint32_t getUID() const
uint32_t getDeviceSinkEnginesNumber() const
Definition: dspengine.h:62
int32_t i
Definition: decimators.h:244
void setReverseApiPort(qint32 reverse_api_port)
static bool match(const Message *message)
Definition: message.cpp:45
UpChannelizer * m_channelizer
Definition: localsource.h:161
virtual void setSampleRate(int sampleRate)=0
For when the sink sample rate is set externally.
DeviceAPI * m_deviceAPI
Definition: localsource.h:159
bool deserialize(const QByteArray &data)
QMutex m_settingsMutex
Definition: localsource.h:180
virtual QString asJson() override
void resize(uint32_t size)
void setIteratorFromOffset(SampleVector::iterator &iterator, int offset)
void propagateSampleRateAndFrequency(uint32_t index)
void setCenterFrequency(uint64_t centerFrequency)
Definition: localsource.h:144
bool m_running
Definition: localsource.h:162
void applySettings(const LocalSourceSettings &settings, bool force=false)
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
int getSampleRate() const
Definition: dspcommands.h:328
void setReverseApiAddress(QString *reverse_api_address)
void setFilterChainHash(qint32 filter_chain_hash)
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
LocalSourceThread * m_sinkThread
Definition: localsource.h:165
int m_localSamplesIndexOffset
Definition: localsource.h:170
DeviceAPI * getDeviceAPI()
Definition: channelapi.h:91
void addChannelSourceAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:174
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const LocalSourceSettings &settings, bool force)
virtual void stop()
int getIndexInDeviceSet() const
Definition: channelapi.h:87
SampleVector m_localSamples
Definition: localsource.h:168
virtual ~LocalSource()
Definition: localsource.cpp:74
DSPDeviceSinkEngine * getDeviceSinkEngineByIndex(uint deviceIndex)
Definition: dspengine.h:63
static double getShiftFactor(unsigned int log2, unsigned int chainHash)
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const LocalSourceSettings &settings)
virtual const QString & getDeviceDescription() const =0
LocalSourceSettings m_settings
Definition: localsource.h:164
void setLocalSourceSettings(SWGLocalSourceSettings *local_source_settings)