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.
bladerf2output.cpp
Go to the documentation of this file.
1 // Copyright (C) 2018 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 
19 #include <string.h>
20 #include <errno.h>
21 
22 #include <QDebug>
23 #include <QNetworkReply>
24 #include <QBuffer>
25 
26 #include "SWGDeviceState.h"
27 #include "SWGDeviceSettings.h"
29 #include "SWGDeviceReport.h"
31 
32 #include "dsp/dspcommands.h"
33 #include "dsp/dspengine.h"
34 #include "device/deviceapi.h"
37 
38 #include "bladerf2outputthread.h"
39 #include "bladerf2output.h"
40 
44 
46  m_deviceAPI(deviceAPI),
47  m_settings(),
48  m_dev(0),
49  m_thread(0),
50  m_deviceDescription("BladeRF2Output"),
51  m_running(false)
52 {
53  openDevice();
54  m_deviceAPI->setNbSinkStreams(1);
55  m_networkManager = new QNetworkAccessManager();
56  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
57 }
58 
60 {
61  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
62  delete m_networkManager;
63 
64  if (m_running) {
65  stop();
66  }
67 
68  closeDevice();
69 }
70 
72 {
73  delete this;
74 }
75 
77 {
79 
80  // look for Tx buddies and get reference to the device object
81  if (m_deviceAPI->getSinkBuddies().size() > 0) // look sink sibling first
82  {
83  qDebug("BladeRF2Output::openDevice: look in Tx buddies");
84 
85  DeviceAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0];
86  DeviceBladeRF2Shared *deviceBladeRF2Shared = (DeviceBladeRF2Shared*) sinkBuddy->getBuddySharedPtr();
87 
88  if (deviceBladeRF2Shared == 0)
89  {
90  qCritical("BladeRF2Output::openDevice: the sink buddy shared pointer is null");
91  return false;
92  }
93 
94  DeviceBladeRF2 *device = deviceBladeRF2Shared->m_dev;
95 
96  if (device == 0)
97  {
98  qCritical("BladeRF2Output::openDevice: cannot get device pointer from Tx buddy");
99  return false;
100  }
101 
102  m_deviceShared.m_dev = device;
103  }
104  // look for Rx buddies and get reference to the device object
105  else if (m_deviceAPI->getSourceBuddies().size() > 0) // then source
106  {
107  qDebug("BladeRF2Output::openDevice: look in Rx buddies");
108 
109  DeviceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
110  DeviceBladeRF2Shared *deviceBladeRF2Shared = (DeviceBladeRF2Shared*) sourceBuddy->getBuddySharedPtr();
111 
112  if (deviceBladeRF2Shared == 0)
113  {
114  qCritical("BladeRF2Output::openDevice: the source buddy shared pointer is null");
115  return false;
116  }
117 
118  DeviceBladeRF2 *device = deviceBladeRF2Shared->m_dev;
119 
120  if (device == 0)
121  {
122  qCritical("BladeRF2Output::openDevice: cannot get device pointer from Rx buddy");
123  return false;
124  }
125 
126  m_deviceShared.m_dev = device;
127  }
128  // There are no buddies then create the first BladeRF2 device
129  else
130  {
131  qDebug("BladeRF2Output::openDevice: open device here");
132 
134  char serial[256];
135  strcpy(serial, qPrintable(m_deviceAPI->getSamplingDeviceSerial()));
136 
137  if (!m_deviceShared.m_dev->open(serial))
138  {
139  qCritical("BladeRF2Output::openDevice: cannot open BladeRF2 device");
140  return false;
141  }
142  }
143 
144  m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel
145  m_deviceShared.m_sink = this;
146  m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API
147  return true;
148 }
149 
151 {
152  if (m_deviceShared.m_dev == 0) { // was never open
153  return;
154  }
155 
156  if (m_running) {
157  stop();
158  }
159 
160  if (m_thread) { // stills own the thread => transfer to a buddy
162  }
163 
164  m_deviceShared.m_channel = -1; // publicly release channel
166 
167  // No buddies so effectively close the device
168 
169  if ((m_deviceAPI->getSinkBuddies().size() == 0) && (m_deviceAPI->getSourceBuddies().size() == 0))
170  {
172  delete m_deviceShared.m_dev;
173  m_deviceShared.m_dev = 0;
174  }
175 }
176 
178 {
179  applySettings(m_settings, true);
180 }
181 
183 {
184  if (m_thread == 0) // this does not own the thread
185  {
186  BladeRF2OutputThread *bladeRF2OutputThread = 0;
187 
188  // find a buddy that has allocated the thread
189  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
190  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
191 
192  for (; it != sinkBuddies.end(); ++it)
193  {
194  BladeRF2Output *buddySink = ((DeviceBladeRF2Shared*) (*it)->getBuddySharedPtr())->m_sink;
195 
196  if (buddySink)
197  {
198  bladeRF2OutputThread = buddySink->getThread();
199 
200  if (bladeRF2OutputThread) {
201  break;
202  }
203  }
204  }
205 
206  return bladeRF2OutputThread;
207  }
208  else
209  {
210  return m_thread; // own thread
211  }
212 }
213 
215 {
216  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
217  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
218 
219  for (; it != sinkBuddies.end(); ++it)
220  {
221  BladeRF2Output *buddySink = ((DeviceBladeRF2Shared*) (*it)->getBuddySharedPtr())->m_sink;
222 
223  if (buddySink)
224  {
225  buddySink->setThread(m_thread);
226  m_thread = 0; // zero for others
227  }
228  }
229 }
230 
232 {
233  // There is a single thread per physical device (Tx side). This thread is unique and referenced by a unique
234  // buddy in the group of sink buddies associated with this physical device.
235  //
236  // This start method is responsible for managing the thread and channel enabling when the streaming of a Tx channel is started
237  //
238  // It checks the following conditions
239  // - the thread is allocated or not (by itself or one of its buddies). If it is it grabs the thread pointer.
240  // - the requested channel is the first (0) or the following (just 1 in BladeRF 2 case)
241  //
242  // The BladeRF support library lets you work in two possible modes:
243  // - Single Output (SO) with only one channel streaming. This HAS to be channel 0.
244  // - Multiple Output (MO) with two channels streaming using interleaved samples. It MUST be in this configuration if channel 1
245  // is used. When we will run with only channel 1 streaming from the client perspective the channel 0 will actually be enabled
246  // and streaming but zero samples will be sent to it.
247  //
248  // It manages the transition form SO where only one channel (the first or channel 0) should be running to the
249  // Multiple Output (MO) if the requested channel is 1. More generally it checks if the requested channel is within the current
250  // channel range allocated in the thread or past it. To perform the transition it stops the thread, deletes it and creates a new one.
251  // It marks the thread as needing start.
252  //
253  // If the requested channel is within the thread channel range (this thread being already allocated) it simply removes its FIFO reference
254  // so that the samples are not taken from the FIFO anymore and leaves the thread unchanged (no stop, no delete/new)
255  //
256  // If there is no thread allocated it creates a new one with a number of channels that fits the requested channel. That is
257  // 1 if channel 0 is requested (SO mode) and 2 if channel 1 is requested (MO mode). It marks the thread as needing start.
258  //
259  // Eventually it registers the FIFO in the thread. If the thread has to be started it enables the channels up to the number of channels
260  // allocated in the thread and starts the thread.
261 
262  if (!m_deviceShared.m_dev)
263  {
264  qDebug("BladeRF2Output::start: no device object");
265  return false;
266  }
267 
268  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
269  BladeRF2OutputThread *bladeRF2OutputThread = findThread();
270  bool needsStart = false;
271 
272  if (bladeRF2OutputThread) // if thread is already allocated
273  {
274  qDebug("BladeRF2Output::start: thread is already allocated");
275 
276  int nbOriginalChannels = bladeRF2OutputThread->getNbChannels();
277 
278  if (requestedChannel+1 > nbOriginalChannels) // expansion by deleting and re-creating the thread
279  {
280  qDebug("BladeRF2Output::start: expand channels. Re-allocate thread and take ownership");
281 
282  SampleSourceFifo **fifos = new SampleSourceFifo*[nbOriginalChannels];
283  unsigned int *log2Interps = new unsigned int[nbOriginalChannels];
284 
285  for (int i = 0; i < nbOriginalChannels; i++) // save original FIFO references and data
286  {
287  fifos[i] = bladeRF2OutputThread->getFifo(i);
288  log2Interps[i] = bladeRF2OutputThread->getLog2Interpolation(i);
289  }
290 
291  bladeRF2OutputThread->stopWork();
292  delete bladeRF2OutputThread;
293  bladeRF2OutputThread = new BladeRF2OutputThread(m_deviceShared.m_dev->getDev(), requestedChannel+1);
294  m_thread = bladeRF2OutputThread; // take ownership
295 
296  for (int i = 0; i < nbOriginalChannels; i++) // restore original FIFO references
297  {
298  bladeRF2OutputThread->setFifo(i, fifos[i]);
299  bladeRF2OutputThread->setLog2Interpolation(i, log2Interps[i]);
300  }
301 
302  // remove old thread address from buddies (reset in all buddies)
303  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
304  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
305 
306  for (; it != sinkBuddies.end(); ++it) {
307  ((DeviceBladeRF2Shared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
308  }
309 
310  // close all channels
311  for (int i = bladeRF2OutputThread->getNbChannels()-1; i >= 0; i--) {
313  }
314 
315  // was used as temporary storage:
316  delete[] fifos;
317  delete[] log2Interps;
318 
319  needsStart = true;
320  }
321  else
322  {
323  qDebug("BladeRF2Output::start: keep buddy thread");
324  }
325  }
326  else // first allocation
327  {
328  qDebug("BladeRF2Output::start: allocate thread and take ownership");
329  bladeRF2OutputThread = new BladeRF2OutputThread(m_deviceShared.m_dev->getDev(), requestedChannel+1);
330  m_thread = bladeRF2OutputThread; // take ownership
331  needsStart = true;
332  }
333 
334  bladeRF2OutputThread->setFifo(requestedChannel, &m_sampleSourceFifo);
335  bladeRF2OutputThread->setLog2Interpolation(requestedChannel, m_settings.m_log2Interp);
336 
337  applySettings(m_settings, true); // re-apply forcibly to set sample rate with the new number of channels
338 
339  if (needsStart)
340  {
341  qDebug("BladeRF2Output::start: enabling channel(s) and (re)starting the thread");
342 
343  for (unsigned int i = 0; i < bladeRF2OutputThread->getNbChannels(); i++) // open all channels
344  {
345  if (!m_deviceShared.m_dev->openTx(i)) {
346  qCritical("BladeRF2Output::start: channel %u cannot be enabled", i);
347  }
348  }
349 
350  bladeRF2OutputThread->startWork();
351  }
352 
353  qDebug("BladeRF2Output::start: started");
354  m_running = true;
355 
356  return true;
357 }
358 
360 {
361  // This stop method is responsible for managing the thread and channel disabling when the streaming of
362  // a Tx channel is stopped
363  //
364  // If the thread is currently managing only one channel (SO mode). The thread can be just stopped and deleted.
365  // Then the channel is closed (disabled).
366  //
367  // If the thread is currently managing many channels (MO mode) and we are removing the last channel. The transition
368  // from MO to SO or reduction of MO size is handled by stopping the thread, deleting it and creating a new one
369  // with one channel less if (and only if) there is still a channel active.
370  //
371  // If the thread is currently managing many channels (MO mode) but the channel being stopped is not the last
372  // channel then the FIFO reference is simply removed from the thread so that this FIFO will not be used anymore.
373  // In this case the channel is not closed (disabled) so that other channels can continue with the
374  // same configuration. The device continues streaming on this channel but the samples are set to all zeros.
375 
376  if (!m_running) {
377  return;
378  }
379 
380  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
381  BladeRF2OutputThread *bladeRF2OutputThread = findThread();
382 
383  if (bladeRF2OutputThread == 0) { // no thread allocated
384  return;
385  }
386 
387  int nbOriginalChannels = bladeRF2OutputThread->getNbChannels();
388 
389  if (nbOriginalChannels == 1) // SO mode => just stop and delete the thread
390  {
391  qDebug("BladeRF2Output::stop: SO mode. Just stop and delete the thread");
392  bladeRF2OutputThread->stopWork();
393  delete bladeRF2OutputThread;
394  m_thread = 0;
395 
396  // remove old thread address from buddies (reset in all buddies)
397  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
398  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
399 
400  for (; it != sinkBuddies.end(); ++it) {
401  ((DeviceBladeRF2Shared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
402  }
403 
404  m_deviceShared.m_dev->closeTx(0); // close the unique channel
405  }
406  else if (requestedChannel == nbOriginalChannels - 1) // remove last MO channel => reduce by deleting and re-creating the thread
407  {
408  qDebug("BladeRF2Output::stop: MO mode. Reduce by deleting and re-creating the thread");
409  bladeRF2OutputThread->stopWork();
410  SampleSourceFifo **fifos = new SampleSourceFifo*[nbOriginalChannels-1];
411  unsigned int *log2Interps = new unsigned int[nbOriginalChannels-1];
412  bool stillActiveFIFO = false;
413 
414  for (int i = 0; i < nbOriginalChannels-1; i++) // save original FIFO references
415  {
416  fifos[i] = bladeRF2OutputThread->getFifo(i);
417  stillActiveFIFO = stillActiveFIFO || (bladeRF2OutputThread->getFifo(i) != 0);
418  log2Interps[i] = bladeRF2OutputThread->getLog2Interpolation(i);
419  }
420 
421  delete bladeRF2OutputThread;
422  m_thread = 0;
423 
424  if (stillActiveFIFO)
425  {
426  bladeRF2OutputThread = new BladeRF2OutputThread(m_deviceShared.m_dev->getDev(), nbOriginalChannels-1);
427  m_thread = bladeRF2OutputThread; // take ownership
428 
429  for (int i = 0; i < nbOriginalChannels-1; i++) // restore original FIFO references
430  {
431  bladeRF2OutputThread->setFifo(i, fifos[i]);
432  bladeRF2OutputThread->setLog2Interpolation(i, log2Interps[i]);
433  }
434  }
435  else
436  {
437  qDebug("BladeRF2Output::stop: do not re-create thread as there are no more FIFOs active");
438  }
439 
440  // remove old thread address from buddies (reset in all buddies)
441  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
442  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
443 
444  for (; it != sinkBuddies.end(); ++it) {
445  ((DeviceBladeRF2Shared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
446  }
447 
448  // close all channels
449  for (int i = nbOriginalChannels-1; i >= 0; i--) {
451  }
452 
453  if (stillActiveFIFO)
454  {
455  qDebug("BladeRF2Output::stop: enabling channel(s) and restarting the thread");
456 
457  for (unsigned int i = 0; i < bladeRF2OutputThread->getNbChannels(); i++) // open all channels
458  {
459  if (!m_deviceShared.m_dev->openTx(i)) {
460  qCritical("BladeRF2Output::start: channel %u cannot be enabled", i);
461  }
462  }
463 
464  bladeRF2OutputThread->startWork();
465  }
466 
467  // was used as temporary storage:
468  delete[] fifos;
469  delete[] log2Interps;
470  }
471  else // remove channel from existing thread
472  {
473  qDebug("BladeRF2Output::stop: MO mode. Not changing MO configuration. Just remove FIFO reference");
474  bladeRF2OutputThread->setFifo(requestedChannel, 0); // remove FIFO
475  }
476 
477  applySettings(m_settings, true); // re-apply forcibly to set sample rate with the new number of channels
478 
479  m_running = false;
480 }
481 
482 QByteArray BladeRF2Output::serialize() const
483 {
484  return m_settings.serialize();
485 }
486 
487 bool BladeRF2Output::deserialize(const QByteArray& data)
488 {
489  bool success = true;
490 
491  if (!m_settings.deserialize(data))
492  {
494  success = false;
495  }
496 
498  m_inputMessageQueue.push(message);
499 
500  if (m_guiMessageQueue)
501  {
503  m_guiMessageQueue->push(messageToGUI);
504  }
505 
506  return success;
507 }
508 
510 {
511  return m_deviceDescription;
512 }
513 
515 {
516  int rate = m_settings.m_devSampleRate;
517  return (rate / (1<<m_settings.m_log2Interp));
518 }
519 
521 {
523 }
524 
525 void BladeRF2Output::setCenterFrequency(qint64 centerFrequency)
526 {
528  settings.m_centerFrequency = centerFrequency;
529 
530  MsgConfigureBladeRF2* message = MsgConfigureBladeRF2::create(settings, false);
531  m_inputMessageQueue.push(message);
532 
533  if (m_guiMessageQueue)
534  {
535  MsgConfigureBladeRF2* messageToGUI = MsgConfigureBladeRF2::create(settings, false);
536  m_guiMessageQueue->push(messageToGUI);
537  }
538 }
539 
540 bool BladeRF2Output::setDeviceCenterFrequency(struct bladerf *dev, int requestedChannel, quint64 freq_hz, int loPpmTenths)
541 {
542  qint64 df = ((qint64)freq_hz * loPpmTenths) / 10000000LL;
543  freq_hz += df;
544 
545  int status = bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(requestedChannel), freq_hz);
546 
547  if (status < 0) {
548  qWarning("BladeRF2Output::setDeviceCenterFrequency: bladerf_set_frequency(%lld) failed: %s",
549  freq_hz, bladerf_strerror(status));
550  return false;
551  }
552  else
553  {
554  qDebug("BladeRF2Output::setDeviceCenterFrequency: bladerf_set_frequency(%lld)", freq_hz);
555  return true;
556  }
557 }
558 
560 {
561  if (m_deviceShared.m_dev) {
562  m_deviceShared.m_dev->getFrequencyRangeTx(min, max, step);
563  }
564 }
565 
566 void BladeRF2Output::getSampleRateRange(int& min, int& max, int& step)
567 {
568  if (m_deviceShared.m_dev) {
569  m_deviceShared.m_dev->getSampleRateRangeTx(min, max, step);
570  }
571 }
572 
573 void BladeRF2Output::getBandwidthRange(int& min, int& max, int& step)
574 {
575  if (m_deviceShared.m_dev) {
576  m_deviceShared.m_dev->getBandwidthRangeTx(min, max, step);
577  }
578 }
579 
580 void BladeRF2Output::getGlobalGainRange(int& min, int& max, int& step)
581 {
582  if (m_deviceShared.m_dev) {
583  m_deviceShared.m_dev->getGlobalGainRangeTx(min, max, step);
584  }
585 }
586 
588 {
589  if (MsgConfigureBladeRF2::match(message))
590  {
591  MsgConfigureBladeRF2& conf = (MsgConfigureBladeRF2&) message;
592  qDebug() << "BladeRF2Output::handleMessage: MsgConfigureBladeRF2";
593 
594  if (!applySettings(conf.getSettings(), conf.getForce()))
595  {
596  qDebug("BladeRF2Output::handleMessage: MsgConfigureBladeRF2 config error");
597  }
598 
599  return true;
600  }
602  {
604  struct bladerf *dev = m_deviceShared.m_dev->getDev();
606  int status;
607  unsigned int tmp_uint;
608  bool tmp_bool;
609 
610  // evaluate changes that may have been introduced by changes in a buddy
611 
612  if (dev) // The BladeRF device must have been open to do so
613  {
614  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
615 
616  if (report.getRxElseTx()) // Rx buddy change: check for sample rate change only
617  {
618  settings.m_devSampleRate = report.getDevSampleRate();
619 // status = bladerf_get_sample_rate(dev, BLADERF_CHANNEL_TX(requestedChannel), &tmp_uint);
620 //
621 // if (status < 0) {
622 // qCritical("BladeRF2Output::handleMessage: MsgReportBuddyChange: bladerf_get_sample_rate error: %s", bladerf_strerror(status));
623 // } else {
624 // settings.m_devSampleRate = tmp_uint;
625 // }
626  }
627  else // Tx buddy change: check for: frequency, gain mode and value, bias tee, sample rate, bandwidth
628  {
629  settings.m_devSampleRate = report.getDevSampleRate();
630  settings.m_LOppmTenths = report.getLOppmTenths();
631  settings.m_centerFrequency = report.getCenterFrequency();
632 
633  status = bladerf_get_bandwidth(dev, BLADERF_CHANNEL_TX(requestedChannel), &tmp_uint);
634 
635  if (status < 0) {
636  qCritical("BladeRF2Output::handleMessage: MsgReportBuddyChange: bladerf_get_bandwidth error: %s", bladerf_strerror(status));
637  } else {
638  settings.m_bandwidth = tmp_uint;
639  }
640 
641  status = bladerf_get_bias_tee(dev, BLADERF_CHANNEL_TX(requestedChannel), &tmp_bool);
642 
643  if (status < 0) {
644  qCritical("BladeRF2Output::handleMessage: MsgReportBuddyChange: bladerf_get_bias_tee error: %s", bladerf_strerror(status));
645  } else {
646  settings.m_biasTee = tmp_bool;
647  }
648  }
649 
650  // change DSP settings if buddy change introduced a change in center frequency or base rate
652  {
653  int sampleRate = settings.m_devSampleRate/(1<<settings.m_log2Interp);
654  DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, settings.m_centerFrequency);
656  }
657 
658  m_settings = settings; // acknowledge the new settings
659 
660  // propagate settings to GUI if any
661  if (getMessageQueueToGUI())
662  {
664  getMessageQueueToGUI()->push(reportToGUI);
665  }
666  }
667 
668  return true;
669  }
670  else if (MsgStartStop::match(message))
671  {
672  MsgStartStop& cmd = (MsgStartStop&) message;
673  qDebug() << "BladeRF2Input::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
674 
675  if (cmd.getStartStop())
676  {
678  {
680  }
681  }
682  else
683  {
685  }
686 
689  }
690 
691  return true;
692  }
693  else
694  {
695  return false;
696  }
697 }
698 
699 bool BladeRF2Output::applySettings(const BladeRF2OutputSettings& settings, bool force)
700 {
701  bool forwardChangeOwnDSP = false;
702  bool forwardChangeRxBuddies = false;
703  bool forwardChangeTxBuddies = false;
704  QList<QString> reverseAPIKeys;
705 
706  struct bladerf *dev = m_deviceShared.m_dev->getDev();
707  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
708  int nbChannels = getNbChannels();
709  qint64 deviceCenterFrequency = settings.m_centerFrequency;
710  deviceCenterFrequency -= settings.m_transverterMode ? settings.m_transverterDeltaFrequency : 0;
711  deviceCenterFrequency = deviceCenterFrequency < 0 ? 0 : deviceCenterFrequency;
712 
713  if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || (m_settings.m_log2Interp != settings.m_log2Interp) || force)
714  {
715  reverseAPIKeys.append("devSampleRate");
716  BladeRF2OutputThread *bladeRF2OutputThread = findThread();
717  SampleSourceFifo *fifo = 0;
718 
719  if (bladeRF2OutputThread)
720  {
721  fifo = bladeRF2OutputThread->getFifo(requestedChannel);
722  bladeRF2OutputThread->setFifo(requestedChannel, 0);
723  }
724 
725  int fifoSize;
726 
727  if (settings.m_log2Interp >= 5)
728  {
730  }
731  else
732  {
733  fifoSize = (std::max)(
736  }
737 
738  m_sampleSourceFifo.resize(fifoSize);
739 
740  if (fifo) {
741  bladeRF2OutputThread->setFifo(requestedChannel, &m_sampleSourceFifo);
742  }
743  }
744 
745  if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
746  {
747  forwardChangeOwnDSP = true;
748  forwardChangeRxBuddies = true;
749  forwardChangeTxBuddies = true;
750 
751  if (dev != 0)
752  {
753  unsigned int actualSamplerate;
754 
755  int status = bladerf_set_sample_rate(dev, BLADERF_CHANNEL_TX(requestedChannel),
756  settings.m_devSampleRate,
757  &actualSamplerate);
758 
759  if (status < 0)
760  {
761  qCritical("BladeRF2Output::applySettings: could not set sample rate: %d: %s",
762  settings.m_devSampleRate, bladerf_strerror(status));
763  }
764  else
765  {
766  qDebug() << "BladeRF2Output::applySettings: bladerf_set_sample_rate: actual sample rate is " << actualSamplerate;
767  }
768  }
769  }
770 
771  if ((m_settings.m_bandwidth != settings.m_bandwidth) || force)
772  {
773  reverseAPIKeys.append("bandwidth");
774  forwardChangeTxBuddies = true;
775 
776  if (dev != 0)
777  {
778  unsigned int actualBandwidth;
779  int status = bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(requestedChannel), settings.m_bandwidth, &actualBandwidth);
780 
781  if(status < 0)
782  {
783  qCritical("BladeRF2Output::applySettings: could not set bandwidth: %d: %s",
784  settings.m_bandwidth, bladerf_strerror(status));
785  }
786  else
787  {
788  qDebug() << "BladeRF2Output::applySettings: bladerf_set_bandwidth: actual bandwidth is " << actualBandwidth;
789  }
790  }
791  }
792 
793  if ((m_settings.m_log2Interp != settings.m_log2Interp) || force)
794  {
795  reverseAPIKeys.append("log2Interp");
796  forwardChangeOwnDSP = true;
797  BladeRF2OutputThread *outputThread = findThread();
798 
799  if (outputThread != 0)
800  {
801  outputThread->setLog2Interpolation(requestedChannel, settings.m_log2Interp);
802  qDebug() << "BladeRF2Output::applySettings: set interpolation to " << (1<<settings.m_log2Interp);
803  }
804  }
805 
806  if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) {
807  reverseAPIKeys.append("centerFrequency");
808  }
809  if ((m_settings.m_transverterMode != settings.m_transverterMode) || force) {
810  reverseAPIKeys.append("transverterMode");
811  }
813  reverseAPIKeys.append("transverterDeltaFrequency");
814  }
815  if ((m_settings.m_LOppmTenths != settings.m_LOppmTenths) || force) {
816  reverseAPIKeys.append("LOppmTenths");
817  }
818 
822  || (m_settings.m_LOppmTenths != settings.m_LOppmTenths)
823  || (m_settings.m_devSampleRate != settings.m_devSampleRate) || force)
824  {
825  forwardChangeOwnDSP = true;
826  forwardChangeTxBuddies = true;
827 
828  if (dev != 0)
829  {
830  if (setDeviceCenterFrequency(dev, requestedChannel, deviceCenterFrequency, settings.m_LOppmTenths))
831  {
832  if (getMessageQueueToGUI())
833  {
834  int min, max, step;
835  getGlobalGainRange(min, max, step);
836  MsgReportGainRange *msg = MsgReportGainRange::create(min, max, step);
837  getMessageQueueToGUI()->push(msg);
838  }
839  }
840  }
841  }
842 
843  if ((m_settings.m_biasTee != settings.m_biasTee) || force)
844  {
845  reverseAPIKeys.append("biasTee");
846  forwardChangeTxBuddies = true;
848  }
849 
850  if ((m_settings.m_globalGain != settings.m_globalGain) || force)
851  {
852  reverseAPIKeys.append("globalGain");
853  forwardChangeTxBuddies = true;
854 
855  if (dev)
856  {
857 // qDebug("BladeRF2Output::applySettings: channel: %d gain: %d", requestedChannel, settings.m_globalGain);
858  int status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(requestedChannel), settings.m_globalGain);
859 
860  if (status < 0) {
861  qWarning("BladeRF2Output::applySettings: bladerf_set_gain(%d) failed: %s",
862  settings.m_globalGain, bladerf_strerror(status));
863  } else {
864  qDebug("BladeRF2Output::applySettings: bladerf_set_gain(%d)", settings.m_globalGain);
865  }
866  }
867  }
868 
869  if (forwardChangeOwnDSP)
870  {
871  int sampleRate = settings.m_devSampleRate/(1<<settings.m_log2Interp);
872  DSPSignalNotification *notif = new DSPSignalNotification(sampleRate, settings.m_centerFrequency);
874  }
875 
876  if (forwardChangeRxBuddies)
877  {
878  // send to source buddies
879  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
880  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
881 
882  for (; itSource != sourceBuddies.end(); ++itSource)
883  {
885  settings.m_centerFrequency,
886  settings.m_LOppmTenths,
887  2,
888  settings.m_devSampleRate, // need to forward actual rate to the Rx side
889  false);
890  (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
891  }
892  }
893 
894  if (forwardChangeTxBuddies)
895  {
896  // send to sink buddies
897  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
898  std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
899 
900  for (; itSink != sinkBuddies.end(); ++itSink)
901  {
903  settings.m_centerFrequency,
904  settings.m_LOppmTenths,
905  2,
906  settings.m_devSampleRate,
907  false);
908  (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
909  }
910  }
911 
912  if (settings.m_useReverseAPI)
913  {
914  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
918  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
919  }
920 
921  m_settings = settings;
922 
923  qDebug() << "BladeRF2Output::applySettings: "
924  << " m_transverterMode: " << m_settings.m_transverterMode
925  << " m_transverterDeltaFrequency: " << m_settings.m_transverterDeltaFrequency
926  << " deviceCenterFrequency: " << deviceCenterFrequency
927  << " m_centerFrequency: " << m_settings.m_centerFrequency << " Hz"
928  << " m_LOppmTenths: " << m_settings.m_LOppmTenths
929  << " m_bandwidth: " << m_settings.m_bandwidth
930  << " m_log2Interp: " << m_settings.m_log2Interp
931  << " m_devSampleRate: " << m_settings.m_devSampleRate
932  << " nbChannels: " << nbChannels
933  << " m_globalGain: " << m_settings.m_globalGain
934  << " m_biasTee: " << m_settings.m_biasTee;
935 
936  return true;
937 }
938 
940 {
941  BladeRF2OutputThread *bladeRF2OutputThread = findThread();
942 
943  if (bladeRF2OutputThread) {
944  return bladeRF2OutputThread->getNbChannels();
945  } else {
946  return 0;
947  }
948 }
949 
952  QString& errorMessage)
953 {
954  (void) errorMessage;
956  response.getBladeRf2OutputSettings()->init();
958  return 200;
959 }
960 
962  bool force,
963  const QStringList& deviceSettingsKeys,
964  SWGSDRangel::SWGDeviceSettings& response, // query + response
965  QString& errorMessage)
966 {
967  (void) errorMessage;
969 
970  if (deviceSettingsKeys.contains("centerFrequency")) {
972  }
973  if (deviceSettingsKeys.contains("LOppmTenths")) {
974  settings.m_LOppmTenths = response.getBladeRf2OutputSettings()->getLOppmTenths();
975  }
976  if (deviceSettingsKeys.contains("devSampleRate")) {
978  }
979  if (deviceSettingsKeys.contains("bandwidth")) {
980  settings.m_bandwidth = response.getBladeRf2OutputSettings()->getBandwidth();
981  }
982  if (deviceSettingsKeys.contains("log2Interp")) {
983  settings.m_log2Interp = response.getBladeRf2OutputSettings()->getLog2Interp();
984  }
985  if (deviceSettingsKeys.contains("biasTee")) {
986  settings.m_biasTee = response.getBladeRf2OutputSettings()->getBiasTee() != 0;
987  }
988  if (deviceSettingsKeys.contains("globalGain")) {
989  settings.m_globalGain = response.getBladeRf2OutputSettings()->getGlobalGain();
990  }
991  if (deviceSettingsKeys.contains("transverterDeltaFrequency")) {
993  }
994  if (deviceSettingsKeys.contains("transverterMode")) {
995  settings.m_transverterMode = response.getBladeRf2OutputSettings()->getTransverterMode() != 0;
996  }
997  if (deviceSettingsKeys.contains("useReverseAPI")) {
998  settings.m_useReverseAPI = response.getBladeRf2OutputSettings()->getUseReverseApi() != 0;
999  }
1000  if (deviceSettingsKeys.contains("reverseAPIAddress")) {
1002  }
1003  if (deviceSettingsKeys.contains("reverseAPIPort")) {
1005  }
1006  if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
1008  }
1009 
1010  MsgConfigureBladeRF2 *msg = MsgConfigureBladeRF2::create(settings, force);
1012 
1013  if (m_guiMessageQueue) // forward to GUI if any
1014  {
1015  MsgConfigureBladeRF2 *msgToGUI = MsgConfigureBladeRF2::create(settings, force);
1016  m_guiMessageQueue->push(msgToGUI);
1017  }
1018 
1019  webapiFormatDeviceSettings(response, settings);
1020  return 200;
1021 }
1022 
1024 {
1025  (void) errorMessage;
1027  response.getBladeRf2OutputReport()->init();
1028  webapiFormatDeviceReport(response);
1029  return 200;
1030 }
1031 
1033 {
1037  response.getBladeRf2OutputSettings()->setBandwidth(settings.m_bandwidth);
1039  response.getBladeRf2OutputSettings()->setBiasTee(settings.m_biasTee ? 1 : 0);
1042  response.getBladeRf2OutputSettings()->setTransverterMode(settings.m_transverterMode ? 1 : 0);
1043 
1044  response.getBladeRf2OutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
1045 
1046  if (response.getBladeRf2OutputSettings()->getReverseApiAddress()) {
1048  } else {
1049  response.getBladeRf2OutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
1050  }
1051 
1054 }
1055 
1057 {
1059 
1060  if (device)
1061  {
1062  int min, max, step;
1063  uint64_t f_min, f_max;
1064 
1065  device->getBandwidthRangeTx(min, max, step);
1066 
1068  response.getBladeRf2OutputReport()->getBandwidthRange()->setMin(min);
1069  response.getBladeRf2OutputReport()->getBandwidthRange()->setMax(max);
1070  response.getBladeRf2OutputReport()->getBandwidthRange()->setStep(step);
1071 
1072  device->getFrequencyRangeTx(f_min, f_max, step);
1073 
1075  response.getBladeRf2OutputReport()->getFrequencyRange()->setMin(f_min);
1076  response.getBladeRf2OutputReport()->getFrequencyRange()->setMax(f_max);
1077  response.getBladeRf2OutputReport()->getFrequencyRange()->setStep(step);
1078 
1079  device->getGlobalGainRangeTx(min, max, step);
1080 
1084  response.getBladeRf2OutputReport()->getGlobalGainRange()->setStep(step);
1085 
1086  device->getSampleRateRangeTx(min, max, step);
1087 
1091  response.getBladeRf2OutputReport()->getSampleRateRange()->setStep(step);
1092  }
1093 }
1094 
1096  SWGSDRangel::SWGDeviceState& response,
1097  QString& errorMessage)
1098 {
1099  (void) errorMessage;
1101  return 200;
1102 }
1103 
1105  bool run,
1106  SWGSDRangel::SWGDeviceState& response,
1107  QString& errorMessage)
1108 {
1109  (void) errorMessage;
1111  MsgStartStop *message = MsgStartStop::create(run);
1112  m_inputMessageQueue.push(message);
1113 
1114  if (m_guiMessageQueue) // forward to GUI if any
1115  {
1116  MsgStartStop *msgToGUI = MsgStartStop::create(run);
1117  m_guiMessageQueue->push(msgToGUI);
1118  }
1119 
1120  return 200;
1121 }
1122 
1123 void BladeRF2Output::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const BladeRF2OutputSettings& settings, bool force)
1124 {
1126  swgDeviceSettings->setDirection(1); // single Tx
1127  swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
1128  swgDeviceSettings->setDeviceHwType(new QString("BladeRF2"));
1130  SWGSDRangel::SWGBladeRF2OutputSettings *swgBladeRF2OutputSettings = swgDeviceSettings->getBladeRf2OutputSettings();
1131 
1132  // transfer data that has been modified. When force is on transfer all data except reverse API data
1133 
1134  if (deviceSettingsKeys.contains("centerFrequency") || force) {
1135  swgBladeRF2OutputSettings->setCenterFrequency(settings.m_centerFrequency);
1136  }
1137  if (deviceSettingsKeys.contains("LOppmTenths") || force) {
1138  swgBladeRF2OutputSettings->setLOppmTenths(settings.m_LOppmTenths);
1139  }
1140  if (deviceSettingsKeys.contains("devSampleRate") || force) {
1141  swgBladeRF2OutputSettings->setDevSampleRate(settings.m_devSampleRate);
1142  }
1143  if (deviceSettingsKeys.contains("bandwidth") || force) {
1144  swgBladeRF2OutputSettings->setBandwidth(settings.m_bandwidth);
1145  }
1146  if (deviceSettingsKeys.contains("log2Interp") || force) {
1147  swgBladeRF2OutputSettings->setLog2Interp(settings.m_log2Interp);
1148  }
1149  if (deviceSettingsKeys.contains("biasTee") || force) {
1150  swgBladeRF2OutputSettings->setBiasTee(settings.m_biasTee ? 1 : 0);
1151  }
1152  if (deviceSettingsKeys.contains("globalGain") || force) {
1153  swgBladeRF2OutputSettings->setGlobalGain(settings.m_globalGain);
1154  }
1155  if (deviceSettingsKeys.contains("transverterDeltaFrequency") || force) {
1156  swgBladeRF2OutputSettings->setTransverterDeltaFrequency(settings.m_transverterDeltaFrequency);
1157  }
1158  if (deviceSettingsKeys.contains("transverterMode") || force) {
1159  swgBladeRF2OutputSettings->setTransverterMode(settings.m_transverterMode ? 1 : 0);
1160  }
1161 
1162  QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
1163  .arg(settings.m_reverseAPIAddress)
1164  .arg(settings.m_reverseAPIPort)
1165  .arg(settings.m_reverseAPIDeviceIndex);
1166  m_networkRequest.setUrl(QUrl(deviceSettingsURL));
1167  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1168 
1169  QBuffer *buffer=new QBuffer();
1170  buffer->open((QBuffer::ReadWrite));
1171  buffer->write(swgDeviceSettings->asJson().toUtf8());
1172  buffer->seek(0);
1173 
1174  // Always use PATCH to avoid passing reverse API settings
1175  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
1176 
1177  delete swgDeviceSettings;
1178 }
1179 
1181 {
1183  swgDeviceSettings->setDirection(1); // single Tx
1184  swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
1185  swgDeviceSettings->setDeviceHwType(new QString("BladeRF2"));
1186 
1187  QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
1191  m_networkRequest.setUrl(QUrl(deviceSettingsURL));
1192  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1193 
1194  QBuffer *buffer=new QBuffer();
1195  buffer->open((QBuffer::ReadWrite));
1196  buffer->write(swgDeviceSettings->asJson().toUtf8());
1197  buffer->seek(0);
1198 
1199  if (start) {
1200  m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
1201  } else {
1202  m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
1203  }
1204 
1205  delete swgDeviceSettings;
1206 }
1207 
1208 void BladeRF2Output::networkManagerFinished(QNetworkReply *reply)
1209 {
1210  QNetworkReply::NetworkError replyError = reply->error();
1211 
1212  if (replyError)
1213  {
1214  qWarning() << "BladeRF2Output::networkManagerFinished:"
1215  << " error(" << (int) replyError
1216  << "): " << replyError
1217  << ": " << reply->errorString();
1218  return;
1219  }
1220 
1221  QString answer = reply->readAll();
1222  answer.chop(1); // remove last \n
1223  qDebug("BladeRF2Output::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
1224 }
bool startDeviceEngine()
Start the device engine corresponding to the stream type.
Definition: deviceapi.cpp:253
QNetworkRequest m_networkRequest
const BladeRF2OutputSettings & getSettings() const
virtual int webapiSettingsPutPatch(bool force, const QStringList &deviceSettingsKeys, SWGSDRangel::SWGDeviceSettings &response, QString &errorMessage)
void push(Message *message, bool emitSignal=true)
Push message onto queue.
void stopDeviceEngine()
Stop the device engine corresponding to the stream type.
Definition: deviceapi.cpp:266
SWGBladeRF2OutputSettings * getBladeRf2OutputSettings()
void networkManagerFinished(QNetworkReply *reply)
bool openTx(int channel)
void getGlobalGainRangeTx(int &min, int &max, int &step)
const std::vector< DeviceAPI * > & getSinkBuddies() const
Definition: deviceapi.h:166
static const float m_sampleFifoLengthInSeconds
virtual QString asJson() override
static const int m_sampleFifoMinSize
MessageQueue * getDeviceEngineInputMessageQueue()
Device engine message queue.
Definition: deviceapi.cpp:316
virtual const QString & getDeviceDescription() const
SampleSourceFifo * getFifo(unsigned int channel)
BladeRF2OutputThread * m_thread
uint32_t getDeviceItemIndex() const
Definition: deviceapi.h:129
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport &response)
void setStep(qint32 step)
Definition: SWGRange.cpp:131
virtual void destroy()
bool initDeviceEngine()
Init the device engine corresponding to the stream type.
Definition: deviceapi.cpp:240
unsigned int getLog2Interpolation(unsigned int channel) const
virtual void init()
initializations to be done when all collaborating objects are created and possibly connected ...
void setMax(qint32 max)
Definition: SWGRange.cpp:121
void setOriginatorIndex(qint32 originator_index)
void webapiReverseSendStartStop(bool start)
virtual int getSampleRate() const
Sample rate exposed by the sink.
void getBandwidthRange(int &min, int &max, int &step)
bool setDeviceCenterFrequency(struct bladerf *dev, int requestedChannel, quint64 freq_hz, int loPpmTenths)
void setBiasTeeTx(bool enable)
MessageQueue m_inputMessageQueue
Input queue to the sink.
virtual int webapiReportGet(SWGSDRangel::SWGDeviceReport &response, QString &errorMessage)
unsigned int getNbChannels() const
int getDeviceSetIndex() const
Definition: deviceapi.h:131
static const int m_sampleFifoMinSize32
virtual bool start()
void * getBuddySharedPtr() const
Definition: deviceapi.h:161
void getBandwidthRangeTx(int &min, int &max, int &step)
void setFrequencyRange(SWGFrequencyRange *frequency_range)
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
virtual bool deserialize(const QByteArray &data)
void setReverseApiAddress(QString *reverse_api_address)
void getSampleRateRangeTx(int &min, int &max, int &step)
DeviceAPI * m_deviceAPI
virtual int webapiSettingsGet(SWGSDRangel::SWGDeviceSettings &response, QString &errorMessage)
DeviceBladeRF2Shared m_deviceShared
void setBuddySharedPtr(void *ptr)
Definition: deviceapi.h:162
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
void setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
int32_t i
Definition: decimators.h:244
static bool match(const Message *message)
Definition: message.cpp:45
virtual int webapiRun(bool run, SWGSDRangel::SWGDeviceState &response, QString &errorMessage)
void setGlobalGainRange(SWGRange *global_gain_range)
void setLog2Interpolation(unsigned int channel, unsigned int log2_interp)
QString m_deviceDescription
virtual int webapiRunGet(SWGSDRangel::SWGDeviceState &response, QString &errorMessage)
static MsgReportBuddyChange * create(uint64_t centerFrequency, int LOppmTenths, int fcPos, int devSampleRate, bool rxElseTx)
void getFrequencyRangeTx(uint64_t &min, uint64_t &max, int &step)
virtual void stop()
static MsgStartStop * create(bool startStop)
void setMin(qint32 min)
Definition: SWGRange.cpp:111
void getDeviceEngineStateStr(QString &state)
Definition: deviceapi.cpp:389
void resize(uint32_t size)
const QString & getSamplingDeviceSerial() const
Definition: deviceapi.h:121
virtual QByteArray serialize() const
SWGBladeRF2OutputReport * getBladeRf2OutputReport()
bladerf * getDev()
static MsgReportGainRange * create(int min, int max, int step)
BladeRF2OutputSettings m_settings
int m_channel
allocated channel (-1 if none)
bool open(const char *serial)
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings &response, const BladeRF2OutputSettings &settings)
virtual bool handleMessage(const Message &message)
void setBladeRf2OutputReport(SWGBladeRF2OutputReport *blade_rf2_output_report)
void setThread(BladeRF2OutputThread *thread)
void setSampleRateRange(SWGRange *sample_rate_range)
void getGlobalGainRange(int &min, int &max, int &step)
SampleSourceFifo m_sampleSourceFifo
virtual void setCenterFrequency(qint64 centerFrequency)
const std::vector< DeviceAPI * > & getSourceBuddies() const
Definition: deviceapi.h:165
void getFrequencyRange(uint64_t &min, uint64_t &max, int &step)
BladeRF2OutputThread * getThread()
void getSampleRateRange(int &min, int &max, int &step)
void setBandwidthRange(SWGRange *bandwidth_range)
virtual ~BladeRF2Output()
virtual quint64 getCenterFrequency() const
Center frequency exposed by the sink.
MessageQueue * getMessageQueueToGUI()
QNetworkAccessManager * m_networkManager
void setDirection(qint32 direction)
void setTransverterDeltaFrequency(qint64 transverter_delta_frequency)
void webapiReverseSendSettings(QList< QString > &deviceSettingsKeys, const BladeRF2OutputSettings &settings, bool force)
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
static MsgConfigureBladeRF2 * create(const BladeRF2OutputSettings &settings, bool force)
void closeTx(int channel)
T max(const T &x, const T &y)
Definition: framework.h:446
BladeRF2OutputThread * findThread()
bool deserialize(const QByteArray &data)
void setDeviceHwType(QString *device_hw_type)
bool applySettings(const BladeRF2OutputSettings &settings, bool force)
void setBladeRf2OutputSettings(SWGBladeRF2OutputSettings *blade_rf2_output_settings)
T min(const T &x, const T &y)
Definition: framework.h:440
unsigned __int64 uint64_t
Definition: rtptypes_win.h:48