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.
xtrxoutput.cpp
Go to the documentation of this file.
1 // Copyright (C) 2017, 2018 Edouard Griffiths, F4EXB //
3 // Copyright (C) 2017 Sergey Kostanbaev, Fairwaves Inc. //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 #include <cstddef>
19 #include <string.h>
20 #include "xtrx_api.h"
21 
22 #include <QMutexLocker>
23 #include <QDebug>
24 #include <QNetworkReply>
25 #include <QBuffer>
26 
27 #include "SWGDeviceSettings.h"
28 #include "SWGXtrxOutputSettings.h"
29 #include "SWGDeviceState.h"
30 #include "SWGDeviceReport.h"
31 #include "SWGXtrxOutputReport.h"
32 
33 #include "device/deviceapi.h"
34 #include "dsp/dspcommands.h"
35 #include "xtrxoutput.h"
36 #include "xtrxoutputthread.h"
37 #include "xtrx/devicextrxparam.h"
38 #include "xtrx/devicextrxshared.h"
39 #include "xtrx/devicextrx.h"
40 
47 
49  m_deviceAPI(deviceAPI),
50  m_settings(),
51  m_XTRXOutputThread(0),
52  m_deviceDescription("XTRXOutput"),
53  m_running(false)
54 {
55  openDevice();
56  m_deviceAPI->setNbSinkStreams(1);
57  m_networkManager = new QNetworkAccessManager();
58  connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
59 }
60 
62 {
63  disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
64  delete m_networkManager;
65 
66  if (m_running) {
67  stop();
68  }
69 
70  closeDevice();
71 }
72 
74 {
75  delete this;
76 }
77 
79 {
81 
82  // look for Tx buddies and get reference to the device object
83  if (m_deviceAPI->getSinkBuddies().size() > 0) // then sink
84  {
85  qDebug("XTRXOutput::openDevice: look in Tx buddies");
86 
87  DeviceAPI *sinkBuddy = m_deviceAPI->getSinkBuddies()[0];
88  DeviceXTRXShared *deviceXTRXShared = (DeviceXTRXShared*) sinkBuddy->getBuddySharedPtr();
89 
90  if (deviceXTRXShared == 0)
91  {
92  qCritical("XTRXOutput::openDevice: the sink buddy shared pointer is null");
93  return false;
94  }
95 
96  DeviceXTRX *device = deviceXTRXShared->m_dev;
97 
98  if (device == 0)
99  {
100  qCritical("XTRXOutput::openDevice: cannot get device pointer from Tx buddy");
101  return false;
102  }
103 
104  m_deviceShared.m_dev = device;
105  }
106  // look for Rx buddies and get reference to the device object
107  else if (m_deviceAPI->getSourceBuddies().size() > 0) // look source sibling first
108  {
109  qDebug("XTRXOutput::openDevice: look in Rx buddies");
110 
111  DeviceAPI *sourceBuddy = m_deviceAPI->getSourceBuddies()[0];
112  DeviceXTRXShared *deviceXTRXShared = (DeviceXTRXShared*) sourceBuddy->getBuddySharedPtr();
113 
114  if (deviceXTRXShared == 0)
115  {
116  qCritical("XTRXOutput::openDevice: the source buddy shared pointer is null");
117  return false;
118  }
119 
120  DeviceXTRX *device = deviceXTRXShared->m_dev;
121 
122  if (device == 0)
123  {
124  qCritical("XTRXOutput::openDevice: cannot get device pointer from Rx buddy");
125  return false;
126  }
127 
128  m_deviceShared.m_dev = device;
129  }
130  // There are no buddies then create the first BladeRF2 device
131  else
132  {
133  qDebug("XTRXOutput::openDevice: open device here");
134 
136  char serial[256];
137  strcpy(serial, qPrintable(m_deviceAPI->getSamplingDeviceSerial()));
138 
139  if (!m_deviceShared.m_dev->open(serial))
140  {
141  qCritical("XTRXOutput::openDevice: cannot open BladeRF2 device");
142  return false;
143  }
144  }
145 
146  m_deviceShared.m_channel = m_deviceAPI->getDeviceItemIndex(); // publicly allocate channel
147  m_deviceShared.m_sink = this;
148  m_deviceAPI->setBuddySharedPtr(&m_deviceShared); // propagate common parameters to API
149  return true;
150 }
151 
153 {
154  if (m_deviceShared.m_dev == 0) { // was never open
155  return;
156  }
157 
158  if (m_running) {
159  stop();
160  }
161 
162  if (m_XTRXOutputThread) { // stills own the thread => transfer to a buddy
164  }
165 
166  m_deviceShared.m_channel = -1; // publicly release channel
168 
169  // No buddies so effectively close the device
170 
171  if ((m_deviceAPI->getSinkBuddies().size() == 0) && (m_deviceAPI->getSourceBuddies().size() == 0))
172  {
174  delete m_deviceShared.m_dev;
175  m_deviceShared.m_dev = 0;
176  }
177 }
178 
180 {
181  applySettings(m_settings, true, false);
182 }
183 
185 {
186  if (m_XTRXOutputThread == 0) // this does not own the thread
187  {
188  XTRXOutputThread *xtrxOutputThread = 0;
189 
190  // find a buddy that has allocated the thread
191  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
192  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
193 
194  for (; it != sinkBuddies.end(); ++it)
195  {
196  XTRXOutput *buddySink = ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_sink;
197 
198  if (buddySink)
199  {
200  xtrxOutputThread = buddySink->getThread();
201 
202  if (xtrxOutputThread) {
203  break;
204  }
205  }
206  }
207 
208  return xtrxOutputThread;
209  }
210  else
211  {
212  return m_XTRXOutputThread; // own thread
213  }
214 }
215 
217 {
218  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
219  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
220 
221  for (; it != sinkBuddies.end(); ++it)
222  {
223  XTRXOutput *buddySink = ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_sink;
224 
225  if (buddySink)
226  {
227  buddySink->setThread(m_XTRXOutputThread);
228  m_XTRXOutputThread = 0; // zero for others
229  }
230  }
231 }
232 
234 {
235  // There is a single thread per physical device (Tx side). This thread is unique and referenced by a unique
236  // buddy in the group of sink buddies associated with this physical device.
237  //
238  // This start method is responsible for managing the thread when the streaming of a Tx channel is started
239  //
240  // It checks the following conditions
241  // - the thread is allocated or not (by itself or one of its buddies). If it is it grabs the thread pointer.
242  // - the requested channel is another channel (one is already streaming).
243  //
244  // The XTRX support library lets you work in two possible modes:
245  // - Single Output (SO) with only one channel streaming. This can be channel 0 or 1 (channels can be swapped - unlike with BladeRF2).
246  // - Multiple Output (MO) with two channels streaming using interleaved samples. It MUST be in this configuration if both channels are
247  // streaming.
248  //
249  // It manages the transition form SO where only one channel is running to the Multiple Input (MO) if the both channels are requested.
250  // 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 there is no thread allocated it means we are in SO mode and it creates a new one with the requested channel.
254  // It marks the thread as needing start.
255  //
256  // 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
257  // allocated in the thread and starts the thread.
258 
260  {
261  qDebug("XTRXOutput::start: no device object");
262  return false;
263  }
264 
265  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
266  XTRXOutputThread *xtrxOutputThread = findThread();
267  bool needsStart = false;
268 
269  if (xtrxOutputThread) // if thread is already allocated
270  {
271  qDebug("XTRXOutput::start: thread is already allocated");
272 
273  unsigned int nbOriginalChannels = xtrxOutputThread->getNbChannels();
274 
275  // if one channel is already allocated it must be the other one so we'll end up with both channels
276  // thus we expand by deleting and re-creating the thread
277  if (nbOriginalChannels != 0)
278  {
279  qDebug("XTRXOutput::start: expand channels. Re-allocate thread and take ownership");
280 
281  SampleSourceFifo **fifos = new SampleSourceFifo*[2];
282  unsigned int *log2Interps = new unsigned int[2];
283 
284  for (int i = 0; i < 2; i++) // save original FIFO references and data
285  {
286  fifos[i] = xtrxOutputThread->getFifo(i);
287  log2Interps[i] = xtrxOutputThread->getLog2Interpolation(i);
288  }
289 
290  xtrxOutputThread->stopWork();
291  delete xtrxOutputThread;
292  xtrxOutputThread = new XTRXOutputThread(m_deviceShared.m_dev->getDevice(), 2); // MO mode (2 channels)
293  m_XTRXOutputThread = xtrxOutputThread; // take ownership
294  m_deviceShared.m_thread = xtrxOutputThread;
295 
296  for (int i = 0; i < 2; i++) // restore original FIFO references
297  {
298  xtrxOutputThread->setFifo(i, fifos[i]);
299  xtrxOutputThread->setLog2Interpolation(i, log2Interps[i]);
300  }
301 
302  // remove old thread address from buddies (reset in all buddies). The address being held only in the owning source.
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  {
308  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
309  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_thread = 0;
310  }
311 
312  // was used as temporary storage:
313  delete[] fifos;
314  delete[] log2Interps;
315 
316  needsStart = true;
317  }
318  else
319  {
320  qDebug("XTRXOutput::start: keep buddy thread");
321  }
322  }
323  else // first allocation
324  {
325  qDebug("XTRXOutput::start: allocate thread and take ownership");
326  xtrxOutputThread = new XTRXOutputThread(m_deviceShared.m_dev->getDevice(), 1, requestedChannel);
327  m_XTRXOutputThread = xtrxOutputThread; // take ownership
328  m_deviceShared.m_thread = xtrxOutputThread;
329  needsStart = true;
330  }
331 
332  xtrxOutputThread->setFifo(requestedChannel, &m_sampleSourceFifo);
333  xtrxOutputThread->setLog2Interpolation(requestedChannel, m_settings.m_log2SoftInterp);
334 
335  applySettings(m_settings, true);
336 
337  if (needsStart)
338  {
339  qDebug("XTRXOutput::start: (re)start thread");
340  xtrxOutputThread->startWork();
341  }
342 
343  qDebug("XTRXOutput::start: started");
344  m_running = true;
345 
346  return true;
347 }
348 
350 {
351  // This stop method is responsible for managing the thread when the streaming of a Rx channel is stopped
352  //
353  // If the thread is currently managing only one channel (SO mode). The thread can be just stopped and deleted.
354  // Then the channel is closed.
355  //
356  // If the thread is currently managing both channels (MO mode) then we are removing one channel. Thus we must
357  // transition from MO to SO. This transition is handled by stopping the thread, deleting it and creating a new one
358  // managing a single channel.
359 
360  if (!m_running) {
361  return;
362  }
363 
364  int removedChannel = m_deviceAPI->getDeviceItemIndex(); // channel to remove
365  int requestedChannel = removedChannel ^ 1; // channel to keep (opposite channel)
366  XTRXOutputThread *xtrxOutputThread = findThread();
367 
368  if (xtrxOutputThread == 0) { // no thread allocated
369  return;
370  }
371 
372  int nbOriginalChannels = xtrxOutputThread->getNbChannels();
373 
374  if (nbOriginalChannels == 1) // SO mode => just stop and delete the thread
375  {
376  qDebug("XTRXOutput::stop: SO mode. Just stop and delete the thread");
377  xtrxOutputThread->stopWork();
378  delete xtrxOutputThread;
379  m_XTRXOutputThread = 0;
381 
382  // remove old thread address from buddies (reset in all buddies)
383  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
384  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
385 
386  for (; it != sinkBuddies.end(); ++it)
387  {
388  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
389  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_thread = 0;
390  }
391  }
392  else if (nbOriginalChannels == 2) // Reduce from MO to SO by deleting and re-creating the thread
393  {
394  qDebug("XTRXOutput::stop: MO mode. Reduce by deleting and re-creating the thread");
395  xtrxOutputThread->stopWork();
396  delete xtrxOutputThread;
397  xtrxOutputThread = new XTRXOutputThread(m_deviceShared.m_dev->getDevice(), 1, requestedChannel);
398  m_XTRXOutputThread = xtrxOutputThread; // take ownership
399  m_deviceShared.m_thread = xtrxOutputThread;
400 
401  xtrxOutputThread->setFifo(requestedChannel, &m_sampleSourceFifo);
402  xtrxOutputThread->setLog2Interpolation(requestedChannel, m_settings.m_log2SoftInterp);
403 
404  // remove old thread address from buddies (reset in all buddies). The address being held only in the owning source.
405  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
406  std::vector<DeviceAPI*>::const_iterator it = sinkBuddies.begin();
407 
408  for (; it != sinkBuddies.end(); ++it)
409  {
410  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_sink->setThread(0);
411  ((DeviceXTRXShared*) (*it)->getBuddySharedPtr())->m_thread = 0;
412  }
413 
414  applySettings(m_settings, true);
415  xtrxOutputThread->startWork();
416  }
417 
418  m_running = false;
419 }
420 
422 {
423  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
424  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
425 
426  qDebug("XTRXOutput::suspendRxThread (%lu)", sourceBuddies.size());
427 
428  for (; itSource != sourceBuddies.end(); ++itSource)
429  {
430  DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSource)->getBuddySharedPtr();
431 
432  if (buddySharedPtr->m_thread && buddySharedPtr->m_thread->isRunning())
433  {
434  buddySharedPtr->m_thread->stopWork();
435  buddySharedPtr->m_threadWasRunning = true;
436  }
437  else
438  {
439  buddySharedPtr->m_threadWasRunning = false;
440  }
441  }
442 }
443 
445 {
446  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
447  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
448 
449  qDebug("XTRXOutput::resumeRxThread (%lu)", sourceBuddies.size());
450 
451  for (; itSource != sourceBuddies.end(); ++itSource)
452  {
453  DeviceXTRXShared *buddySharedPtr = (DeviceXTRXShared *) (*itSource)->getBuddySharedPtr();
454 
455  if (buddySharedPtr->m_threadWasRunning) {
456  buddySharedPtr->m_thread->startWork();
457  }
458  }
459 }
460 
461 QByteArray XTRXOutput::serialize() const
462 {
463  return m_settings.serialize();
464 }
465 
466 bool XTRXOutput::deserialize(const QByteArray& data)
467 {
468  bool success = true;
469 
470  if (!m_settings.deserialize(data))
471  {
473  success = false;
474  }
475 
477  m_inputMessageQueue.push(message);
478 
479  if (m_guiMessageQueue)
480  {
482  m_guiMessageQueue->push(messageToGUI);
483  }
484 
485  return success;
486 }
487 
488 const QString& XTRXOutput::getDeviceDescription() const
489 {
490  return m_deviceDescription;
491 }
492 
494 {
495  double rate = m_settings.m_devSampleRate;
496 
497  if (m_deviceShared.m_dev) {
499  }
500 
501  return (int)((rate / (1<<m_settings.m_log2SoftInterp)));
502 }
503 
505 {
506  uint32_t devSampleRate = m_settings.m_devSampleRate;
507 
508  if (m_deviceShared.m_dev) {
509  devSampleRate = m_deviceShared.m_dev->getActualOutputRate();
510  }
511 
512  return devSampleRate;
513 }
514 
516 {
517  uint32_t log2HardInterp = m_settings.m_log2HardInterp;
518 
520  log2HardInterp = log2(m_deviceShared.m_dev->getClockGen() / m_deviceShared.m_dev->getActualOutputRate() / 4);
521  }
522 
523  return log2HardInterp;
524 }
525 
527 {
528  if (m_deviceShared.m_dev) {
529  return m_deviceShared.m_dev->getClockGen();
530  } else {
531  return 0.0;
532  }
533 }
534 
536 {
538 }
539 
540 void XTRXOutput::setCenterFrequency(qint64 centerFrequency)
541 {
542  XTRXOutputSettings settings = m_settings;
543  settings.m_centerFrequency = centerFrequency - (m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0);
544 
545  MsgConfigureXTRX* message = MsgConfigureXTRX::create(settings, false);
546  m_inputMessageQueue.push(message);
547 
548  if (m_guiMessageQueue)
549  {
550  MsgConfigureXTRX* messageToGUI = MsgConfigureXTRX::create(settings, false);
551  m_guiMessageQueue->push(messageToGUI);
552  }
553 }
554 
556 {
557  return m_deviceShared.m_channel;
558 }
559 
560 void XTRXOutput::getLORange(float& minF, float& maxF, float& stepF) const
561 {
562  minF = 29e6;
563  maxF = 3840e6;
564  stepF = 10;
565  qDebug("XTRXOutput::getLORange: min: %f max: %f step: %f",
566  minF, maxF, stepF);
567 }
568 
569 void XTRXOutput::getSRRange(float& minF, float& maxF, float& stepF) const
570 {
571  minF = 100e3;
572  maxF = 120e6;
573  stepF = 10;
574  qDebug("XTRXOutput::getSRRange: min: %f max: %f step: %f",
575  minF, maxF, stepF);
576 }
577 
578 void XTRXOutput::getLPRange(float& minF, float& maxF, float& stepF) const
579 {
580  minF = 500e3;
581  maxF = 130e6;
582  stepF = 10;
583  qDebug("XTRXOutput::getLPRange: min: %f max: %f step: %f",
584  minF, maxF, stepF);
585 }
586 
587 bool XTRXOutput::handleMessage(const Message& message)
588 {
589  if (MsgConfigureXTRX::match(message))
590  {
591  MsgConfigureXTRX& conf = (MsgConfigureXTRX&) message;
592  qDebug() << "XTRXOutput::handleMessage: MsgConfigureXTRX";
593 
594  if (!applySettings(conf.getSettings(), conf.getForce()))
595  {
596  qDebug("XTRXOutput::handleMessage config error");
597  }
598 
599  return true;
600  }
602  {
604 
605  if (!report.getRxElseTx())
606  {
610  }
611  else
612  {
615 
616  qDebug() << "XTRXOutput::handleMessage: MsgReportBuddyChange:"
617  << " host_Hz: " << m_deviceShared.m_dev->getActualOutputRate()
618  << " dac_Hz: " << m_deviceShared.m_dev->getClockGen() / 4
619  << " m_log2HardInterp: " << m_settings.m_log2HardInterp;
620  }
621 
622  if (m_settings.m_ncoEnable) // need to reset NCO after sample rate change
623  {
624  applySettings(m_settings, false, true);
625  }
626 
627  int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
628 
631  m_settings.m_centerFrequency + ncoShift);
633 
634  if (getMessageQueueToGUI())
635  {
638  getMessageQueueToGUI()->push(reportToGUI);
639  }
640 
641  return true;
642  }
644  {
646 
647  m_settings.m_extClock = report.getExtClock();
649 
650  if (getMessageQueueToGUI())
651  {
654  getMessageQueueToGUI()->push(reportToGUI);
655  }
656 
657  return true;
658  }
659  else if (MsgGetStreamInfo::match(message))
660  {
662  {
663  uint64_t fifolevel = 0;
664 
666  xtrx_val_get(m_deviceShared.m_dev->getDevice(), XTRX_TX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevel);
667  }
668 
670  true,
671  true,
672  fifolevel,
673  65536);
674 
677  }
678  }
679 
680  return true;
681  }
682  else if (MsgGetDeviceInfo::match(message))
683  {
684  double board_temp = 0.0;
685  bool gps_locked = false;
686 
687  if (!m_deviceShared.m_dev->getDevice() || ((board_temp = m_deviceShared.get_board_temperature() / 256.0) == 0.0)) {
688  qDebug("XTRXOutput::handleMessage: MsgGetDeviceInfo: cannot get board temperature");
689  }
690 
691  if (!m_deviceShared.m_dev->getDevice()) {
692  qDebug("XTRXOutput::handleMessage: MsgGetDeviceInfo: cannot get GPS lock status");
693  } else {
694  gps_locked = m_deviceShared.get_gps_status();
695  }
696 
697  // send to oneself
699  {
702  }
703 
704  // send to sink buddies
705  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
706  std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
707 
708  for (; itSink != sinkBuddies.end(); ++itSink)
709  {
710  if ((*itSink)->getSamplingDeviceGUIMessageQueue())
711  {
713  (*itSink)->getSamplingDeviceGUIMessageQueue()->push(report);
714  }
715  }
716 
717  // send to source buddies
718  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
719  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
720 
721  for (; itSource != sourceBuddies.end(); ++itSource)
722  {
723  if ((*itSource)->getSamplingDeviceGUIMessageQueue())
724  {
726  (*itSource)->getSamplingDeviceGUIMessageQueue()->push(report);
727  }
728  }
729 
730  return true;
731  }
732  else if (MsgStartStop::match(message))
733  {
734  MsgStartStop& cmd = (MsgStartStop&) message;
735  qDebug() << "XTRXOutput::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
736 
737  if (cmd.getStartStop())
738  {
740  {
742  }
743  }
744  else
745  {
747  }
748 
749  return true;
750  }
751  else
752  {
753  return false;
754  }
755 }
756 
757 bool XTRXOutput::applySettings(const XTRXOutputSettings& settings, bool force, bool forceNCOFrequency)
758 {
759  int requestedChannel = m_deviceAPI->getDeviceItemIndex();
760  XTRXOutputThread *outputThread = findThread();
761  QList<QString> reverseAPIKeys;
762 
763  bool forwardChangeOwnDSP = false;
764  bool forwardChangeTxDSP = false;
765  bool forwardChangeAllDSP = false;
766  bool forwardClockSource = false;
767  bool txThreadWasRunning = false;
768  bool doLPCalibration = false;
769  bool doChangeSampleRate = false;
770  bool doChangeFreq = false;
771 
772  // apply settings
773 
774  qDebug() << "XTRXOutput::applySettings: m_centerFrequency: " << m_settings.m_centerFrequency
775  << " m_devSampleRate: " << m_settings.m_devSampleRate
776  << " m_log2SoftInterp: " << m_settings.m_log2SoftInterp
777  << " m_gain: " << m_settings.m_gain
778  << " m_lpfBW: " << m_settings.m_lpfBW
779  << " m_pwrmode: " << m_settings.m_pwrmode
780  << " m_ncoEnable: " << m_settings.m_ncoEnable
781  << " m_ncoFrequency: " << m_settings.m_ncoFrequency
782  << " m_antennaPath: " << m_settings.m_antennaPath
783  << " m_extClock: " << m_settings.m_extClock
784  << " m_extClockFreq: " << m_settings.m_extClockFreq
785  << " force: " << force;
786 
787  if ((m_settings.m_pwrmode != settings.m_pwrmode))
788  {
789  reverseAPIKeys.append("pwrmode");
790 
791  if (m_deviceShared.m_dev->getDevice() != 0)
792  {
793  if (xtrx_val_set(m_deviceShared.m_dev->getDevice(),
794  XTRX_TRX,
795  m_deviceShared.m_channel == 0 ? XTRX_CH_A : XTRX_CH_B,
796  XTRX_LMS7_PWR_MODE,
797  settings.m_pwrmode) < 0) {
798  qCritical("XTRXOutput::applySettings: could not set power mode %d", settings.m_pwrmode);
799  }
800  }
801  }
802 
803  if ((m_settings.m_extClock != settings.m_extClock) || force) {
804  reverseAPIKeys.append("extClock");
805  }
806  if ((m_settings.m_extClockFreq != settings.m_extClockFreq) || force) {
807  reverseAPIKeys.append("extClockFreq");
808  }
809 
810  if ((m_settings.m_extClock != settings.m_extClock)
811  || (settings.m_extClock && (m_settings.m_extClockFreq != settings.m_extClockFreq)) || force)
812  {
813  if (m_deviceShared.m_dev->getDevice() != 0)
814  {
815  xtrx_set_ref_clk(m_deviceShared.m_dev->getDevice(),
816  (settings.m_extClock) ? settings.m_extClockFreq : 0,
817  (settings.m_extClock) ? XTRX_CLKSRC_EXT : XTRX_CLKSRC_INT);
818  {
819  forwardClockSource = true;
820  doChangeSampleRate = true;
821  doChangeFreq = true;
822  qDebug("XTRXOutput::applySettings: clock set to %s (Ext: %d Hz)",
823  settings.m_extClock ? "external" : "internal",
824  settings.m_extClockFreq);
825  }
826  }
827  }
828 
829  if ((m_settings.m_devSampleRate != settings.m_devSampleRate) || force) {
830  reverseAPIKeys.append("devSampleRate");
831  }
832  if ((m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force) {
833  reverseAPIKeys.append("log2HardInterp");
834  }
835 
836  if ((m_settings.m_devSampleRate != settings.m_devSampleRate)
837  || (m_settings.m_log2HardInterp != settings.m_log2HardInterp) || force)
838  {
839  forwardChangeAllDSP = true; //m_settings.m_devSampleRate != settings.m_devSampleRate;
840 
841  if (m_deviceShared.m_dev->getDevice() != 0) {
842  doChangeSampleRate = true;
843  }
844  }
845 
846  if ((m_settings.m_gain != settings.m_gain) || force)
847  {
848  reverseAPIKeys.append("gain");
849 
850  if (m_deviceShared.m_dev->getDevice() != 0)
851  {
852  if (xtrx_set_gain(m_deviceShared.m_dev->getDevice(),
853  m_deviceShared.m_channel == 0 ? XTRX_CH_A : XTRX_CH_B,
854  XTRX_TX_PAD_GAIN,
855  settings.m_gain,
856  0) < 0) {
857  qDebug("XTRXOutput::applySettings: xtrx_set_gain(PAD) failed");
858  } else {
859  qDebug() << "XTRXOutput::applySettings: Gain (PAD) set to " << settings.m_gain;
860  }
861  }
862  }
863 
864  if ((m_settings.m_lpfBW != settings.m_lpfBW) || force)
865  {
866  reverseAPIKeys.append("lpfBW");
867 
868  if (m_deviceShared.m_dev->getDevice() != 0) {
869  doLPCalibration = true;
870  }
871  }
872 
873 #if 0
874  if ((m_settings.m_lpfFIRBW != settings.m_lpfFIRBW) ||
875  (m_settings.m_lpfFIREnable != settings.m_lpfFIREnable) || force)
876  {
877  if (m_deviceShared.m_deviceParams->getDevice() != 0 && m_channelAcquired)
878  {
879  if (LMS_SetGFIRLPF(m_deviceShared.m_deviceParams->getDevice(),
880  LMS_CH_RX,
882  settings.m_lpfFIREnable,
883  settings.m_lpfFIRBW) < 0)
884  {
885  qCritical("XTRXOutput::applySettings: could %s and set LPF FIR to %f Hz",
886  settings.m_lpfFIREnable ? "enable" : "disable",
887  settings.m_lpfFIRBW);
888  }
889  else
890  {
891  //doCalibration = true;
892  qDebug("XTRXOutput::applySettings: %sd and set LPF FIR to %f Hz",
893  settings.m_lpfFIREnable ? "enable" : "disable",
894  settings.m_lpfFIRBW);
895  }
896  }
897  }
898 #endif
899 
900  if ((m_settings.m_log2SoftInterp != settings.m_log2SoftInterp) || force)
901  {
902  reverseAPIKeys.append("log2SoftInterp");
903  forwardChangeOwnDSP = true;
904 
905  if (outputThread != 0)
906  {
907  outputThread->setLog2Interpolation(requestedChannel, settings.m_log2SoftInterp);
908  qDebug() << "XTRXOutput::applySettings: set soft interpolation to " << (1<<settings.m_log2SoftInterp);
909  }
910  }
911 
912  if ((m_settings.m_antennaPath != settings.m_antennaPath) || force)
913  {
914  reverseAPIKeys.append("antennaPath");
915 
916  if (m_deviceShared.m_dev->getDevice() != 0)
917  {
918  if (xtrx_set_antenna(m_deviceShared.m_dev->getDevice(), settings.m_antennaPath) < 0) {
919  qCritical("XTRXOutput::applySettings: could not set antenna path to %d", (int) settings.m_antennaPath);
920  } else {
921  qDebug("XTRXOutput::applySettings: set antenna path to %d", (int) settings.m_antennaPath);
922  }
923  }
924  }
925 
926  if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force)
927  {
928  reverseAPIKeys.append("centerFrequency");
929  doChangeFreq = true;
930  }
931 
932  if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency) || force) {
933  reverseAPIKeys.append("ncoFrequency");
934  }
935  if ((m_settings.m_ncoEnable != settings.m_ncoEnable) || force) {
936  reverseAPIKeys.append("ncoEnable");
937  }
938 
939  if ((m_settings.m_ncoFrequency != settings.m_ncoFrequency)
940  || (m_settings.m_ncoEnable != settings.m_ncoEnable) || force)
941  {
942  forceNCOFrequency = true;
943  }
944 
945  if (settings.m_useReverseAPI)
946  {
947  bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
951  webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
952  }
953 
954  m_settings = settings;
955 
956  if (doChangeSampleRate)
957  {
958  XTRXOutputThread *txThread = findThread();
959 
960  if (txThread && txThread->isRunning())
961  {
962  txThread->stopWork();
963  txThreadWasRunning = true;
964  }
965 
966  suspendRxThread();
967 
968  double master = (settings.m_log2HardInterp == 0) ? 0 : (settings.m_devSampleRate * 4 * (1 << settings.m_log2HardInterp));
969 
971  master, //(settings.m_devSampleRate<<settings.m_log2HardDecim)*4,
972  true) < 0)
973  {
974  qCritical("XTRXOutput::applySettings: could not set sample rate to %f with oversampling of %d",
975  settings.m_devSampleRate,
976  1<<settings.m_log2HardInterp);
977  }
978  else
979  {
980  doChangeFreq = true;
981  forceNCOFrequency = true;
982  forwardChangeAllDSP = true;
983 
984  qDebug("XTRXOutput::applySettings: sample rate set to %f with oversampling of %d",
986  1 << getLog2HardInterp());
987  }
988 
989  resumeRxThread();
990 
991  if (txThreadWasRunning) {
992  txThread->startWork();
993  }
994  }
995 
996  if (doLPCalibration)
997  {
998  if (xtrx_tune_tx_bandwidth(m_deviceShared.m_dev->getDevice(),
999  m_deviceShared.m_channel == 0 ? XTRX_CH_A : XTRX_CH_B,
1001  0) < 0) {
1002  qCritical("XTRXOutput::applySettings: could not set LPF to %f Hz", m_settings.m_lpfBW);
1003  } else {
1004  qDebug("XTRXOutput::applySettings: LPF set to %f Hz", m_settings.m_lpfBW);
1005  }
1006  }
1007 
1008  if (doChangeFreq)
1009  {
1010  forwardChangeTxDSP = true;
1011 
1012  if (m_deviceShared.m_dev->getDevice() != 0)
1013  {
1014  if (xtrx_tune(m_deviceShared.m_dev->getDevice(),
1015  XTRX_TUNE_TX_FDD,
1016  settings.m_centerFrequency,
1017  0) < 0) {
1018  qCritical("XTRXOutput::applySettings: could not set frequency to %lu", settings.m_centerFrequency);
1019  } else {
1020  //doCalibration = true;
1021  qDebug("XTRXOutput::applySettings: frequency set to %lu", settings.m_centerFrequency);
1022  }
1023  }
1024  }
1025 
1026  if (forceNCOFrequency)
1027  {
1028  if (m_deviceShared.m_dev->getDevice() != 0)
1029  {
1030  if (xtrx_tune_ex(m_deviceShared.m_dev->getDevice(),
1031  XTRX_TUNE_BB_TX,
1032  m_deviceShared.m_channel == 0 ? XTRX_CH_A : XTRX_CH_B,
1033  (settings.m_ncoEnable) ? settings.m_ncoFrequency : 0,
1034  NULL) < 0)
1035  {
1036  qCritical("XTRXOutput::applySettings: could not %s and set NCO to %d Hz",
1037  settings.m_ncoEnable ? "enable" : "disable",
1038  settings.m_ncoFrequency);
1039  }
1040  else
1041  {
1042  forwardChangeOwnDSP = true;
1043  qDebug("XTRXOutput::applySettings: %sd and set NCO to %d Hz",
1044  settings.m_ncoEnable ? "enable" : "disable",
1045  settings.m_ncoFrequency);
1046  }
1047  }
1048  }
1049 
1050  // forward changes to buddies or oneself
1051 
1052  if (forwardChangeAllDSP)
1053  {
1054  qDebug("XTRXOutput::applySettings: forward change to all buddies");
1055 
1056  int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
1057 
1058  // send to self first
1061 
1062  if (getMessageQueueToGUI())
1063  {
1065  getMessageQueueToGUI()->push(report);
1066  }
1067 
1068  // send to source buddies
1069  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
1070  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
1071 
1072  for (; itSource != sourceBuddies.end(); ++itSource)
1073  {
1076  (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
1077  }
1078 
1079  // send to sink buddies
1080  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
1081  std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
1082 
1083  for (; itSink != sinkBuddies.end(); ++itSink)
1084  {
1087  (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
1088  }
1089  }
1090  else if (forwardChangeTxDSP)
1091  {
1092  qDebug("XTRXOutput::applySettings: forward change to Tx buddies");
1093 
1094  int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
1095 
1096  // send to self first
1099 
1100  if (getMessageQueueToGUI())
1101  {
1103  getMessageQueueToGUI()->push(report);
1104  }
1105 
1106  // send to sink buddies
1107  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
1108  std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
1109 
1110  for (; itSink != sinkBuddies.end(); ++itSink)
1111  {
1114  (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
1115  }
1116  }
1117  else if (forwardChangeOwnDSP)
1118  {
1119  qDebug("XTRXOutput::applySettings: forward change to self only");
1120 
1121  int ncoShift = m_settings.m_ncoEnable ? m_settings.m_ncoFrequency : 0;
1122 
1125 
1126  if (getMessageQueueToGUI())
1127  {
1129  getMessageQueueToGUI()->push(report);
1130  }
1131  }
1132 
1133  if (forwardClockSource)
1134  {
1135  // send to source buddies
1136  const std::vector<DeviceAPI*>& sourceBuddies = m_deviceAPI->getSourceBuddies();
1137  std::vector<DeviceAPI*>::const_iterator itSource = sourceBuddies.begin();
1138 
1139  for (; itSource != sourceBuddies.end(); ++itSource)
1140  {
1143  (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
1144  }
1145 
1146  // send to sink buddies
1147  const std::vector<DeviceAPI*>& sinkBuddies = m_deviceAPI->getSinkBuddies();
1148  std::vector<DeviceAPI*>::const_iterator itSink = sinkBuddies.begin();
1149 
1150  for (; itSink != sinkBuddies.end(); ++itSink)
1151  {
1154  (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
1155  }
1156  }
1157 
1158  qDebug() << "XTRXOutput::applySettings:"
1159  << " device stream sample rate: " << getDevSampleRate() << "S/s"
1160  << " sample rate with soft interpolation: " << getSampleRate() << "S/s"
1161  << " forceNCOFrequency: " << forceNCOFrequency
1162  << " doLPCalibration: " << doLPCalibration
1163  << " doChangeFreq: " << doChangeFreq
1164  << " doChangeSampleRate: " << doChangeSampleRate;
1165 
1166  return true;
1167 }
1168 
1171  QString& errorMessage)
1172 {
1173  (void) errorMessage;
1175  response.getXtrxOutputSettings()->init();
1177  return 200;
1178 }
1179 
1181  bool force,
1182  const QStringList& deviceSettingsKeys,
1183  SWGSDRangel::SWGDeviceSettings& response, // query + response
1184  QString& errorMessage)
1185 {
1186  (void) errorMessage;
1187  XTRXOutputSettings settings = m_settings;
1188 
1189  if (deviceSettingsKeys.contains("centerFrequency")) {
1191  }
1192  if (deviceSettingsKeys.contains("devSampleRate")) {
1193  settings.m_devSampleRate = response.getXtrxOutputSettings()->getDevSampleRate();
1194  }
1195  if (deviceSettingsKeys.contains("log2HardInterp")) {
1196  settings.m_log2HardInterp = response.getXtrxOutputSettings()->getLog2HardInterp();
1197  }
1198  if (deviceSettingsKeys.contains("log2SoftInterp")) {
1199  settings.m_log2SoftInterp = response.getXtrxOutputSettings()->getLog2SoftInterp();
1200  }
1201  if (deviceSettingsKeys.contains("lpfBW")) {
1202  settings.m_lpfBW = response.getXtrxOutputSettings()->getLpfBw();
1203  }
1204  if (deviceSettingsKeys.contains("gain")) {
1205  settings.m_gain = response.getXtrxOutputSettings()->getGain();
1206  }
1207  if (deviceSettingsKeys.contains("ncoEnable")) {
1208  settings.m_ncoEnable = response.getXtrxOutputSettings()->getNcoEnable() != 0;
1209  }
1210  if (deviceSettingsKeys.contains("ncoFrequency")) {
1211  settings.m_ncoFrequency = response.getXtrxOutputSettings()->getNcoFrequency();
1212  }
1213  if (deviceSettingsKeys.contains("antennaPath")) {
1214  settings.m_antennaPath = (xtrx_antenna_t) response.getXtrxOutputSettings()->getAntennaPath();
1215  }
1216  if (deviceSettingsKeys.contains("extClock")) {
1217  settings.m_extClock = response.getXtrxOutputSettings()->getExtClock() != 0;
1218  }
1219  if (deviceSettingsKeys.contains("extClockFreq")) {
1220  settings.m_extClockFreq = response.getXtrxOutputSettings()->getExtClockFreq();
1221  }
1222  if (deviceSettingsKeys.contains("pwrmode")) {
1223  settings.m_pwrmode = response.getXtrxOutputSettings()->getPwrmode();
1224  }
1225  if (deviceSettingsKeys.contains("useReverseAPI")) {
1226  settings.m_useReverseAPI = response.getXtrxOutputSettings()->getUseReverseApi() != 0;
1227  }
1228  if (deviceSettingsKeys.contains("reverseAPIAddress")) {
1230  }
1231  if (deviceSettingsKeys.contains("reverseAPIPort")) {
1232  settings.m_reverseAPIPort = response.getXtrxOutputSettings()->getReverseApiPort();
1233  }
1234  if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
1236  }
1237 
1238  MsgConfigureXTRX *msg = MsgConfigureXTRX::create(settings, force);
1240 
1241  if (m_guiMessageQueue) // forward to GUI if any
1242  {
1243  MsgConfigureXTRX *msgToGUI = MsgConfigureXTRX::create(settings, force);
1244  m_guiMessageQueue->push(msgToGUI);
1245  }
1246 
1247  webapiFormatDeviceSettings(response, settings);
1248  return 200;
1249 }
1250 
1252 {
1257  response.getXtrxOutputSettings()->setLpfBw(settings.m_lpfBW);
1258  response.getXtrxOutputSettings()->setGain(settings.m_gain);
1259  response.getXtrxOutputSettings()->setNcoEnable(settings.m_ncoEnable ? 1 : 0);
1261  response.getXtrxOutputSettings()->setAntennaPath((int) settings.m_antennaPath);
1262  response.getXtrxOutputSettings()->setExtClock(settings.m_extClock ? 1 : 0);
1264  response.getXtrxOutputSettings()->setPwrmode(settings.m_pwrmode);
1265  response.getXtrxOutputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
1266 
1267  if (response.getXtrxOutputSettings()->getReverseApiAddress()) {
1269  } else {
1270  response.getXtrxOutputSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
1271  }
1272 
1275 }
1276 
1278  SWGSDRangel::SWGDeviceReport& response,
1279  QString& errorMessage)
1280 {
1281  (void) errorMessage;
1283  response.getXtrxOutputReport()->init();
1284  webapiFormatDeviceReport(response);
1285  return 200;
1286 }
1287 
1289  SWGSDRangel::SWGDeviceState& response,
1290  QString& errorMessage)
1291 {
1292  (void) errorMessage;
1294  return 200;
1295 }
1296 
1298  bool run,
1299  SWGSDRangel::SWGDeviceState& response,
1300  QString& errorMessage)
1301 {
1302  (void) errorMessage;
1304  MsgStartStop *message = MsgStartStop::create(run);
1305  m_inputMessageQueue.push(message);
1306 
1307  if (m_guiMessageQueue) // forward to GUI if any
1308  {
1309  MsgStartStop *msgToGUI = MsgStartStop::create(run);
1310  m_guiMessageQueue->push(msgToGUI);
1311  }
1312 
1313  return 200;
1314 }
1315 
1317 {
1318  int ret;
1319  bool success = false;
1320  double temp = 0.0;
1321  bool gpsStatus = false;
1322  uint64_t fifolevel = 0;
1323  uint32_t fifosize = 1<<16;
1324 
1326  {
1327  ret = xtrx_val_get(m_deviceShared.m_dev->getDevice(),
1328  XTRX_TX, XTRX_CH_AB, XTRX_PERF_LLFIFO, &fifolevel);
1329  success = (ret >= 0);
1330  temp = m_deviceShared.get_board_temperature() / 256.0;
1331  gpsStatus = m_deviceShared.get_gps_status();
1332  }
1333 
1334  response.getXtrxOutputReport()->setSuccess(success ? 1 : 0);
1335  response.getXtrxOutputReport()->setFifoSize(fifosize);
1336  response.getXtrxOutputReport()->setFifoFill(fifolevel);
1337  response.getXtrxOutputReport()->setTemperature(temp);
1338  response.getXtrxOutputReport()->setGpsLock(gpsStatus ? 1 : 0);
1339 }
1340 
1341 void XTRXOutput::webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const XTRXOutputSettings& settings, bool force)
1342 {
1344  swgDeviceSettings->setDirection(1); // Single Tx
1345  swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
1346  swgDeviceSettings->setDeviceHwType(new QString("XTRX"));
1348  SWGSDRangel::SWGXtrxOutputSettings *swgXtrxOutputSettings = swgDeviceSettings->getXtrxOutputSettings();
1349 
1350  // transfer data that has been modified. When force is on transfer all data except reverse API data
1351 
1352  if (deviceSettingsKeys.contains("centerFrequency") || force) {
1353  swgXtrxOutputSettings->setCenterFrequency(settings.m_centerFrequency);
1354  }
1355  if (deviceSettingsKeys.contains("devSampleRate") || force) {
1356  swgXtrxOutputSettings->setDevSampleRate(settings.m_devSampleRate);
1357  }
1358  if (deviceSettingsKeys.contains("log2HardInterp") || force) {
1359  swgXtrxOutputSettings->setLog2HardInterp(settings.m_log2HardInterp);
1360  }
1361  if (deviceSettingsKeys.contains("log2SoftInterp") || force) {
1362  swgXtrxOutputSettings->setLog2SoftInterp(settings.m_log2SoftInterp);
1363  }
1364  if (deviceSettingsKeys.contains("ncoEnable") || force) {
1365  swgXtrxOutputSettings->setNcoEnable(settings.m_ncoEnable ? 1 : 0);
1366  }
1367  if (deviceSettingsKeys.contains("ncoFrequency") || force) {
1368  swgXtrxOutputSettings->setNcoFrequency(settings.m_ncoFrequency);
1369  }
1370  if (deviceSettingsKeys.contains("lpfBW") || force) {
1371  swgXtrxOutputSettings->setLpfBw(settings.m_lpfBW);
1372  }
1373  if (deviceSettingsKeys.contains("antennaPath") || force) {
1374  swgXtrxOutputSettings->setAntennaPath((int) settings.m_antennaPath);
1375  }
1376  if (deviceSettingsKeys.contains("gain") || force) {
1377  swgXtrxOutputSettings->setGain(settings.m_gain);
1378  }
1379  if (deviceSettingsKeys.contains("extClock") || force) {
1380  swgXtrxOutputSettings->setExtClock(settings.m_extClock ? 1 : 0);
1381  }
1382  if (deviceSettingsKeys.contains("extClockFreq") || force) {
1383  swgXtrxOutputSettings->setExtClockFreq(settings.m_extClockFreq);
1384  }
1385  if (deviceSettingsKeys.contains("pwrmode") || force) {
1386  swgXtrxOutputSettings->setPwrmode(settings.m_pwrmode);
1387  }
1388 
1389  QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
1390  .arg(settings.m_reverseAPIAddress)
1391  .arg(settings.m_reverseAPIPort)
1392  .arg(settings.m_reverseAPIDeviceIndex);
1393  m_networkRequest.setUrl(QUrl(deviceSettingsURL));
1394  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1395 
1396  QBuffer *buffer=new QBuffer();
1397  buffer->open((QBuffer::ReadWrite));
1398  buffer->write(swgDeviceSettings->asJson().toUtf8());
1399  buffer->seek(0);
1400 
1401  // Always use PATCH to avoid passing reverse API settings
1402  m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
1403 
1404  delete swgDeviceSettings;
1405 }
1406 
1408 {
1410  swgDeviceSettings->setDirection(1); // Single Tx
1411  swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
1412  swgDeviceSettings->setDeviceHwType(new QString("XTRX"));
1413 
1414  QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
1418  m_networkRequest.setUrl(QUrl(deviceSettingsURL));
1419  m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
1420 
1421  QBuffer *buffer=new QBuffer();
1422  buffer->open((QBuffer::ReadWrite));
1423  buffer->write(swgDeviceSettings->asJson().toUtf8());
1424  buffer->seek(0);
1425 
1426  if (start) {
1427  m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
1428  } else {
1429  m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
1430  }
1431 
1432  delete swgDeviceSettings;
1433 }
1434 
1435 void XTRXOutput::networkManagerFinished(QNetworkReply *reply)
1436 {
1437  QNetworkReply::NetworkError replyError = reply->error();
1438 
1439  if (replyError)
1440  {
1441  qWarning() << "XTRXOutput::networkManagerFinished:"
1442  << " error(" << (int) replyError
1443  << "): " << replyError
1444  << ": " << reply->errorString();
1445  return;
1446  }
1447 
1448  QString answer = reply->readAll();
1449  answer.chop(1); // remove last \n
1450  qDebug("XTRXOutput::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
1451 }
bool m_running
Definition: xtrxoutput.h:232
virtual QByteArray serialize() const
Definition: xtrxoutput.cpp:461
bool startDeviceEngine()
Start the device engine corresponding to the stream type.
Definition: deviceapi.cpp:253
double getClockGen() const
Definition: xtrxoutput.cpp:526
XTRXOutput * m_sink
uint32_t getLog2HardInterp() const
Definition: xtrxoutput.cpp:515
void webapiReverseSendSettings(QList< QString > &deviceSettingsKeys, const XTRXOutputSettings &settings, bool force)
virtual bool isRunning()
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
virtual bool deserialize(const QByteArray &data)
Definition: xtrxoutput.cpp:466
void setExtClockFreq(qint32 ext_clock_freq)
virtual int getSampleRate() const
Sample rate exposed by the sink.
Definition: xtrxoutput.cpp:493
void getLPRange(float &minF, float &maxF, float &stepF) const
Definition: xtrxoutput.cpp:578
void setUseReverseApi(qint32 use_reverse_api)
unsigned int getLog2Interpolation(unsigned int channel) const
static MsgConfigureXTRX * create(const XTRXOutputSettings &settings, bool force)
Definition: xtrxoutput.h:48
const std::vector< DeviceAPI * > & getSinkBuddies() const
Definition: deviceapi.h:166
static MsgReportClockSourceChange * create(bool extClock, uint32_t m_extClockFreq)
virtual QString asJson() override
bool m_extClock
True if external clock source.
virtual int webapiRun(bool run, SWGSDRangel::SWGDeviceState &response, QString &errorMessage)
MessageQueue * getDeviceEngineInputMessageQueue()
Device engine message queue.
Definition: deviceapi.cpp:316
void setThread(XTRXOutputThread *thread)
Definition: xtrxoutput.h:181
uint32_t m_gain
Optimally distributed gain (dB)
double getClockGen() const
Definition: devicextrx.h:38
int m_ncoFrequency
Actual NCO frequency (the resulting frequency with mixing is displayed)
double getActualOutputRate() const
Definition: devicextrx.h:40
virtual quint64 getCenterFrequency() const
Center frequency exposed by the sink.
Definition: xtrxoutput.cpp:535
uint32_t getDeviceItemIndex() const
Definition: deviceapi.h:129
uint32_t getDevSampleRate() const
Definition: xtrxoutput.cpp:504
virtual void stopWork()
void resumeRxThread()
Definition: xtrxoutput.cpp:444
xtrx_antenna_t m_antennaPath
void setReverseApiPort(qint32 reverse_api_port)
bool initDeviceEngine()
Init the device engine corresponding to the stream type.
Definition: deviceapi.cpp:240
double set_samplerate(double rate, double master, bool output)
Definition: devicextrx.cpp:95
XTRXOutputThread * getThread()
Definition: xtrxoutput.h:180
DeviceAPI * m_deviceAPI
Definition: xtrxoutput.h:227
void closeDevice()
Definition: xtrxoutput.cpp:152
static MsgReportClockGenChange * create()
Definition: xtrxoutput.h:98
std::size_t getChannelIndex()
Definition: xtrxoutput.cpp:555
virtual int webapiSettingsGet(SWGSDRangel::SWGDeviceSettings &response, QString &errorMessage)
bool openDevice()
Definition: xtrxoutput.cpp:78
bool m_ncoEnable
Enable TSP NCO and mixing.
void setOriginatorIndex(qint32 originator_index)
virtual ~XTRXOutput()
Definition: xtrxoutput.cpp:61
DeviceXTRXShared m_deviceShared
Definition: xtrxoutput.h:233
struct xtrx_dev * getDevice()
Definition: devicextrx.h:35
virtual void init()
initializations to be done when all collaborating objects are created and possibly connected ...
Definition: xtrxoutput.cpp:179
XTRXOutputThread * m_XTRXOutputThread
Definition: xtrxoutput.h:230
unsigned int uint32_t
Definition: rtptypes_win.h:46
MessageQueue m_inputMessageQueue
Input queue to the sink.
SWGXtrxOutputReport * getXtrxOutputReport()
QByteArray serialize() const
QNetworkAccessManager * m_networkManager
Definition: xtrxoutput.h:234
int getDeviceSetIndex() const
Definition: deviceapi.h:131
bool m_threadWasRunning
flag to know if thread needs to be resumed after suspend
uint16_t m_reverseAPIDeviceIndex
void * getBuddySharedPtr() const
Definition: deviceapi.h:161
void setNcoFrequency(qint32 nco_frequency)
uint32_t m_extClockFreq
Frequency (Hz) of external clock source.
QString m_deviceDescription
Definition: xtrxoutput.h:231
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
virtual int webapiReportGet(SWGSDRangel::SWGDeviceReport &response, QString &errorMessage)
SWGXtrxOutputSettings * getXtrxOutputSettings()
virtual bool handleMessage(const Message &message)
Definition: xtrxoutput.cpp:587
void setLog2SoftInterp(qint32 log2_soft_interp)
void setBuddySharedPtr(void *ptr)
Definition: deviceapi.h:162
static MsgReportStreamInfo * create(bool success, bool active, uint32_t fifoFilledCount, uint32_t fifoSize)
Definition: xtrxoutput.h:118
int32_t i
Definition: decimators.h:244
XTRXOutputThread * findThread()
Definition: xtrxoutput.cpp:184
virtual int webapiSettingsPutPatch(bool force, const QStringList &deviceSettingsKeys, SWGSDRangel::SWGDeviceSettings &response, QString &errorMessage)
double get_board_temperature()
static bool match(const Message *message)
Definition: message.cpp:45
SampleSourceFifo * getFifo(unsigned int channel)
DeviceXTRX * m_dev
XTRXOutputSettings m_settings
Definition: xtrxoutput.h:229
void setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
void webapiReverseSendStartStop(bool start)
virtual int webapiRunGet(SWGSDRangel::SWGDeviceState &response, QString &errorMessage)
void setLog2HardInterp(qint32 log2_hard_interp)
void close()
Definition: devicextrx.cpp:70
int m_channel
allocated channel (-1 if none)
void setTemperature(float temperature)
virtual void stop()
Definition: xtrxoutput.cpp:349
unsigned int getNbChannels() const
bool deserialize(const QByteArray &data)
void getDeviceEngineStateStr(QString &state)
Definition: deviceapi.cpp:389
void resize(uint32_t size)
void setXtrxOutputReport(SWGXtrxOutputReport *xtrx_output_report)
const QString & getSamplingDeviceSerial() const
Definition: deviceapi.h:121
const XTRXOutputSettings & getSettings() const
Definition: xtrxoutput.h:45
bool getStartStop() const
Definition: xtrxoutput.h:158
void networkManagerFinished(QNetworkReply *reply)
static MsgReportBuddyChange * create(int devSampleRate, unsigned log2HardDecimInterp, uint64_t centerFrequency, bool rxElseTx)
void setDevSampleRate(qint32 dev_sample_rate)
void webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings &response, const XTRXOutputSettings &settings)
bool applySettings(const XTRXOutputSettings &settings, bool force=false, bool forceNCOFrequency=false)
Definition: xtrxoutput.cpp:757
virtual void setCenterFrequency(qint64 centerFrequency)
Definition: xtrxoutput.cpp:540
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport &response)
SampleSourceFifo m_sampleSourceFifo
const std::vector< DeviceAPI * > & getSourceBuddies() const
Definition: deviceapi.h:165
virtual void startWork()
static MsgStartStop * create(bool startStop)
Definition: xtrxoutput.h:160
virtual void destroy()
Definition: xtrxoutput.cpp:73
ThreadInterface * m_thread
holds the thread address if started else 0
void getLORange(float &minF, float &maxF, float &stepF) const
Definition: xtrxoutput.cpp:560
void setReverseApiAddress(QString *reverse_api_address)
float m_lpfBW
LMS analog lowpass filter bandwidth (Hz)
void setCenterFrequency(qint64 center_frequency)
QNetworkRequest m_networkRequest
Definition: xtrxoutput.h:235
void getSRRange(float &minF, float &maxF, float &stepF) const
Definition: xtrxoutput.cpp:569
void suspendRxThread()
Definition: xtrxoutput.cpp:421
MessageQueue * getMessageQueueToGUI()
bool open(const char *deviceStr)
Definition: devicextrx.cpp:54
void setLog2Interpolation(unsigned int channel, unsigned int log2_interp)
void setDirection(qint32 direction)
MessageQueue * m_guiMessageQueue
Input message queue to the GUI.
virtual bool start()
Definition: xtrxoutput.cpp:233
virtual const QString & getDeviceDescription() const
Definition: xtrxoutput.cpp:488
static MsgReportDeviceInfo * create(float temperature, bool gpsLocked)
void setXtrxOutputSettings(SWGXtrxOutputSettings *xtrx_output_settings)
void setDeviceHwType(QString *device_hw_type)
void moveThreadToBuddy()
Definition: xtrxoutput.cpp:216
void setReverseApiDeviceIndex(qint32 reverse_api_device_index)
MessageQueue * getSamplingDeviceGUIMessageQueue()
Sampling device (ex: single Tx) GUI input message queue.
Definition: deviceapi.cpp:342
unsigned __int64 uint64_t
Definition: rtptypes_win.h:48