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.
xtrxoutputthread.cpp
Go to the documentation of this file.
1 // Copyright (C) 2019 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #include <algorithm>
19 #include <chrono>
20 #include <thread>
21 
22 #include "xtrx/devicextrx.h"
23 #include "dsp/samplesourcefifo.h"
24 #include "xtrxoutputthread.h"
25 
26 
27 XTRXOutputThread::XTRXOutputThread(struct xtrx_dev *dev, unsigned int nbChannels, unsigned int uniqueChannelIndex, QObject* parent) :
28  QThread(parent),
29  m_running(false),
30  m_dev(dev),
31  m_nbChannels(nbChannels),
32  m_uniqueChannelIndex(uniqueChannelIndex)
33 {
34  qDebug("XTRXOutputThread::XTRXOutputThread: nbChannels: %u uniqueChannelIndex: %u", nbChannels, uniqueChannelIndex);
35  m_channels = new Channel[2];
36 }
37 
39 {
40  qDebug("XTRXOutputThread::~XTRXOutputThread");
41 
42  if (m_running) {
43  stopWork();
44  }
45 
46  delete[] m_channels;
47 }
48 
50 {
51  m_startWaitMutex.lock();
52  start();
53 
54  while(!m_running) {
55  m_startWaiter.wait(&m_startWaitMutex, 100);
56  }
57 
58  m_startWaitMutex.unlock();
59 }
60 
62 {
63  m_running = false;
64  wait();
65 }
66 
68 {
69  unsigned int fifoCount = 0;
70 
71  for (unsigned int i = 0; i < 2; i++)
72  {
73  if (m_channels[i].m_sampleFifo) {
74  fifoCount++;
75  }
76  }
77 
78  return fifoCount;
79 }
80 
81 void XTRXOutputThread::setLog2Interpolation(unsigned int channel, unsigned int log2_interp)
82 {
83  if (channel < 2) {
84  m_channels[channel].m_log2Interp = log2_interp;
85  }
86 }
87 
88 unsigned int XTRXOutputThread::getLog2Interpolation(unsigned int channel) const
89 {
90  if (channel < 2) {
91  return m_channels[channel].m_log2Interp;
92  } else {
93  return 0;
94  }
95 }
96 
97 void XTRXOutputThread::setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
98 {
99  if (channel < 2) {
100  m_channels[channel].m_sampleFifo = sampleFifo;
101  }
102 }
103 
105 {
106  if (channel < 2) {
107  return m_channels[channel].m_sampleFifo;
108  } else {
109  return 0;
110  }
111 }
112 
114 {
115  int res;
116 
117  m_running = true;
118  m_startWaiter.wakeAll();
119 
120  unsigned int nbFifos = getNbFifos();
121 
122  if ((m_nbChannels != 0) && (nbFifos != 0))
123  {
124  xtrx_run_params params;
125  xtrx_run_params_init(&params);
126 
127  params.dir = XTRX_TX;
128  params.tx_repeat_buf = 0;
129  params.tx.paketsize = 2*DeviceXTRX::blockSize;
130  params.tx.chs = XTRX_CH_AB;
131  params.tx.wfmt = XTRX_WF_16;
132  params.tx.hfmt = XTRX_IQ_INT16;
133  params.tx.flags |= XTRX_RSP_SWAP_IQ;
134 
135  if (m_nbChannels == 1)
136  {
137  qDebug("XTRXOutputThread::run: SO mode for channel #%u", m_uniqueChannelIndex);
138  params.tx.flags |= XTRX_RSP_SISO_MODE;
139 
140  if (m_uniqueChannelIndex == 1) {
141  params.tx.flags |= XTRX_RSP_SWAP_AB;
142  }
143  }
144 
145  res = xtrx_run_ex(m_dev, &params);
146 
147  if (res != 0)
148  {
149  qCritical("XTRXOutputThread::run: could not start stream err:%d", res);
150  m_running = false;
151  }
152  else
153  {
154  std::this_thread::sleep_for(std::chrono::milliseconds(50));
155  qDebug("XTRXOutputThread::run: stream started");
156  }
157 
158  const unsigned int elemSize = 4; // XTRX uses 4 byte I+Q samples
159  std::vector<std::vector<char>> buffMem(m_nbChannels, std::vector<char>(elemSize*DeviceXTRX::blockSize));
160  std::vector<void *> buffs(m_nbChannels);
161  master_ts ts = 4096*1024;
162 
163  for (std::size_t i = 0; i < m_nbChannels; i++) {
164  buffs[i] = buffMem[i].data();
165  }
166 
167  xtrx_send_ex_info_t nfo;
168  nfo.samples = DeviceXTRX::blockSize;
169  nfo.buffer_count = m_nbChannels;
170  nfo.buffers = (void* const*) buffs.data();
171  nfo.flags = XTRX_TX_DONT_BUFFER; // | XTRX_TX_SEND_ZEROS;
172  nfo.timeout = 0;
173  nfo.out_txlatets = 0;
174  nfo.ts = ts;
175 
176  while (m_running)
177  {
178 // if (m_nbChannels > 1) {
179 // callbackMO((qint16*) buffs[0], (qint16*) buffs[1], nfo.samples);
180 // } else {
181 // callbackSO((qint16*) buffs[0], nfo.samples);
182 // }
183 
184  callbackSO((qint16*) buffs[0], nfo.samples);
185  res = xtrx_send_sync_ex(m_dev, &nfo);
186 
187  if (res < 0)
188  {
189  qCritical("XTRXOutputThread::run send error: %d", res);
190  qDebug("XTRXOutputThread::run: out_samples: %u out_flags: %u", nfo.out_samples, nfo.out_flags);
191  break;
192  }
193 
194  if (nfo.out_flags & XTRX_TX_DISCARDED_TO) {
195  qDebug("XTRXOutputThread::run: underrun");
196  }
197 
198  if (nfo.out_txlatets) {
199  qDebug("XTRXOutputThread::run: out_txlatets: %lu", nfo.out_txlatets);
200  }
201 
202  nfo.ts += DeviceXTRX::blockSize;
203  }
204 
205  res = xtrx_stop(m_dev, XTRX_TX);
206 
207  if (res != 0)
208  {
209  qCritical("XTRXOutputThread::run: could not stop stream");
210  }
211  else
212  {
213  std::this_thread::sleep_for(std::chrono::milliseconds(50));
214  qDebug("XTRXOutputThread::run: stream stopped");
215  }
216  }
217  else
218  {
219  qWarning("XTRXOutputThread::run: no channels or FIFO allocated. Aborting");
220  }
221 
222  m_running = false;
223 }
224 
225 void XTRXOutputThread::callback(qint16* buf, qint32 len)
226 {
227  if (m_channels[m_uniqueChannelIndex].m_sampleFifo)
228  {
230 
231  if (bal < -0.25) {
232  qDebug("XTRXOutputThread::callbackSO: read lags: %f", bal);
233  } else if (bal > 0.25) {
234  qDebug("XTRXOutputThread::callbackSO: read leads: %f", bal);
235  }
236 
237  SampleVector::iterator beginRead;
239  beginRead -= len;
240 
241  if (m_channels[m_uniqueChannelIndex].m_log2Interp == 0)
242  {
244  }
245  else
246  {
247  switch (m_channels[m_uniqueChannelIndex].m_log2Interp)
248  {
249  case 1:
251  break;
252  case 2:
254  break;
255  case 3:
257  break;
258  case 4:
260  break;
261  case 5:
263  break;
264  case 6:
266  break;
267  default:
268  break;
269  }
270  }
271  }
272  else
273  {
274  std::fill(buf, buf+2*len, 0);
275  }
276 }
277 
278 void XTRXOutputThread::callbackSO(qint16* buf, qint32 len)
279 {
280  if (m_channels[m_uniqueChannelIndex].m_sampleFifo)
281  {
283 
284  if (bal < -0.25) {
285  qDebug("XTRXOutputThread::callbackSO: read lags: %f", bal);
286  } else if (bal > 0.25) {
287  qDebug("XTRXOutputThread::callbackSO: read leads: %f", bal);
288  }
289 
290  SampleVector::iterator beginRead;
292  beginRead -= len;
293 
294  if (m_channels[m_uniqueChannelIndex].m_log2Interp == 0)
295  {
297  }
298  else
299  {
300  switch (m_channels[m_uniqueChannelIndex].m_log2Interp)
301  {
302  case 1:
304  break;
305  case 2:
307  break;
308  case 3:
310  break;
311  case 4:
313  break;
314  case 5:
316  break;
317  case 6:
319  break;
320  default:
321  break;
322  }
323  }
324  }
325  else
326  {
327  std::fill(buf, buf+2*len, 0);
328  }
329 }
330 
331 void XTRXOutputThread::callbackMO(qint16* buf0, qint16* buf1, qint32 len)
332 {
333  unsigned int uniqueChannelIndex = m_uniqueChannelIndex;
334 
335  // channel 0
337  callbackSO(buf0, len);
338  // channel 1
340  callbackSO(buf1, len);
341 
342  m_uniqueChannelIndex = uniqueChannelIndex;
343 }
unsigned int getLog2Interpolation(unsigned int channel) const
void interpolate64_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
virtual void stopWork()
float getRWBalance() const
void callbackSO(qint16 *buf, qint32 len)
void interpolate32_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
unsigned int m_nbChannels
Channel * m_channels
Array of channels dynamically allocated for the given number of Rx channels.
void interpolate8_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
void callback(qint16 *buf, qint32 len)
int32_t i
Definition: decimators.h:244
void interpolate2_cen(SampleVector::iterator *it, T *buf, qint32 len, bool invertIQ=false)
SampleSourceFifo * getFifo(unsigned int channel)
void setFifo(unsigned int channel, SampleSourceFifo *sampleFifo)
void readAdvance(SampleVector::iterator &readUntil, unsigned int nbSamples)
struct xtrx_dev * m_dev
XTRXOutputThread(struct xtrx_dev *dev, unsigned int nbChannels, unsigned int uniqueChannelIndex=0, QObject *parent=0)
SampleSourceFifo * m_sampleFifo
static const unsigned int blockSize
Definition: devicextrx.h:44
Interpolators< qint16, SDR_TX_SAMP_SZ, 12 > m_interpolators
unsigned int getNbFifos()
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)
virtual void startWork()
unsigned int m_uniqueChannelIndex
void callbackMO(qint16 *buf0, qint16 *buf1, qint32 len)
void setLog2Interpolation(unsigned int channel, unsigned int log2_interp)
QWaitCondition m_startWaiter