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.
soapysdroutputthread.cpp
Go to the documentation of this file.
1 // Copyright (C) 2015 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #include <algorithm>
19 
20 #include <SoapySDR/Formats.hpp>
21 #include <SoapySDR/Errors.hpp>
22 
23 #include "dsp/samplesourcefifo.h"
24 
25 #include "soapysdroutputthread.h"
26 
27 SoapySDROutputThread::SoapySDROutputThread(SoapySDR::Device* dev, unsigned int nbTxChannels, QObject* parent) :
28  QThread(parent),
29  m_running(false),
30  m_dev(dev),
31  m_sampleRate(0),
32  m_nbChannels(nbTxChannels),
33  m_interpolatorType(InterpolatorFloat)
34 {
35  qDebug("SoapySDROutputThread::SoapySDROutputThread");
36  m_channels = new Channel[nbTxChannels];
37 }
38 
40 {
41  qDebug("SoapySDROutputThread::~SoapySDROutputThread");
42 
43  if (m_running) {
44  stopWork();
45  }
46 
47  delete[] m_channels;
48 }
49 
51 {
52  if (m_running) {
53  return;
54  }
55 
56  m_startWaitMutex.lock();
57  start();
58 
59  while(!m_running) {
60  m_startWaiter.wait(&m_startWaitMutex, 100);
61  }
62 
63  m_startWaitMutex.unlock();
64 }
65 
67 {
68  if (!m_running) {
69  return;
70  }
71 
72  m_running = false;
73  wait();
74 }
75 
77 {
78  m_running = true;
79  m_startWaiter.wakeAll();
80 
81  unsigned int nbFifos = getNbFifos();
82 
83  if ((m_nbChannels > 0) && (nbFifos > 0))
84  {
85  // build channels list
86  std::vector<std::size_t> channels(m_nbChannels);
87  std::iota(channels.begin(), channels.end(), 0); // Fill with 0, 1, ..., m_nbChannels-1.
88 
89  //initialize the sample rate for all channels
90  qDebug("SoapySDROutputThread::run: m_sampleRate: %u", m_sampleRate);
91  for (const auto &it : channels) {
92  m_dev->setSampleRate(SOAPY_SDR_TX, it, m_sampleRate);
93  }
94 
95  // Determine sample format to be used
96  double fullScale(0.0);
97  std::string format = m_dev->getNativeStreamFormat(SOAPY_SDR_TX, channels.front(), fullScale);
98 
99  qDebug("SoapySDROutputThread::run: format: %s fullScale: %f", format.c_str(), fullScale);
100 
101  if ((format == "CS8") && (fullScale == 128.0)) { // 8 bit signed - native
103  } else if ((format == "CS16") && (fullScale == 2048.0)) { // 12 bit signed - native
105  } else if ((format == "CS16") && (fullScale == 32768.0)) { // 16 bit signed - native
107  } else { // for other types make a conversion to float
109  format = "CF32";
110  }
111 
112  unsigned int elemSize = SoapySDR::formatToSize(format); // sample (I+Q) size in bytes
113  SoapySDR::Stream *stream = m_dev->setupStream(SOAPY_SDR_TX, format, channels);
114 
115  //allocate buffers for the stream read/write
116  const unsigned int numElems = m_dev->getStreamMTU(stream); // number of samples (I+Q)
117  std::vector<std::vector<char>> buffMem(m_nbChannels, std::vector<char>(elemSize*numElems));
118  std::vector<void *> buffs(m_nbChannels);
119 
120  for (std::size_t i = 0; i < m_nbChannels; i++) {
121  buffs[i] = buffMem[i].data();
122  }
123 
124  m_dev->activateStream(stream);
125  int flags(0);
126  long long timeNs(0);
127  float blockTime = ((float) numElems) / (m_sampleRate <= 0 ? 1024000 : m_sampleRate);
128  long initialTtimeoutUs = 10000000 * blockTime; // 10 times the block time
129  long timeoutUs = initialTtimeoutUs < 250000 ? 250000 : initialTtimeoutUs; // 250ms minimum
130 
131  qDebug("SoapySDROutputThread::run: numElems: %u elemSize: %u initialTtimeoutUs: %ld timeoutUs: %ld",
132  numElems, elemSize, initialTtimeoutUs, timeoutUs);
133  qDebug("SoapySDROutputThread::run: start running loop");
134 
135  while (m_running)
136  {
137  int ret = m_dev->writeStream(stream, buffs.data(), numElems, flags, timeNs, timeoutUs);
138 
139  if (ret == SOAPY_SDR_TIMEOUT)
140  {
141  qWarning("SoapySDROutputThread::run: timeout: flags: %d timeNs: %lld timeoutUs: %ld", flags, timeNs, timeoutUs);
142  }
143  else if (ret == SOAPY_SDR_OVERFLOW)
144  {
145  qWarning("SoapySDROutputThread::run: overflow: flags: %d timeNs: %lld timeoutUs: %ld", flags, timeNs, timeoutUs);
146  }
147  else if (ret < 0)
148  {
149  qCritical("SoapySDROutputThread::run: Unexpected write stream error: %s", SoapySDR::errToStr(ret));
150  break;
151  }
152 
153  if (m_nbChannels > 1)
154  {
155  callbackMO(buffs, numElems); // size given in number of samples (1 item per sample)
156  }
157  else
158  {
159  switch (m_interpolatorType)
160  {
161  case Interpolator8:
162  callbackSO8((qint8*) buffs[0], numElems);
163  break;
164  case Interpolator12:
165  callbackSO12((qint16*) buffs[0], numElems);
166  break;
167  case Interpolator16:
168  callbackSO16((qint16*) buffs[0], numElems);
169  break;
170  case InterpolatorFloat:
171  default:
172  callbackSOIF((float*) buffs[0], numElems);
173  break;
174  }
175  }
176  }
177 
178  qDebug("SoapySDROutputThread::run: stop running loop");
179  m_dev->deactivateStream(stream);
180  m_dev->closeStream(stream);
181 
182  }
183  else
184  {
185  qWarning("SoapySDROutputThread::run: no channels or FIFO allocated. Aborting");
186  }
187 
188  m_running = false;
189 }
190 
192 {
193  unsigned int fifoCount = 0;
194 
195  for (unsigned int i = 0; i < m_nbChannels; i++)
196  {
197  if (m_channels[i].m_sampleFifo) {
198  fifoCount++;
199  }
200  }
201 
202  return fifoCount;
203 }
204 
205 void SoapySDROutputThread::setLog2Interpolation(unsigned int channel, unsigned int log2_interp)
206 {
207  if (channel < m_nbChannels) {
208  m_channels[channel].m_log2Interp = log2_interp;
209  }
210 }
211 
212 unsigned int SoapySDROutputThread::getLog2Interpolation(unsigned int channel) const
213 {
214  if (channel < m_nbChannels) {
215  return m_channels[channel].m_log2Interp;
216  } else {
217  return 0;
218  }
219 }
220 
221 void SoapySDROutputThread::setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
222 {
223  if (channel < m_nbChannels) {
224  m_channels[channel].m_sampleFifo = sampleFifo;
225  }
226 }
227 
229 {
230  if (channel < m_nbChannels) {
231  return m_channels[channel].m_sampleFifo;
232  } else {
233  return 0;
234  }
235 }
236 
237 void SoapySDROutputThread::callbackMO(std::vector<void *>& buffs, qint32 samplesPerChannel)
238 {
239  for(unsigned int ichan = 0; ichan < m_nbChannels; ichan++)
240  {
241  if (m_channels[ichan].m_sampleFifo)
242  {
243  switch (m_interpolatorType)
244  {
245  case Interpolator8:
246  callbackSO8((qint8*) buffs[ichan], samplesPerChannel, ichan);
247  break;
248  case Interpolator12:
249  callbackSO12((qint16*) buffs[ichan], samplesPerChannel, ichan);
250  break;
251  case Interpolator16:
252  callbackSO16((qint16*) buffs[ichan], samplesPerChannel, ichan);
253  break;
254  case InterpolatorFloat:
255  default:
256  // TODO
257  break;
258  }
259  }
260  else // no FIFO for this channel means channel is unused: fill with zeros
261  {
262  switch (m_interpolatorType)
263  {
264  case Interpolator8:
265  std::fill((qint8*) buffs[ichan], (qint8*) buffs[ichan] + 2*samplesPerChannel, 0);
266  break;
267  case Interpolator12:
268  case Interpolator16:
269  std::fill((qint16*) buffs[ichan], (qint16*) buffs[ichan] + 2*samplesPerChannel, 0);
270  break;
271  case InterpolatorFloat:
272  default:
273  std::fill((float*) buffs[ichan], (float*) buffs[ichan] + 2*samplesPerChannel, 0.0f);
274  break;
275  }
276  }
277  }
278 }
279 
280 // Interpolate according to specified log2 (ex: log2=4 => decim=16). len is a number of samples (not a number of I or Q)
281 
282 void SoapySDROutputThread::callbackSO8(qint8* buf, qint32 len, unsigned int channel)
283 {
284  if (m_channels[channel].m_sampleFifo)
285  {
286  float bal = m_channels[channel].m_sampleFifo->getRWBalance();
287 
288  if (bal < -0.25) {
289  qDebug("SoapySDROutputThread::callbackSO8: read lags: %f", bal);
290  } else if (bal > 0.25) {
291  qDebug("SoapySDROutputThread::callbackSO8: read leads: %f", bal);
292  }
293 
294  SampleVector::iterator beginRead;
295  m_channels[channel].m_sampleFifo->readAdvance(beginRead, len/(1<<m_channels[channel].m_log2Interp));
296  beginRead -= len;
297 
298  if (m_channels[channel].m_log2Interp == 0)
299  {
300  m_channels[channel].m_interpolators8.interpolate1(&beginRead, buf, len*2);
301  }
302  else
303  {
304  switch (m_channels[channel].m_log2Interp)
305  {
306  case 1:
307  m_channels[channel].m_interpolators8.interpolate2_cen(&beginRead, buf, len*2);
308  break;
309  case 2:
310  m_channels[channel].m_interpolators8.interpolate4_cen(&beginRead, buf, len*2);
311  break;
312  case 3:
313  m_channels[channel].m_interpolators8.interpolate8_cen(&beginRead, buf, len*2);
314  break;
315  case 4:
316  m_channels[channel].m_interpolators8.interpolate16_cen(&beginRead, buf, len*2);
317  break;
318  case 5:
319  m_channels[channel].m_interpolators8.interpolate32_cen(&beginRead, buf, len*2);
320  break;
321  case 6:
322  m_channels[channel].m_interpolators8.interpolate64_cen(&beginRead, buf, len*2);
323  break;
324  default:
325  break;
326  }
327  }
328  }
329  else
330  {
331  std::fill(buf, buf+2*len, 0);
332  }
333 }
334 
335 void SoapySDROutputThread::callbackSO12(qint16* buf, qint32 len, unsigned int channel)
336 {
337  if (m_channels[channel].m_sampleFifo)
338  {
339  float bal = m_channels[channel].m_sampleFifo->getRWBalance();
340 
341  if (bal < -0.25) {
342  qDebug("SoapySDROutputThread::callbackSO12: read lags: %f", bal);
343  } else if (bal > 0.25) {
344  qDebug("SoapySDROutputThread::callbackSO12: read leads: %f", bal);
345  }
346 
347  SampleVector::iterator beginRead;
348  m_channels[channel].m_sampleFifo->readAdvance(beginRead, len/(1<<m_channels[channel].m_log2Interp));
349  beginRead -= len;
350 
351  if (m_channels[channel].m_log2Interp == 0)
352  {
353  m_channels[channel].m_interpolators12.interpolate1(&beginRead, buf, len*2);
354  }
355  else
356  {
357  switch (m_channels[channel].m_log2Interp)
358  {
359  case 1:
360  m_channels[channel].m_interpolators12.interpolate2_cen(&beginRead, buf, len*2);
361  break;
362  case 2:
363  m_channels[channel].m_interpolators12.interpolate4_cen(&beginRead, buf, len*2);
364  break;
365  case 3:
366  m_channels[channel].m_interpolators12.interpolate8_cen(&beginRead, buf, len*2);
367  break;
368  case 4:
369  m_channels[channel].m_interpolators12.interpolate16_cen(&beginRead, buf, len*2);
370  break;
371  case 5:
372  m_channels[channel].m_interpolators12.interpolate32_cen(&beginRead, buf, len*2);
373  break;
374  case 6:
375  m_channels[channel].m_interpolators12.interpolate64_cen(&beginRead, buf, len*2);
376  break;
377  default:
378  break;
379  }
380  }
381  }
382  else
383  {
384  std::fill(buf, buf+2*len, 0);
385  }
386 }
387 
388 void SoapySDROutputThread::callbackSO16(qint16* buf, qint32 len, unsigned int channel)
389 {
390  if (m_channels[channel].m_sampleFifo)
391  {
392  float bal = m_channels[channel].m_sampleFifo->getRWBalance();
393 
394  if (bal < -0.25) {
395  qDebug("SoapySDROutputThread::callbackSO16: read lags: %f", bal);
396  } else if (bal > 0.25) {
397  qDebug("SoapySDROutputThread::callbackSO16: read leads: %f", bal);
398  }
399 
400  SampleVector::iterator beginRead;
401  m_channels[channel].m_sampleFifo->readAdvance(beginRead, len/(1<<m_channels[channel].m_log2Interp));
402  beginRead -= len;
403 
404  if (m_channels[channel].m_log2Interp == 0)
405  {
406  m_channels[channel].m_interpolators16.interpolate1(&beginRead, buf, len*2);
407  }
408  else
409  {
410  switch (m_channels[channel].m_log2Interp)
411  {
412  case 1:
413  m_channels[channel].m_interpolators16.interpolate2_cen(&beginRead, buf, len*2);
414  break;
415  case 2:
416  m_channels[channel].m_interpolators16.interpolate4_cen(&beginRead, buf, len*2);
417  break;
418  case 3:
419  m_channels[channel].m_interpolators16.interpolate8_cen(&beginRead, buf, len*2);
420  break;
421  case 4:
422  m_channels[channel].m_interpolators16.interpolate16_cen(&beginRead, buf, len*2);
423  break;
424  case 5:
425  m_channels[channel].m_interpolators16.interpolate32_cen(&beginRead, buf, len*2);
426  break;
427  case 6:
428  m_channels[channel].m_interpolators16.interpolate64_cen(&beginRead, buf, len*2);
429  break;
430  default:
431  break;
432  }
433  }
434  }
435  else
436  {
437  std::fill(buf, buf+2*len, 0);
438  }
439 }
440 
441 
442 void SoapySDROutputThread::callbackSOIF(float* buf, qint32 len, unsigned int channel)
443 {
444  if (m_channels[channel].m_sampleFifo)
445  {
446  float bal = m_channels[channel].m_sampleFifo->getRWBalance();
447 
448  if (bal < -0.25) {
449  qDebug("SoapySDROutputThread::callbackSO16: read lags: %f", bal);
450  } else if (bal > 0.25) {
451  qDebug("SoapySDROutputThread::callbackSO16: read leads: %f", bal);
452  }
453 
454  SampleVector::iterator beginRead;
455  m_channels[channel].m_sampleFifo->readAdvance(beginRead, len/(1<<m_channels[channel].m_log2Interp));
456  beginRead -= len;
457 
458  if (m_channels[channel].m_log2Interp == 0)
459  {
460  m_channels[channel].m_interpolatorsIF.interpolate1(&beginRead, buf, len*2);
461  }
462  else
463  {
464  switch (m_channels[channel].m_log2Interp)
465  {
466  case 1:
467  m_channels[channel].m_interpolatorsIF.interpolate2_cen(&beginRead, buf, len*2);
468  break;
469  case 2:
470  m_channels[channel].m_interpolatorsIF.interpolate4_cen(&beginRead, buf, len*2);
471  break;
472  case 3:
473  m_channels[channel].m_interpolatorsIF.interpolate8_cen(&beginRead, buf, len*2);
474  break;
475  case 4:
476  m_channels[channel].m_interpolatorsIF.interpolate16_cen(&beginRead, buf, len*2);
477  break;
478  case 5:
479  m_channels[channel].m_interpolatorsIF.interpolate32_cen(&beginRead, buf, len*2);
480  break;
481  case 6:
482  m_channels[channel].m_interpolatorsIF.interpolate64_cen(&beginRead, buf, len*2);
483  break;
484  default:
485  break;
486  }
487  }
488  }
489  else
490  {
491  std::fill(buf, buf+2*len, 0.0f);
492  }
493 }
void interpolate16_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
unsigned int getLog2Interpolation(unsigned int channel) const
void callbackSO16(qint16 *buf, qint32 len, unsigned int channel=0)
void interpolate8_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void interpolate64_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
float getRWBalance() const
Interpolators< qint16, SDR_TX_SAMP_SZ, 12 > m_interpolators12
void interpolate4_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void interpolate64_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void interpolate32_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
QWaitCondition m_startWaiter
SoapySDR::Device * m_dev
void interpolate8_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
void callbackSOIF(float *buf, qint32 len, unsigned int channel=0)
int32_t i
Definition: decimators.h:244
InterpolatorType m_interpolatorType
void interpolate2_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
Interpolators< qint8, SDR_TX_SAMP_SZ, 8 > m_interpolators8
InterpolatorsIF< SDR_TX_SAMP_SZ, SDR_TX_SAMP_SZ > m_interpolatorsIF
void readAdvance(SampleVector::iterator &readUntil, unsigned int nbSamples)
SoapySDROutputThread(SoapySDR::Device *dev, unsigned int nbTxChannels, QObject *parent=0)
Interpolators< qint16, SDR_TX_SAMP_SZ, 16 > m_interpolators16
void setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
void interpolate2_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void interpolate32_cen(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void callbackMO(std::vector< void *> &buffs, qint32 samplesPerChannel)
void interpolate1(SampleVector::iterator *it, float *buf, qint32 len, bool invertIQ=false)
void interpolate1(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
void interpolate16_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
void interpolate4_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
void callbackSO8(qint8 *buf, qint32 len, unsigned int channel=0)
Channel * m_channels
Array of channels dynamically allocated for the given number of Tx channels.
SampleSourceFifo * getFifo(unsigned int channel)
void callbackSO12(qint16 *buf, qint32 len, unsigned int channel=0)
void setLog2Interpolation(unsigned int channel, unsigned int log2_interp)