libcyberradio  22.01.24
TXClient.cpp
1 #include "LibCyberRadio/NDR651/TXClient.h"
2 
3 namespace LibCyberRadio
4 {
5  namespace NDR651
6  {
7  TXClient::TXClient(std::string radioHostName, bool debug):
8  Debuggable(debug, "TXClient"),
9  txInterfaceName(""), // Invalid
10  tenGbeIndex(INVALID_VALUE), // Invalid
11  ducChannel(INVALID_VALUE), // Invalid
12  ducAttenuation(0), // Optional
13  ducRateIndex(INVALID_VALUE), // Invalid
14  rfChannel(INVALID_VALUE), // Invalid
15  txUdpPort(0), // Invalid (use 0 to avoid overflow though)
16  ducFreq(0), // Optional
17  ducFullThreshPercent(0.90), // Optional
18  ducEmptyThreshPercent(0.82), // Optional
19  updatesPerSecond(20), // Optional (flowControlUpdatesPerSecond)
20  txFreq(INVALID_VALUE), // Invalid
21  txAttenuation(0), // Optional
22  txInversion(false),
23  radioHostName(radioHostName),
24  debugOn(debug),
25  // Internals
26  packetizer(NULL),
27  statusRX(NULL),
28  rc(NULL),
29  txSock(0),
30  isGrouped(false),
31  isRunning(false),
32  DUCPaused(true),
33  DUCReady(false),
34  prefillSampleCount(0L)
35  {
36  // Create a radio controller (sends cmds to 651)
37  this->rc = new RadioController(radioHostName, 8617, debug);
38  }
39 
40  TXClient::~TXClient()
41  {
42  if (this->rc != NULL)
43  {
44  delete this->rc;
45  }
46  if (this->statusRX != NULL)
47  {
48  delete this->statusRX;
49  }
50  if (this->packetizer != NULL)
51  {
52  delete this->packetizer;
53  }
54  }
55 
56  /********* PRIVATE METHODS **********/
57  std::string TXClient::getSourceMac()
58  {
59  if (this->packetizer != NULL)
60  {
61  // Source MAC Address
62  char macstring[32];
63  struct ifreq ifr;
64  memset(&ifr, 0x00, sizeof(ifr));
65  strcpy(ifr.ifr_name, this->txInterfaceName.c_str());
66  ioctl(this->packetizer->getSocketFd(), SIOCGIFHWADDR, &ifr);
67  sprintf(macstring, "%02x:%02x:%02x:%02x:%02x:%02x",
68  (unsigned char)ifr.ifr_hwaddr.sa_data[0],
69  (unsigned char)ifr.ifr_hwaddr.sa_data[1],
70  (unsigned char)ifr.ifr_hwaddr.sa_data[2],
71  (unsigned char)ifr.ifr_hwaddr.sa_data[3],
72  (unsigned char)ifr.ifr_hwaddr.sa_data[4],
73  (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
74  return std::string(macstring);
75  }
76  return NULL;
77  }
78 
79  std::string TXClient::getSourceIP()
80  {
81  if (this->packetizer != NULL)
82  {
83  struct ifreq ifr;
84  ifr.ifr_addr.sa_family = AF_INET;
85  strncpy(ifr.ifr_name, this->txInterfaceName.c_str(), IFNAMSIZ-1);
86  ioctl(this->packetizer->getSocketFd(), SIOCGIFADDR, &ifr);
87  return std::string(inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
88  }
89  return NULL;
90  }
91 
92  bool TXClient::validInputs(std::string &errors)
93  {
94  // I am not checking the validity of these inputs because the radio does that better (hopefully)
95  // I am only checking if the default values from the constructor were changed, which is enough to determine if called the config methods were called.
96  bool valid = true;
97  if (this->txInterfaceName.empty()) {
98  errors += "Tx Interface Name not configured\n";
99  valid = false;
100  }
101  if (this->tenGbeIndex == INVALID_VALUE) {
102  errors += "Ten GBE Index not configured.\n";
103  valid = false;
104  }
105  if (this->ducChannel == INVALID_VALUE) {
106  errors += "DUC Channel not configured.\n";
107  valid = false;
108  }
109  if (this->ducRateIndex == INVALID_VALUE) {
110  errors += "DUC Rate Index not configured.\n";
111  valid = false;
112  }
113  if (this->rfChannel == INVALID_VALUE) {
114  errors += "RF TX Channel not configured.\n";
115  valid = false;
116  }
117  if (this->txUdpPort == 0) {
118  errors += "TX UDP Port not configured.\n";
119  valid = false;
120  }
121  // if ((((int)(this->txFreq)) == INVALID_VALUE)) {
122  // errors += "TX Frequency not configured.\n";
123  // valid = false;
124  // }
125  return valid;
126  }
127 
128  /* Public Methods */
129 
130  /********* ACCESSORS **********/
131  void TXClient::setGrouped(bool isGrouped)
132  {
133  this->isGrouped = isGrouped;
134  }
135 
136  bool TXClient::isDUCPaused()
137  {
138  return this->DUCPaused;
139  }
140 
141  bool TXClient::setDUCPaused(bool paused)
142  {
143  std::cout << "setDUCPaused( " << this->DUCPaused << " -> " << paused << " )" << std::endl;
144  this->DUCPaused = paused;
145  bool ret = true;
146  return ret;
147  }
148 
149  bool TXClient::isDUCReady()
150  {
151  // std::cout << "isDUCReady() = " << this->DUCReady << std::endl;
152  return this->DUCReady;
153  }
154 
155  unsigned int TXClient::getDucChannel()
156  {
157  return this->ducChannel;
158  }
159 
160  // Public Config Functions
161  void TXClient::disableRF()
162  {
163  // TXP off
164  this->debug("[disableRF] Called\n");
165  this->rc->setTXP(this->rfChannel, false);
166  this->debug("[disableRF] Returning\n");
167  }
168 
169  bool TXClient::setDUCChannel(unsigned int ducChannel)
170  {
171  this->debug("[setDUCChannel] Called\n");
172  this->debug("[setDUCChannel] -- ducChannel = %u\n", ducChannel);
173  bool ret = false;
174  if (!this->isRunning)
175  {
176  this->ducChannel = ducChannel;
177  ret = true;
178  }
179  this->debug("[setDUCChannel] Returning %s\n", debugBool(ret) );
180  return ret;
181  }
182 
183  bool TXClient::setTxChannel(unsigned int txChannel)
184  {
185  bool ret = false;
186  this->debug("[setTxChannel] Called\n");
187  this->debug("[setTxChannel] -- txChannel = %u\n", txChannel);
188  if (!this->isRunning) {
189  this->rfChannel = txChannel;
190  ret = true;
191  }
192  this->debug("[setTxChannel] Returning %s\n", debugBool(ret) );
193  return ret;
194  }
195 
196  // Should be able to be called on the fly
197  bool TXClient::setDUCRateIndex(unsigned int ducRateIndex)
198  {
199  // if (!this->isRunning) {
200  // this->ducRateIndex = ducRateIndex;
201  // return true;
202  // }
203  // return false;
204  this->debug("[setDUCRateIndex] Called\n");
205  this->debug("[setDUCRateIndex] -- ducRateIndex = %u\n", ducRateIndex);
206  boost::mutex::scoped_lock lock(this->objectAccessMutex);
207  bool ret = this->setDUCRateIndexUnlocked(ducRateIndex);
208  this->debug("[setDUCRateIndex] Returning %s\n", debugBool(ret) );
209  return ret;
210  }
211 
212  // Should be able to be called on the fly
213  bool TXClient::setDUCFreq(double ducFreq)
214  {
215  this->debug("[setDUCFreq] Called\n");
216  this->debug("[setDUCFreq] -- ducFreq = %f\n", ducFreq);
217  boost::mutex::scoped_lock lock(this->objectAccessMutex);
218  bool result = false;
219  if (this->ducChannel != INVALID_VALUE)
220  {
221  result = this->rc->setDUCF(this->ducChannel, ducFreq);
222  if (result == true)
223  {
224  this->ducFreq = ducFreq;
225  }
226  }
227  else
228  {
229  // Not configured yet
230  this->debug("[setDUCFreq] Not configured yet!\n");
231  }
232  this->debug("[setDUCFreq] Returning %s\n", debugBool(result) );
233  return result;
234  }
235 
236  // Can be called on the fly
237  bool TXClient::setDUCAtten(double ducAttenuation)
238  {
239  this->debug("[setDUCAtten] Called\n");
240  this->debug("[setDUCAtten] -- ducAttenuation = %f\n", ducAttenuation);
241  boost::mutex::scoped_lock lock(this->objectAccessMutex);
242  bool result = false;
243  if (this->ducChannel != INVALID_VALUE)
244  {
245  result = this->rc->setDUCA(this->ducChannel, ducAttenuation);
246  if (result == true)
247  {
248  this->ducAttenuation = ducAttenuation;
249  }
250  }
251  else
252  {
253  // Not configured yet
254  this->debug("[setDUCAtten] Not configured yet!\n");
255  }
256  this->debug("[setDUCAtten] Returning %s\n", debugBool(result) );
257  return result;
258  }
259 
260  bool TXClient::setDUCParameters(unsigned int ducIndex, unsigned int ducRateIndex, unsigned int txChannel)
261  {
262  this->debug("[setDUCParameters] Called\n");
263  this->debug("[setDUCParameters] -- ducIndex = %u\n", ducIndex);
264  this->debug("[setDUCParameters] -- ducRateIndex = %u\n", ducRateIndex);
265  this->debug("[setDUCParameters] -- txChannel = %u\n", txChannel);
266  boost::mutex::scoped_lock lock(this->objectAccessMutex);
267  //bool success = true;
268  //success = success && this->setDUCChannel(ducIndex);
269  //success = success && this->setDUCRateIndex(ducRateIndex);
270  //success = success && this->setTxChannel(txChannel);
271  bool success = this->setDUCChannel(ducIndex) &&
272  this->setDUCRateIndexUnlocked(ducRateIndex) &&
273  this->setTxChannel(txChannel);
274  this->debug("[setDUCParameters] Returning %s\n", debugBool(success) );
275  return success;
276  }
277 
278  // Can be called on the fly
279  bool TXClient::setTxAtten(double txAttenuation)
280  {
281  this->debug("[setTxAtten] Called\n");
282  this->debug("[setTxAtten] -- txAttenuation = %f\n", txAttenuation);
283  bool result = false;
284  if (this->ducChannel != INVALID_VALUE)
285  {
286  result = this->rc->setTXA(this->rfChannel, txAttenuation);
287  if (result == true)
288  {
289  // Because TX attenuation doesn't have a well-defined behavior in
290  // the ICD -- particularly in regard to allowed resolution -- play
291  // it safe by querying what attenuation value the radio actually set.
292  this->txAttenuation = this->rc->getTXA(this->rfChannel);
293  }
294  }
295  this->debug("[setTxAtten] Returning %s\n", debugBool(result) );
296  return result;
297  }
298 
299  // Can be called on the fly
300  bool TXClient::setTxFreq(double txFreq)
301  {
302  this->debug("[setTxFreq] Called\n");
303  this->debug("[setTxFreq] -- txFreq = %f\n", txFreq);
304  bool result = false;
305  if (this->rfChannel != INVALID_VALUE)
306  {
307  result = this->rc->setTXF(this->rfChannel, txFreq);
308  if (result == true) {
309  this->txFreq = txFreq;
310  }
311  }
312  this->debug("[setTxFreq] Returning %s\n", debugBool(result) );
313  return result;
314  }
315 
316  // Can be called on the fly
317  bool TXClient::setTxInversion(bool txInversion)
318  {
319  if (this->ducChannel == INVALID_VALUE) return false; // Not configured yet
320  bool result = this->rc->setTXINV(this->ducChannel, txInversion);
321  if (result == true) {
322  this->txInversion = txInversion;
323  }
324  return result;
325  }
326 
327  bool TXClient::setEthernetInterface(unsigned int tenGbeIndex, const std::string &txInterfaceName, unsigned short port)
328  {
329  if (!this->isRunning) {
330  this->tenGbeIndex = tenGbeIndex;
331  this->txInterfaceName = std::string(txInterfaceName);
332  this->txUdpPort = port;
333  return true;
334  }
335  return false;
336  }
337 
338  /********* CONTROL FUNCTIONS **********/
339  void TXClient::start()
340  {
341  this->debug("[start] Called\n");
342  if (this->isRunning)
343  {
344  // Stop any previous configurations
345  this->stop();
346  }
347  std::string errors;
348  if (!this->validInputs(errors)) // Don't run if something wasn't configured
349  {
350  std::cerr << "ERRORS CONFIGURING TX CLIENT: " << errors << std::endl;
351  throw std::runtime_error(errors);
352  }
353 
354  this->isRunning = true;
355  this->debug("[start] Starting Transmit Client\n");
356 
357  // Prefill 50% of DUC buffer before enabling DUC
358  this->prefillSampleCount = ((unsigned int )(0.50 * 67108860)) - ((unsigned int )(0.50 * 67108860)%4);
359 
360  // Create a Packetizer (wraps samples in Vita Frames and sends them)
361  this->packetizer = new Packetizer(this->txInterfaceName, this->txUdpPort, this->ducRateIndex, this->debugOn);
362  this->packetizer->start(); // Inits the TX socket, prepares vita headers
363 
364  // Create the status receiver for radio flow control notifications
365  this->statusRX = new StatusReceiver(this->txInterfaceName, UDP_STATUS_BASE + this->ducChannel, this->debugOn, false);
366  std::ostringstream statusRxName;
367  statusRxName << "StatusRx" << this->txUdpPort;
368  this->statusRX->setName(statusRxName.str());
369 
370  // Enable TX Channel Power (setTXP checks if it is already enabled, and that seems to avoid some timing issues)
371  this->debug("[start] Enabling TX\n");
372  this->rc->setTXP(
373  this->rfChannel,
374  true
375  );
376 
377  // Configure DUC
378  this->debug("[start] Configuring DUC\n");
379  this->rc->setDUC(
380  this->ducChannel,
381  this->tenGbeIndex,
382  this->ducFreq,
383  this->ducAttenuation,
384  this->ducRateIndex,
385  this->rfChannel, // Note 3 is TX on RF1 and RF2
386  this->DUCPaused ? 2 : 0, // Mode 2 is pause mode. We decided to always prefill the DUC
387  this->txUdpPort // Stream ID, which is same as txUdpPort
388  );
389  this->debug("[start] Configuring DUC complete\n");
390 
391  // Configure DUC HS (Flow control configuration)
392  this->rc->setDUCHSPercent(
393  this->ducChannel,
394  this->tenGbeIndex,
395  this->ducFullThreshPercent, // Full thresh percent
396  this->ducEmptyThreshPercent, // Empty thresh percent
397  this->updatesPerSecond, // Notifcations per second
398  this->txUdpPort
399  );
400 
401  // Configure DIP for DUCHS
402  this->rc->setDIP(
403  this->ducChannel,
404  this->tenGbeIndex,
405  this->getSourceIP(),
406  this->getSourceMac(),
407  this->statusRX->getUdpPort()
408  );
409 
410  // Start listening for flow control from the radio.
411  this->statusRX->start();
412  this->debug("[start] Returning");
413  }
414 
415  void TXClient::stop(bool disableRF)
416  {
417  if (this->isRunning)
418  {
419  this->debug("Stopping Transmit Client\n");
420  this->statusRX->interrupt();
421  this->rc->clearDUC(this->ducChannel);
422  this->rc->clearDUCHS(this->ducChannel);
423  this->isRunning = false;
424  if (disableRF) this->disableRF();
425  }
426 
427  // Delete the packetizer, which will be re-init at next call to start()
428  if (this->packetizer != NULL)
429  {
430  delete this->packetizer;
431  this->packetizer = NULL;
432  }
433 
434  // Delete the status receiver, which will be re-init at next call to start()
435  if (this->statusRX != NULL)
436  {
437  delete this->statusRX;
438  this->statusRX = NULL;
439  }
440  }
441 
442  void TXClient::sendFrame(short * samples, unsigned int samplesPerFrame)
443  {
444 
445  if (this->isRunning)
446  {
447  if (this->prefillSampleCount > 0)
448  {
449  // Send more samples to prefill the buffer
450  this->packetizer->sendFrame(samples);
451  this->prefillSampleCount -= samplesPerFrame;
452  if ((this->prefillSampleCount <= 0)) // We have sent enough samples to prefill the buffer
453  {
454  // this->DUCPaused = false;
455  this->DUCReady = true;
456  if (!this->isGrouped) // If we are in a group, DUCGE will be called instead
457  {
458  this->DUCPaused = false;
459  // We have sent the prefill sample amount
460  this->rc->setDUC(
461  this->ducChannel,
462  this->tenGbeIndex,
463  this->ducFreq,
464  this->ducAttenuation,
465  this->ducRateIndex,
466  this->rfChannel, // Note 3 is TX on RF1 and RF2
467  this->DUCPaused ? 2 : 0, // Mode 0 unpauses the DUC
468  this->txUdpPort // Stream ID, which is same as txUdpPort
469  );
470  }
471  }
472  }
473  else // DUC is prefilled, we can use flow control
474  {
475  // Wait until the radio says it can receive more frames
476  this->statusRX->blockUntilAvailable(samplesPerFrame);
477 
478  // The radio is ready, send the frame
479  this->packetizer->sendFrame(samples);
480 
481  // Tell the Status Receiver we have sent frames
482  this->statusRX->sentNSamples(samplesPerFrame);
483  }
484  }
485  else
486  {
487  this->debug("WARNING: Called send frame without calling start()\n");
488  }
489  }
490 
491  bool TXClient::pauseDUC(bool paused)
492  {
493  this->debug("[pauseDUC] Called\n");
494  this->debug("[pauseDUC] -- paused = %s\n", this->debugBool(paused));
495  bool ret = false;
496  boost::mutex::scoped_lock lock(this->objectAccessMutex);
497  this->DUCPaused = paused;
498  if (this->ducChannel != INVALID_VALUE)
499  {
500  ret = this->rc->setDUCP(this->ducChannel, this->DUCPaused);
501  }
502  return ret;
503  }
504 
505  // "Unlocked" version -- This version is designed to be called from
506  // setDucParameters(), which handles mutex locking, so it doesn't have
507  // a lock of its own.
508  bool TXClient::setDUCRateIndexUnlocked(unsigned int ducRateIndex)
509  {
510  // if (!this->isRunning) {
511  // this->ducRateIndex = ducRateIndex;
512  // return true;
513  // }
514  // return false;
515  this->debug("[setDUCRateIndexUnlocked] Called\n");
516  this->debug("[setDUCRateIndexUnlocked] -- ducRateIndex = %u\n", ducRateIndex);
517  bool result = true;
518  if (this->ducChannel != INVALID_VALUE)
519  {
520  // Send the DUC command ONLY if we've already configured this
521  if ( this->ducRateIndex != INVALID_VALUE )
522  {
523  this->debug("[setDUCRateIndexUnlocked] Sending radio command\n");
524  result = this->rc->setDUC(
525  /* unsigned ducChannel */ this->ducChannel,
526  /* unsigned int tenGbeIndex */ this->tenGbeIndex,
527  /* double ducFreq */ this->ducFreq,
528  /* double attenuation */ this->ducAttenuation,
529  /* unsigned int ducRateIndex */ ducRateIndex,
530  /* unsigned int rfTxChannel */ this->rfChannel,
531  /* unsigned int mode */ this->DUCPaused ? 2 : 0,
532  /* unsigned int streamID */ this->txUdpPort);
533  }
534  if (result == true)
535  {
536  this->debug("[setDUCRateIndexUnlocked] Skipping radio command\n");
537  this->ducRateIndex = ducRateIndex;
538  }
539  }
540  else
541  {
542  // Not configured yet
543  this->debug("[setDUCRateIndexUnlocked] Not configured yet!\n");
544  }
545  this->debug("[setDUCRateIndexUnlocked] Returning %s\n", debugBool(result) );
546  return result;
547  }
548 
549  }
550 }
Defines functionality for LibCyberRadio applications.
Definition: App.h:23