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.
localsink.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 "localsink.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/downchannelizer.h"
32 #include "dsp/dspcommands.h"
34 #include "dsp/dspengine.h"
35 #include "dsp/devicesamplesource.h"
37 #include "device/deviceapi.h"
38 
39 #include "localsinkthread.h"
40 
44 
45 const QString LocalSink::m_channelIdURI = "sdrangel.channel.localsink";
46 const QString LocalSink::m_channelId = "LocalSink";
47 
49  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
50  m_deviceAPI(deviceAPI),
51  m_running(false),
52  m_sinkThread(0),
53  m_centerFrequency(0),
54  m_frequencyOffset(0),
55  m_sampleRate(48000),
56  m_deviceSampleRate(48000)
57 {
58  setObjectName(m_channelId);
59 
60  m_channelizer = new DownChannelizer(this);
64 
65  m_networkManager = new QNetworkAccessManager();
66  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
67 }
68 
70 {
71  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
72  delete m_networkManager;
75  delete m_threadedChannelizer;
76  delete m_channelizer;
77 }
78 
79 void LocalSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
80 {
81  (void) firstOfBurst;
82  emit samplesAvailable((const quint8*) &(*begin), (end-begin)*sizeof(Sample));
83 }
84 
86 {
87  qDebug("LocalSink::start");
88 
89  if (m_running) {
90  stop();
91  }
92 
95 
96  if (deviceSource) {
97  m_sinkThread->setSampleFifo(deviceSource->getSampleFifo());
98  }
99 
100  connect(this,
101  SIGNAL(samplesAvailable(const quint8*, uint)),
102  m_sinkThread,
103  SLOT(processSamples(const quint8*, uint)),
104  Qt::QueuedConnection);
105 
106  m_sinkThread->startStop(true);
107  m_running = true;
108 }
109 
111 {
112  qDebug("LocalSink::stop");
113 
114  if (m_sinkThread != 0)
115  {
116  m_sinkThread->startStop(false);
117  m_sinkThread->deleteLater();
118  m_sinkThread = 0;
119  }
120 
121  m_running = false;
122 }
123 
125 {
127  {
129 
130  qDebug() << "LocalSink::handleMessage: MsgChannelizerNotification:"
131  << " channelSampleRate: " << notif.getSampleRate()
132  << " offsetFrequency: " << notif.getFrequencyOffset();
133 
134  if (notif.getSampleRate() > 0)
135  {
136  setSampleRate(notif.getSampleRate());
137  }
138 
139  return true;
140  }
141  else if (DSPSignalNotification::match(cmd))
142  {
144 
145  qDebug() << "LocalSink::handleMessage: DSPSignalNotification:"
146  << " inputSampleRate: " << notif.getSampleRate()
147  << " centerFrequency: " << notif.getCenterFrequency();
148 
151  calculateFrequencyOffset(); // This is when device sample rate changes
153 
154  // Redo the channelizer stuff with the new sample rate to re-synchronize everything
158 
159  if (m_guiMessageQueue)
160  {
162  m_guiMessageQueue->push(msg);
163  }
164 
165  return true;
166  }
167  else if (MsgConfigureLocalSink::match(cmd))
168  {
170  qDebug() << "LocalSink::handleMessage: MsgConfigureLocalSink";
171  applySettings(cfg.getSettings(), cfg.getForce());
172 
173  return true;
174  }
175  else if (MsgConfigureChannelizer::match(cmd))
176  {
180 
181  qDebug() << "LocalSink::handleMessage: MsgConfigureChannelizer:"
182  << " log2Decim: " << m_settings.m_log2Decim
183  << " filterChainHash: " << m_settings.m_filterChainHash;
184 
188 
189  calculateFrequencyOffset(); // This is when decimation or filter chain changes
191 
192  return true;
193  }
194  else
195  {
196  return false;
197  }
198 }
199 
200 QByteArray LocalSink::serialize() const
201 {
202  return m_settings.serialize();
203 }
204 
205 bool LocalSink::deserialize(const QByteArray& data)
206 {
207  (void) data;
208  if (m_settings.deserialize(data))
209  {
212  return true;
213  }
214  else
215  {
219  return false;
220  }
221 }
222 
223 void LocalSink::getLocalDevices(std::vector<uint32_t>& indexes)
224 {
225  indexes.clear();
226  DSPEngine *dspEngine = DSPEngine::instance();
227 
228  for (uint32_t i = 0; i < dspEngine->getDeviceSourceEnginesNumber(); i++)
229  {
230  DSPDeviceSourceEngine *deviceSourceEngine = dspEngine->getDeviceSourceEngineByIndex(i);
231  DeviceSampleSource *deviceSource = deviceSourceEngine->getSource();
232 
233  if (deviceSource->getDeviceDescription() == "LocalInput") {
234  indexes.push_back(i);
235  }
236  }
237 }
238 
240 {
241  DSPEngine *dspEngine = DSPEngine::instance();
242 
243  if (index < dspEngine->getDeviceSourceEnginesNumber())
244  {
245  DSPDeviceSourceEngine *deviceSourceEngine = dspEngine->getDeviceSourceEngineByIndex(index);
246  DeviceSampleSource *deviceSource = deviceSourceEngine->getSource();
247 
248  if (deviceSource->getDeviceDescription() == "LocalInput")
249  {
250  if (!getDeviceAPI()) {
251  qDebug("LocalSink::getLocalDevice: the parent device is unset");
252  } else if (getDeviceAPI()->getDeviceUID() == deviceSourceEngine->getUID()) {
253  qDebug("LocalSink::getLocalDevice: source device at index %u is the parent device", index);
254  } else {
255  return deviceSource;
256  }
257  }
258  else
259  {
260  qDebug("LocalSink::getLocalDevice: source device at index %u is not a Local Input source", index);
261  }
262  }
263  else
264  {
265  qDebug("LocalSink::getLocalDevice: non existent source device index: %u", index);
266  }
267 
268  return nullptr;
269 }
270 
272 {
273  DeviceSampleSource *deviceSource = getLocalDevice(index);
274 
275  if (deviceSource)
276  {
279  }
280 }
281 
282 void LocalSink::applySettings(const LocalSinkSettings& settings, bool force)
283 {
284  qDebug() << "LocalSink::applySettings:"
285  << " m_localDeviceIndex: " << settings.m_localDeviceIndex
286  << " force: " << force;
287 
288  QList<QString> reverseAPIKeys;
289 
290  if ((settings.m_localDeviceIndex != m_settings.m_localDeviceIndex) || force)
291  {
292  reverseAPIKeys.append("localDeviceIndex");
293  DeviceSampleSource *deviceSource = getLocalDevice(settings.m_localDeviceIndex);
294 
295  if (deviceSource)
296  {
297  if (m_sinkThread) {
298  m_sinkThread->setSampleFifo(deviceSource->getSampleFifo());
299  }
300 
302  }
303  else
304  {
305  qWarning("LocalSink::applySettings: invalid local device for index %u", settings.m_localDeviceIndex);
306  }
307  }
308 
309  if ((settings.m_useReverseAPI) && (reverseAPIKeys.size() != 0))
310  {
311  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
316  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
317  }
318 
319  m_settings = settings;
320 }
321 
323 {
324  unsigned int s = 1;
325 
326  for (unsigned int i = 0; i < settings.m_log2Decim; i++) {
327  s *= 3;
328  }
329 
330  settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash;
331 }
332 
334 {
336  m_frequencyOffset = m_deviceSampleRate * shiftFactor;
337 }
338 
341  QString& errorMessage)
342 {
343  (void) errorMessage;
345  response.getLocalSinkSettings()->init();
347  return 200;
348 }
349 
351  bool force,
352  const QStringList& channelSettingsKeys,
354  QString& errorMessage)
355 {
356  (void) errorMessage;
357  LocalSinkSettings settings = m_settings;
358 
359  if (channelSettingsKeys.contains("localDeviceIndex")) {
361  }
362  if (channelSettingsKeys.contains("rgbColor")) {
363  settings.m_rgbColor = response.getLocalSinkSettings()->getRgbColor();
364  }
365  if (channelSettingsKeys.contains("title")) {
366  settings.m_title = *response.getLocalSinkSettings()->getTitle();
367  }
368  if (channelSettingsKeys.contains("log2Decim")) {
369  settings.m_log2Decim = response.getLocalSinkSettings()->getLog2Decim();
370  }
371 
372  if (channelSettingsKeys.contains("filterChainHash"))
373  {
375  validateFilterChainHash(settings);
376  }
377 
378  if (channelSettingsKeys.contains("useReverseAPI")) {
379  settings.m_useReverseAPI = response.getLocalSinkSettings()->getUseReverseApi() != 0;
380  }
381  if (channelSettingsKeys.contains("reverseAPIAddress")) {
383  }
384  if (channelSettingsKeys.contains("reverseAPIPort")) {
386  }
387  if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
389  }
390  if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
392  }
393 
396 
397  if ((settings.m_log2Decim != m_settings.m_log2Decim) || (settings.m_filterChainHash != m_settings.m_filterChainHash) || force)
398  {
401  }
402 
403  qDebug("LocalSink::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
404  if (m_guiMessageQueue) // forward to GUI if any
405  {
406  MsgConfigureLocalSink *msgToGUI = MsgConfigureLocalSink::create(settings, force);
407  m_guiMessageQueue->push(msgToGUI);
408  }
409 
410  webapiFormatChannelSettings(response, settings);
411 
412  return 200;
413 }
414 
416 {
418  response.getLocalSinkSettings()->setRgbColor(settings.m_rgbColor);
419 
420  if (response.getLocalSinkSettings()->getTitle()) {
421  *response.getLocalSinkSettings()->getTitle() = settings.m_title;
422  } else {
423  response.getLocalSinkSettings()->setTitle(new QString(settings.m_title));
424  }
425 
426  response.getLocalSinkSettings()->setLog2Decim(settings.m_log2Decim);
428  response.getLocalSinkSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
429 
430  if (response.getLocalSinkSettings()->getReverseApiAddress()) {
432  } else {
433  response.getLocalSinkSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
434  }
435 
439 }
440 
441 void LocalSink::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const LocalSinkSettings& settings, bool force)
442 {
444  swgChannelSettings->setDirection(0); // single sink (Rx)
445  swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
446  swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
447  swgChannelSettings->setChannelType(new QString("LocalSink"));
448  swgChannelSettings->setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings());
449  SWGSDRangel::SWGLocalSinkSettings *swgLocalSinkSettings = swgChannelSettings->getLocalSinkSettings();
450 
451  // transfer data that has been modified. When force is on transfer all data except reverse API data
452 
453  if (channelSettingsKeys.contains("localDeviceIndex") || force) {
454  swgLocalSinkSettings->setLocalDeviceIndex(settings.m_localDeviceIndex);
455  }
456  if (channelSettingsKeys.contains("rgbColor") || force) {
457  swgLocalSinkSettings->setRgbColor(settings.m_rgbColor);
458  }
459  if (channelSettingsKeys.contains("title") || force) {
460  swgLocalSinkSettings->setTitle(new QString(settings.m_title));
461  }
462  if (channelSettingsKeys.contains("log2Decim") || force) {
463  swgLocalSinkSettings->setLog2Decim(settings.m_log2Decim);
464  }
465  if (channelSettingsKeys.contains("filterChainHash") || force) {
466  swgLocalSinkSettings->setFilterChainHash(settings.m_filterChainHash);
467  }
468 
469  QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
470  .arg(settings.m_reverseAPIAddress)
471  .arg(settings.m_reverseAPIPort)
472  .arg(settings.m_reverseAPIDeviceIndex)
473  .arg(settings.m_reverseAPIChannelIndex);
474  m_networkRequest.setUrl(QUrl(channelSettingsURL));
475  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
476 
477  QBuffer *buffer=new QBuffer();
478  buffer->open((QBuffer::ReadWrite));
479  buffer->write(swgChannelSettings->asJson().toUtf8());
480  buffer->seek(0);
481 
482  // Always use PATCH to avoid passing reverse API settings
483  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
484 
485  delete swgChannelSettings;
486 }
487 
488 void LocalSink::networkManagerFinished(QNetworkReply *reply)
489 {
490  QNetworkReply::NetworkError replyError = reply->error();
491 
492  if (replyError)
493  {
494  qWarning() << "LocalSink::networkManagerFinished:"
495  << " error(" << (int) replyError
496  << "): " << replyError
497  << ": " << reply->errorString();
498  return;
499  }
500 
501  QString answer = reply->readAll();
502  answer.chop(1); // remove last \n
503  qDebug("LocalSink::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
504 }
int64_t m_frequencyOffset
Definition: localsink.h:166
void networkManagerFinished(QNetworkReply *reply)
Definition: localsink.cpp:488
void setOriginatorChannelIndex(qint32 originator_channel_index)
uint64_t m_centerFrequency
Definition: localsink.h:165
virtual void setSampleRate(int sampleRate)=0
For when the source sample rate is set externally.
DeviceSampleSource * getLocalDevice(uint32_t index)
Definition: localsink.cpp:239
SampleSinkFifo * getSampleFifo()
void push(Message *message, bool emitSignal=true)
Push message onto queue.
void setReverseApiChannelIndex(qint32 reverse_api_channel_index)
virtual QByteArray serialize() const
Definition: localsink.cpp:200
void removeChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:163
void addChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:156
QByteArray serialize() const
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
void setLocalDeviceIndex(qint32 local_device_index)
virtual int webapiSettingsPutPatch(bool force, const QStringList &channelSettingsKeys, SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: localsink.cpp:350
int getDeviceSetIndex() const
Definition: channelapi.h:89
const LocalSinkSettings & getSettings() const
Definition: localsink.h:44
DeviceSampleSource * getSource()
DownChannelizer * m_channelizer
Definition: localsink.h:159
uint32_t m_deviceSampleRate
Definition: localsink.h:168
virtual void setCenterFrequency(qint64 centerFrequency)=0
bool m_running
Definition: localsink.h:160
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void setReverseApiAddress(QString *reverse_api_address)
DSPDeviceSourceEngine * getDeviceSourceEngineByIndex(uint deviceIndex)
Definition: dspengine.h:59
void setCenterFrequency(uint64_t centerFrequency)
Definition: localsink.h:142
void setChannelType(QString *channel_type)
void setLocalSinkSettings(SWGLocalSinkSettings *local_sink_settings)
void setOriginatorDeviceSetIndex(qint32 originator_device_set_index)
MessageQueue m_inputMessageQueue
Queue for asynchronous inbound communication.
void samplesAvailable(const quint8 *data, uint count)
virtual bool deserialize(const QByteArray &data)
Definition: localsink.cpp:205
unsigned int uint32_t
Definition: rtptypes_win.h:46
ThreadedBasebandSampleSink * m_threadedChannelizer
Definition: localsink.h:158
uint16_t m_reverseAPIChannelIndex
qint64 getCenterFrequency() const
Definition: dspcommands.h:329
void webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings &response, const LocalSinkSettings &settings)
Definition: localsink.cpp:415
virtual const QString & getDeviceDescription() const =0
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
virtual int webapiSettingsGet(SWGSDRangel::SWGChannelSettings &response, QString &errorMessage)
Definition: localsink.cpp:339
void setSampleRate(uint32_t sampleRate)
Definition: localsink.h:145
void applySettings(const LocalSinkSettings &settings, bool force=false)
Definition: localsink.cpp:282
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
LocalSink(DeviceAPI *deviceAPI)
Definition: localsink.cpp:48
static DSPEngine * instance()
Definition: dspengine.cpp:51
bool deserialize(const QByteArray &data)
virtual ~LocalSink()
Definition: localsink.cpp:69
void startStop(bool start)
int32_t i
Definition: decimators.h:244
static MsgSampleRateNotification * create(int sampleRate)
Definition: localsink.h:67
void setUseReverseApi(qint32 use_reverse_api)
QNetworkRequest m_networkRequest
Definition: localsink.h:171
void set(MessageQueue *messageQueue, unsigned int log2Decim, unsigned int filterChainHash)
static bool match(const Message *message)
Definition: message.cpp:45
void removeChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Remove a channel sink (Rx)
Definition: deviceapi.cpp:127
uint32_t getDeviceSourceEnginesNumber() const
Definition: dspengine.h:58
uint16_t m_reverseAPIDeviceIndex
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
Definition: localsink.cpp:124
virtual QString asJson() override
void propagateSampleRateAndFrequency(uint32_t index)
Definition: localsink.cpp:271
void webapiReverseSendSettings(QList< QString > &channelSettingsKeys, const LocalSinkSettings &settings, bool force)
Definition: localsink.cpp:441
static MsgConfigureLocalSink * create(const LocalSinkSettings &settings, bool force)
Definition: localsink.h:47
void addChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Add a channel sink (Rx)
Definition: deviceapi.cpp:118
virtual void start()
Definition: localsink.cpp:85
void setSampleFifo(SampleSinkFifo *sampleFifo)
virtual void stop()
Definition: localsink.cpp:110
int getSampleRate() const
Definition: dspcommands.h:328
static const QString m_channelId
Definition: localsink.h:151
SWGLocalSinkSettings * getLocalSinkSettings()
DeviceAPI * getDeviceAPI()
Definition: channelapi.h:91
DeviceAPI * m_deviceAPI
Definition: localsink.h:157
static MsgConfigureChannelizer * create(unsigned int log2Decim, unsigned int filterChainHash)
Definition: localsink.h:90
void setReverseApiPort(qint32 reverse_api_port)
LocalSinkSettings m_settings
Definition: localsink.h:162
int getIndexInDeviceSet() const
Definition: channelapi.h:87
LocalSinkThread * m_sinkThread
Definition: localsink.h:163
static const QString m_channelIdURI
Definition: localsink.h:150
void validateFilterChainHash(LocalSinkSettings &settings)
Definition: localsink.cpp:322
static double getShiftFactor(unsigned int log2, unsigned int chainHash)
QNetworkAccessManager * m_networkManager
Definition: localsink.h:170
void getLocalDevices(std::vector< uint32_t > &indexes)
Definition: localsink.cpp:223
void calculateFrequencyOffset()
Definition: localsink.cpp:333
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool po)
Definition: localsink.cpp:79
void setFilterChainHash(qint32 filter_chain_hash)