libcyberradio 22.01.24
TXClient.cpp
1#include "LibCyberRadio/NDR651/TXClient.h"
2
3namespace 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}
Provides programming elements for controlling the CyberRadio Solutions NDR651 radio.
Defines functionality for LibCyberRadio applications.
Definition App.h:24