PicoScenes API Docs
 
Loading...
Searching...
No Matches
AbstractSDRFrontEnd.hxx
Go to the documentation of this file.
1//
2// Created by Zhiping Jiang on 5/2/21.
3//
4
5#ifndef PICOSCENES_PLATFORM_ABSTRACTSDRFRONTEND_HXX
6#define PICOSCENES_PLATFORM_ABSTRACTSDRFRONTEND_HXX
7
8#include <deque>
9#include "BoostHeaders.hxx"
10#include "Intrinsics.hxx"
11#include "SystemTools.hxx"
12#include "AbstractFrontEnd.hxx"
14#include "BBSignalsFileWriter.hxx"
15#include "LicenseService.hxx"
17
18#ifdef interface
19#undef interface
20#endif
21
26public:
35
42 virtual CS16Vector generateSignal(const ModularPicoScenesTxFrame& frame, uint8_t numTxAntenna) = 0;
43
48 virtual std::vector<CS16Vector> generateMultiChannelSignals(const ModularPicoScenesTxFrame& frame, const uint8_t numTxAntenna) {
49 auto signals = generateSignal(frame, numTxAntenna);
50 auto numSamples = static_cast<int64_t>(signals.size()) / numTxAntenna;
51
52 std::vector multiChannelSignals(numTxAntenna, CS16Vector(numSamples));
53 for (auto channelIndex = 0; channelIndex < numTxAntenna; channelIndex++) {
54 Intrinsics::fastCopyN(signals.cbegin() + numSamples * channelIndex, numSamples, multiChannelSignals[channelIndex].begin());
55 }
56
57 return multiChannelSignals;
58 }
59
69 virtual void prebuildSignals(ModularPicoScenesTxFrame& frame, const uint8_t numTxAntenna) {
70 frame.prebuiltSignals = std::move(generateMultiChannelSignals(frame, numTxAntenna));
71 }
72
80 virtual int transmitSignalsFromFile(const std::string& signalSourceFile, uint64_t repeat) = 0;
81
82 virtual int32_t transmitSignals(const std::vector<const void*>& signals, int64_t bufferLength, double postfixDuration) = 0;
83
84 virtual int transmitSignalsContinuously(const std::vector<const void*>& multiChannelHeaders, int64_t numSignalsPerAntenna, bool burstStart, bool burstEnd) = 0;
85
95 int transmit(const ModularPicoScenesTxFrame& frame) override {
96 static std::shared_ptr<BBSignalsFileWriter<std::complex<int16_t>>> signalWriter;
98 startTx();
99
100 if (txSignalDumpFilePath && !signalWriter) {
102 signalWriter = std::make_shared<BBSignalsFileWriter<std::complex<int16_t>>>(*txSignalDumpFilePath);
103 }
104
105 auto numTxChannels = userSpecifiedTxChannels().size();
106 size_t txResult{};
107 std::vector<CS16Vector> inplaceGeneratedSignals{};
108 if (frame.prebuiltSignals.empty()) {
109 inplaceGeneratedSignals = std::move(generateMultiChannelSignals(frame, numTxChannels));
110 txResult = transmitSignal(inplaceGeneratedSignals, frame.txParameters.postfixPaddingTime);
111 } else {
112 txResult = transmitSignal(frame.prebuiltSignals, frame.txParameters.postfixPaddingTime);
113 }
114 const auto& ref2Signals = !frame.prebuiltSignals.empty() ? frame.prebuiltSignals : inplaceGeneratedSignals;
115
116 // TODO to fix
117 // if (signalWriter)
118 // signalWriter->write2DStream(ref2Signals);
119
120 if (!txResult) {
121 LoggingService_SDR_debug_printf("<%s>(%s)-->%s | PPDU: %u x %u (%.3f us)", getReferredInterfaceName(), DeviceSubtype2String(getFrontEndSubtype()), frame.toString(), ref2Signals[0].size(), ref2Signals.size(), 1e6f * ref2Signals[0].size() / getTxSamplingRate());
122 } else {
123 LoggingService_SDR_debug_printf("<%s>(%s) tx failure. | PPDU: %u x %u (%.3f us)", getReferredInterfaceName(), DeviceSubtype2String(getFrontEndSubtype()), ref2Signals[0].size(), ref2Signals.size(), 1e6f * ref2Signals[0].size() / getTxSamplingRate());
124 }
125
126 return 0;
127 }
128
129 virtual size_t transmitSignal(const CS16Vector& signals, const uint8_t numTxChannels, const double postfixDuration) {
130 int64_t numSignalsPerAntenna = signals.size() / numTxChannels;
131
132 auto signalHeaders = std::vector<const void*>(numTxChannels);
133 for (auto channelIndex = 0; channelIndex < numTxChannels; channelIndex++) {
134 signalHeaders[channelIndex] = signals.data() + channelIndex * numSignalsPerAntenna;
135 }
136
137 return transmitSignals(signalHeaders, numSignalsPerAntenna, postfixDuration);
138 }
139
140 virtual size_t transmitSignal(const std::vector<CS16Vector>& multiChannelSignals, const double postfixDuration) {
141 int64_t numSignalsPerAntenna = multiChannelSignals[0].size();
142 uint8_t numTxChannels = multiChannelSignals.size();
143
144 auto signalHeaders = std::vector<const void*>(numTxChannels);
145 for (auto channelIndex = 0; channelIndex < numTxChannels; channelIndex++) {
146 signalHeaders[channelIndex] = multiChannelSignals[channelIndex].data();
147 }
148
149 return transmitSignals(signalHeaders, numSignalsPerAntenna, postfixDuration);
150 }
151
152 virtual int32_t transmitSignalInBatchedSingleBurst(const std::vector<std::vector<const void*>>& multipleSignals, const std::vector<int64_t>& signalLength, const std::vector<double>& postfixDurations, uint32_t repeat) = 0;
153
154 int transmitFramesInBatch(const std::vector<const ModularPicoScenesTxFrame*>& frames, const uint32_t repeat) override {
155 static std::shared_ptr<BBSignalsFileWriter<std::complex<int16_t>>> signalWriter;
156 if (!txServiceStarted)
157 startTx();
158
159 if (txSignalDumpFilePath && !signalWriter) {
161 signalWriter = std::make_shared<BBSignalsFileWriter<std::complex<int16_t>>>(*txSignalDumpFilePath);
162 }
163
164 std::vector<std::vector<CS16Vector>> frameSignals;
165 std::vector<std::vector<const void*>> frameSignalHeads;
166 std::vector<int64_t> framePPDULengths;
167 std::vector<double> framePostfixDurations;
168 for (const auto* frame: frames) {
169 if (!frame->prebuiltSignals.empty()) {
170 std::vector<const void*> signalHeaders;
171 signalHeaders.reserve(frame->prebuiltSignals.size());
172 for (const auto& column: frame->prebuiltSignals)
173 signalHeaders.emplace_back(column.data());
174 frameSignalHeads.emplace_back(signalHeaders);
175 framePPDULengths.emplace_back(frame->prebuiltSignals.at(0).size());
176 framePostfixDurations.emplace_back(frame->txParameters.postfixPaddingTime);
177 } else {
178 frameSignals.emplace_back(generateMultiChannelSignals(*frame, userSpecifiedTxChannels().size()));
179 std::vector<const void*> signalHeaders;
180 for (const auto& column: frameSignals.back())
181 signalHeaders.emplace_back(column.data());
182 frameSignalHeads.emplace_back(signalHeaders);
183 framePPDULengths.emplace_back(frameSignals.back().at(0).size());
184 framePostfixDurations.emplace_back(frame->txParameters.postfixPaddingTime);
185 }
186 }
187
188 const auto txResult = transmitSignalInBatchedSingleBurst(frameSignalHeads, framePPDULengths, framePostfixDurations, repeat);
189
190 auto totalTransmitSamples = [&] {
191 size_t total{0};
192 for (auto i = 0; i < framePPDULengths.size(); i++) {
193 total += framePPDULengths[i] + static_cast<int64_t>(std::round(framePostfixDurations[i] * getTxSamplingRate()));
194 }
195 total *= repeat;
196 return total;
197 }();
198 [[maybe_unused]] auto latestResult = getLastTxStatus();
199
200 // TODO temporarily disable this field
201 // if (signalWriter) {
202 // for (auto r = 0; r < repeat; r++) {
203 // for (auto i = 0; i < framePPDULengths.size(); i++) {
204 // signalWriter->write2DStream(frameSignalHeads[i], framePPDULengths[i]);
205 // size_t zeroLength = static_cast<int64_t>(std::round(framePostfixDurations[i] * getTxSamplingRate()));
206 // signalWriter->writeZerosTo2DStream(zeroLength);
207 // }
208 // }
209 // }
210
211 if (!txResult) {
212 LoggingService_SDR_debug_printf("%s(%s) batch-Tx %u frames w/ %u repeat. Total Tx: %u samples (%.3f us) | 1st frame -->%s | PPDU: %u (%.3f us)", getReferredInterfaceName(), DeviceType2String(getFrontEndType()), frames.size(), repeat, totalTransmitSamples, 1e6f * totalTransmitSamples / getTxSamplingRate(), frames[0]->toString(), framePPDULengths[0], 1e6f * framePPDULengths[0] / getTxSamplingRate());
213 } else {
214 LoggingService_SDR_warning_printf("%s(%s) FAILED to batch-Tx %u frames w/ %u repeats. Total Tx: %u samples (%.3f us) | 1st frame -->%s | PPDU: %u (%.3f us)", getReferredInterfaceName(), DeviceType2String(getFrontEndType()), frames.size(), repeat, totalTransmitSamples, 1e6f * totalTransmitSamples / getTxSamplingRate(), frames[0]->toString(), framePPDULengths[0], 1e6f * framePPDULengths[0] / getTxSamplingRate());
215 }
216
217 return txResult;
218 }
219
223 template<int repeat = 1, typename... Frames, typename = std::enable_if_t<(std::is_same_v<std::remove_cvref_t<Frames>, ModularPicoScenesTxFrame*> && ...) || (std::is_same_v<std::remove_cvref_t<Frames>, ModularPicoScenesTxFrame> && ...) || ((std::is_same_v<std::remove_cvref_t<Frames>, std::shared_ptr<ModularPicoScenesTxFrame>> || std::is_same_v<std::remove_cvref_t<Frames>, std::unique_ptr<ModularPicoScenesTxFrame>>) && ...)>>
224 [[maybe_unused]] int transmitFramesInBatch(Frames&&... frames) {
225 return AbstractFrontEnd::transmitFramesInBatch<repeat>(std::forward<Frames>(frames)...);
226 }
227
228 // int transmitTest(const ModularPicoScenesTxFrame &frame1, const ModularPicoScenesTxFrame &frame2) {
229 // ModularPicoScenesTxFrame t1, t2, t3;
230 // auto t4 = std::make_shared<ModularPicoScenesTxFrame>();
231 // auto t5 = std::make_shared<ModularPicoScenesTxFrame>();
232 // auto t6 = std::make_unique<ModularPicoScenesTxFrame>();
233 // auto t7 = std::make_unique<ModularPicoScenesTxFrame>();
234 // transmitFramesInBatch(t4, t6);
235 // transmitFramesInBatch(t6, t7);
236 // transmitFramesInBatch(t1, t2, t3);
237 // return transmitFramesInBatch(frame1, frame2);
238 // }
239
240 virtual std::optional<double> getCurrentNoiseFloor() = 0;
241
242 [[nodiscard]] virtual SDRFrontEndTxStatus getLastTxStatus() const = 0;
243
244 virtual void clearTxStatus() = 0;
245
246 void setTxSignalDumpFilePath(const std::string& txSignalDumpFilePathV) {
247 AbstractSDRFrontEnd::txSignalDumpFilePath = txSignalDumpFilePathV;
248 }
249
250 void setRxSignalDumpFilePath(const std::string& rxSignalDumpFilePathV) {
251 rxSignalDumpFilePath = rxSignalDumpFilePathV;
252 }
253
254 void setRxSignalDumpDelayedStartDuration(const double delayedStartDuration) {
255 rxSignalDumpDelayedStartDuration = delayedStartDuration;
256 }
257
258 void setSignalReplayFilePath(const std::string& signalReplayFilePath) {
259 rxSignalReplayFilePath = signalReplayFilePath;
260 }
261
262 void setTxSignalReplayDelayMs(const std::optional<uint32_t>& txSignalReplayDelayMs) {
263 txSignalReplayDelay_ms = txSignalReplayDelayMs;
264 }
265
266 ChannelBandwidthEnum getRxChannelBandwidthMode() override {
267 return rxCBW;
268 }
269
270 void setRxChannelBandwidthMode(ChannelBandwidthEnum rxCbw) override {
271 rxCBW = rxCbw;
272 LoggingService_SDR_info_print("<{}> sets RX channel bandwidth (CBW) to [{}].", getReferredInterfaceName(), ChannelBandwidth2String(getRxChannelBandwidthMode()));
273 }
274
275 void setTxIQMismatch(double iq_ratio_db, double iq_crosstalk_degree) override {
276 getUserSpecifiedTxParameters().txIQAmplitudeImbalance_dB = iq_ratio_db;
277 getUserSpecifiedTxParameters().txIQPhaseImbalance_rad = iq_crosstalk_degree / 360 * M_PI;
278 LoggingService_SDR_info_print("<{}> sets Tx IQ mismatch ratio (DB) to [{}] and IQ crosstalk degree to [{}].", getReferredInterfaceName(), getTxIqRatioDB(), getTxIqCrossTalkDegree());
279 }
280
281 void setRxIQMismatch(double iq_ratio_db, double iq_crosstalk_degree) override {
282 rxIQMismatchRatioDb = iq_ratio_db;
283 rxIQMismatchRatioDegree = iq_crosstalk_degree;
284 LoggingService_SDR_info_print("<{}> sets Rx IQ mismatch ratio (DB) to [{}] and IQ crosstalk degree to [{}].", getReferredInterfaceName(), getRxIqRatioDB(), getRxIqCrossTalkDegree());
285 }
286
287 double getTxIqRatioDB() override {
288 return getUserSpecifiedTxParameters().txIQAmplitudeImbalance_dB;
289 }
290
291 double getTxIqCrossTalkDegree() override {
292 return getUserSpecifiedTxParameters().txIQPhaseImbalance_rad / M_PI * 360;
293 }
294
295 double getRxIqRatioDB() override {
296 return rxIQMismatchRatioDb;
297 }
298
299 double getRxIqCrossTalkDegree() override {
301 }
302
303 double getRxSensitivity() override {
304 return rxSensitivity;
305 }
306
307 void setRxSensitivity(const double rxSensitivityV) override {
308 rxSensitivity = rxSensitivityV;
309 LoggingService_SDR_info_print("{}'s RX sensitivity is changed to [{}] dB.", getReferredInterfaceName(), getRxSensitivity());
310 }
311
312 void setCarrierFrequencyOffset4TxEncoding(double carrierFrequencyOffset) override {
313 userSpecifiedTxParameters.carrierFrequencyOffset = carrierFrequencyOffset;
314 LoggingService_SDR_info_print("<{}> sets TX carrier frequency offset to [{}].", getReferredInterfaceName(), *getCarrierFrequencyOffset4TxEncoding());
315 }
316
317 std::optional<double> getCarrierFrequencyOffset4TxEncoding() override {
318 return userSpecifiedTxParameters.carrierFrequencyOffset;
319 }
320
321 void setSamplingFrequencyOffset4TxEncoding(double samplingFrequencyOffset) override {
322 userSpecifiedTxParameters.samplingRateOffset = samplingFrequencyOffset;
323 LoggingService_SDR_info_print("<{}> sets TX sampling frequency offset to [{}].", getReferredInterfaceName(), *getSamplingFrequencyOffset4TxEncoding());
324 }
325
326 std::optional<double> getSamplingFrequencyOffset4TxEncoding() override {
327 return userSpecifiedTxParameters.samplingRateOffset;
328 }
329
330 void setCarrierFrequencyOffset4RxDecoding(double rxCarrierFrequencyOffsetV) override {
331 rxCarrierFrequencyOffset = rxCarrierFrequencyOffsetV;
332 LoggingService_SDR_info_print("<{}> sets RX carrier frequency offset to [{}].", getReferredInterfaceName(), *getCarrierFrequencyOffset4RxDecoding());
333 }
334
335 std::optional<double> getCarrierFrequencyOffset4RxDecoding() override {
337 }
338
339 void setSamplingFrequencyOffset4RxDecoding(double samplingFrequencyOffset) override {
340 rxSamplingFrequencyOffset = samplingFrequencyOffset;
341 LoggingService_SDR_info_print("<{}> sets RX sampling frequency offset to [{}].", getReferredInterfaceName(), *getSamplingFrequencyOffset4RxDecoding());
342 }
343
344 std::optional<double> getSamplingFrequencyOffset4RxDecoding() override {
346 }
347
348 double getSamplingRate() override {
349 const auto txRate = getTxSamplingRate();
350 const auto rxRate = getRxSamplingRate();
351
352 if (txRate == rxRate)
353 return txRate;
354
355 LoggingService_SDR_warning_print(fmt::format("<{}> has different Tx and Rx sampling rates (Tx={} MHz, Rx={} MHz), return Rx rate.", getReferredInterfaceName(), txRate / 1e6, rxRate / 1e6));
356 return rxRate;
357 }
358
359 void setSamplingRate(double samplingRate) override {
360 setTxSamplingRate(samplingRate);
361 setRxSamplingRate(samplingRate);
362 }
363
364 double getTxResampleRatio() override {
365 return getUserSpecifiedTxParameters().resampleRatio;
366 }
367
368 void setTxResampleRatio(double txResampleRatioV) override {
369 if (!txChannels.empty()) {
370 getUserSpecifiedTxParameters().resampleRatio = txResampleRatioV;
371 LoggingService_SDR_info_print("<{}> sets TX resample ratio to [{}].", getReferredInterfaceName(), getTxResampleRatio());
372 }
373 }
374
375 double getRxResampleRatio() override {
376 return rxResampleRatio;
377 }
378
379 bool isRxResampleBypassFIR() override {
380 return rxResampleBypassFIR;
381 }
382
383 void setRxResampleBypassFIR(const bool bypass) override {
384 if (!rxChannels.empty()) {
385 rxResampleBypassFIR = bypass;
386 LoggingService_SDR_info_print("<{}> sets RX resample bypass FIR to [{}].", getReferredInterfaceName(), isRxResampleBypassFIR());
387 }
388 }
389
390 bool isTxSplitHighLow() override {
391 return getUserSpecifiedTxParameters().splitHighLow;
392 }
393
394 void setTxSplitHighLow(const bool split) override {
395 getUserSpecifiedTxParameters().splitHighLow = split;
396 }
397
398 bool isRxMergeHighLow() override {
399 return mergeHighLow;
400 }
401
402 void setRxMergeHighLow(const bool merge) override {
403 mergeHighLow = merge;
404 }
405
406 void setRxResampleRatio(double rxResampleRatioV) override {
407 rxResampleRatio = rxResampleRatioV;
408 LoggingService_SDR_info_print("<{}> sets RX resample ratio to [{}].", getReferredInterfaceName(), getRxResampleRatio());
409 }
410
411 double getRxOfdmSymbolOffset() override {
412 return rxOFDMSymbolOffset;
413 }
414
415 void setRxOfdmSymbolOffset(const double rxOfdmSymbolOffset) override {
416 rxOFDMSymbolOffset = rxOfdmSymbolOffset;
417 LoggingService_SDR_info_print("{}'s RX OFDM Symbol Offset is changed to [{}].", getReferredInterfaceName(), getRxOfdmSymbolOffset());
418 }
419
422 }
423
424 void getNumSamplesSavedBeforeStart(const double numSamplesSavedBeforeStartV) override {
425 numSamplesSavedBeforeStart = numSamplesSavedBeforeStartV;
426 }
427
428 uint8_t getTxChainMask() override {
429 std::bitset<8> txcmBits;
430 for (const auto& txch: userSpecifiedTxChannels())
431 txcmBits.set(txch, true);
432
433 return txcmBits.to_ulong();
434 }
435
436 void setTxChainMask(const uint8_t txChainMask) override {
437 std::bitset<8> txcmBits{txChainMask};
438 std::vector<size_t> newTxChannels;
439 for (size_t i = 0; i < txcmBits.size(); i++) {
440 if (txcmBits[i])
441 newTxChannels.emplace_back(i);
442 }
443 setTxChannels(newTxChannels);
444 }
445
446 uint8_t getRxChainMask() override {
447 std::bitset<8> rxcmBits;
448 for (const auto& rxch: userSpecifiedRxChannels())
449 rxcmBits.set(rxch, true);
450
451 return rxcmBits.to_ulong();
452 }
453
454 void setRxChainMask(const uint8_t rxChainMask) override {
455 std::bitset<8> rxcmBits{rxChainMask};
456 std::vector<size_t> newRxChannels;
457 for (size_t i = 0; i < rxcmBits.size(); i++) {
458 if (rxcmBits[i])
459 newRxChannels.emplace_back(i);
460 }
461 setRxChannels(newRxChannels);
462 }
463
464 [[nodiscard]] const std::vector<size_t>& userSpecifiedTxChannels() const override {
465 return txChannels;
466 }
467
468 [[deprecated("Use 'const std::vector<size_t>& userSpecifiedTxChannels() const' instead")]]
469 std::vector<size_t> getTxChannels() override {
470 return txChannels;
471 }
472
473 void setTxChannels(const std::vector<size_t>& txChannelsN) override {
474 txChannels = txChannelsN;
475 std::stringstream ss;
476 // If we deliberately set empty txChannels, it means we want to disable Tx
477 if (!txChannelsN.empty()) {
478 std::copy(txChannels.cbegin(), txChannels.cend() - 1, std::ostream_iterator<int>(ss, ","));
479 ss << txChannels.back();
480 LoggingService_SDR_info_print("<{}> sets Tx-Channel to [{}].", getReferredInterfaceName(), ss.str());
481 }
482 }
483
484 [[nodiscard]] const std::vector<size_t>& userSpecifiedRxChannels() const override {
485 return rxChannels;
486 }
487
488 [[deprecated("Use 'const std::vector<size_t>& userSpecifiedRxChannels() const' instead")]]
489 std::vector<size_t> getRxChannels() override {
490 return rxChannels;
491 }
492
493 void setRxChannels(const std::vector<size_t>& rxChannelsN) override {
494 rxChannels = rxChannelsN;
495 std::stringstream ss;
496 // If we deliberately set empty rxChannels, it means we want to disable Rx
497 if (!rxChannelsN.empty()) {
498 std::copy(rxChannels.cbegin(), rxChannels.cend() - 1, std::ostream_iterator<int>(ss, ","));
499 ss << rxChannels.back();
500 }
501 LoggingService_SDR_info_print("<{}> sets Rx-Channel to [{}].", getReferredInterfaceName(), ss.str());
502 }
503
504 bool isFullDuplexEnabled() override {
505 return fullDuplex;
506 }
507
508 void setFullDuplex(const bool enableFullDuplex) override {
509 fullDuplex = enableFullDuplex;
510 }
511
512 [[nodiscard]] SDRFrontEndTransferType getTransferType() const override {
513 return transferType;
514 }
515
516 void setTransferType(const SDRFrontEndTransferType transferTypeV) override {
517 transferType = transferTypeV;
518 }
519
520 void setNumThreads4RxDecoding(const uint8_t numThreads) override {
521 numDecodingThreads = numThreads;
522 LoggingService_SDR_info_print("<{}> sets {}-thread parallel baseband decoding.", getReferredInterfaceName(), numDecodingThreads);
523 }
524
525 void printStatus() override {
526 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
527 }
528
529 ExtraInfo buildExtraInfo() override {
530 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
531 }
532
533 std::tuple<double, double, double> getChannelAndBandwidth() override {
534 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
535 }
536
537 int setChannelAndBandwidth(double control, double bw, double center) override {
538 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
539 }
540
541 int setChannelAndBandwidth(std::optional<double> control, std::optional<double> bw, std::optional<double> center) override {
542 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
543 }
544
545 [[deprecated("Use 'getTxCarrierFrequencies' or 'getRxCarrierFrequencies' instead")]]
546 double getCarrierFrequency() override {
547 if (!userSpecifiedTxChannels().empty()) {
548 return getTxCarrierFrequencies()[0];
549 }
550 if (!userSpecifiedRxChannels().empty()) {
551 return getRxCarrierFrequencies()[0];
552 }
553 return 0.0;
554 }
555
556 void setCarrierFrequency(double carrierFrequency) override {
557 if (!userSpecifiedTxChannels().empty()) {
558 setTxCarrierFrequencies(std::vector<double>(userSpecifiedTxChannels().size(), carrierFrequency));
559 }
560 if (!userSpecifiedRxChannels().empty()) {
561 setRxCarrierFrequencies(std::vector<double>(userSpecifiedRxChannels().size(), carrierFrequency));
562 }
563 }
564
565 double getControlChannelFrequency() override {
566 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
567 }
568
569 void setControlChannelFrequency(double controlFrequency) override {
570 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
571 }
572
573 double getRxChannelBandwidth() override {
574 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
575 }
576
577 void setRxChannelBandwidth(double rxcbw) override {
578 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
579 }
580
581 double getTxpower() override {
582 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
583 }
584
585 void setTxpower(double txpower) override {
586 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
587 }
588
589 double getRxGain() override {
590 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
591 }
592
593 void setRxGain(double rxGain) override {
594 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
595 }
596
597 void setRxGain(double rxGain, uint8_t channel) override {
598 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
599 }
600
601 uint16_t getChannelFlags() override {
602 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
603 }
604
605 std::string getClockSource() override {
606 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
607 }
608
609 void setClockSource(const std::string& clockSource) override {
610 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
611 }
612
613 void setClockSources(const std::vector<std::string>& clockSources) override {
614 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
615 }
616
617 std::vector<std::string> getClockSources() override {
618 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
619 }
620
621 std::string getTimeSource() override {
622 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
623 }
624
625 void setTimeSource(const std::string& timeSource) override {
626 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
627 }
628
629 void setTimeSources(const std::vector<std::string>& timeSources) override {
630 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
631 }
632
633 std::vector<std::string> getTimeSources() override {
634 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
635 }
636
637 double getMasterClockRate() override {
638 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
639 }
640
641 void setMasterClockRate(double masterClockRate) override {
642 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
643 }
644
645 double getSDRFrontEndTime() override {
646 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
647 }
648
649 std::string getTxAntenna() override {
650 return getTxAntennas().at(0);
651 }
652
653 void setTxAntenna(const std::string& txAnt) override {
654 setTxAntennas({txAnt});
655 }
656
657 std::string getRxAntenna() override {
658 return getRxAntennas().at(0);
659 }
660
661 void setRxAntenna(const std::string& rxAnt) override {
662 setRxAntennas({rxAnt});
663 }
664
665 std::vector<std::string> getTxAntennas() override {
666 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
667 }
668
669 void setTxAntennas(const std::vector<std::string>& txAnts) override {
670 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
671 }
672
673 std::vector<std::string> getRxAntennas() override {
674 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
675 }
676
677 void setRxAntennas(const std::vector<std::string>& rxAnts) override {
678 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
679 }
680
681 double getFilterBandwidth() override {
682 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
683 }
684
685 void setFilterBandwidth(double bw) override {
686 throw std::runtime_error(fmt::format("<{}> does not support operation: {}.", getReferredInterfaceName(), std::string(BOOST_CURRENT_FUNCTION)));
687 }
688
689 void setRxPipelineChannels(std::vector<std::vector<size_t>> PipelineChannels)override {
690 rxPipelineChannels = PipelineChannels;
691 }
692
693 std::vector<std::vector<size_t>> getRxPipelineChannels()override {
694 return rxPipelineChannels;
695 }
696
697 void setPinelineConfig(std::vector<PipelineConfig> cfgs)override {
698 rxPipelineConfigs = cfgs;
699 }
700
701 std::vector<PipelineConfig> getPinelineConfig()override {
702 return rxPipelineConfigs;
703 }
704protected:
705 std::optional<uint32_t> txSignalReplayDelay_ms;
706 std::optional<std::string> txSignalDumpFilePath;
707 std::optional<std::string> rxSignalDumpFilePath;
708 std::optional<double> rxSignalDumpDelayedStartDuration;
709 std::optional<std::string> rxSignalReplayFilePath;
710
711 bool fullDuplex{false};
714 std::vector<size_t> txChannels{}; // TwinRx has no Tx channel
715 std::vector<size_t> rxChannels{}; // LFTX and BasicTx have no Rx channel
716 std::vector<std::vector<size_t>> rxPipelineChannels{};
717 std::vector<PipelineConfig> rxPipelineConfigs{};
718
719 ChannelBandwidthEnum rxCBW{ChannelBandwidthEnum::CBW_20};
720 double rxResampleRatio{1.0};
722 bool mergeHighLow{false};
723 double rxOFDMSymbolOffset{0.75};
725 double rxSensitivity{10};
728 std::optional<double> rxCarrierFrequencyOffset{};
729 std::optional<double> rxSamplingFrequencyOffset{};
730
731 // Override hardware information refresh methods
733 // Default implementation - derived classes should override
734 }
735
737 // Default implementation - derived classes should override
738 // boardInfo.motherboardSerial = SDRSerialNumber;
739 // boardInfo.motherboardModel = SDRHWName;
740 // boardInfo.fpgaImageFlavor = SDRHWType;
741 }
742
744 // Default implementation - derived classes should override
745 }
746
748 // Derived classes should override this
749 }
750
752 // Derived classes should override this
753 }
754
756 // Derived classes should override this
757 }
758
760 // Default implementation - derived classes should override
761 }
762
764 // Default implementation - derived classes should override
765 }
766
768 // Derived classes should override this
769 }
770
772 // Derived classes should override this
773 }
774
776 // Derived classes should override this
777 }
778
780 // Derived classes should override this
781 }
782};
783
784#endif // PICOSCENES_PLATFORM_ABSTRACTSDRFRONTEND_HXX
#define LoggingService_SDR_warning_printf(...)
#define LoggingService_SDR_debug_printf(...)
#define LoggingService_SDR_info_print(...)
#define LoggingService_SDR_warning_print(...)
SDRFrontEndTransferType
Enumeration for SDR frontend data transfer type.
The shared abstract frontend interface for both SDR and NIC.
PicoScenesDeviceSubtype getFrontEndSubtype() const
Return the PicoScenesDeviceType subtype of this frontend.
std::string referredInterfaceName
the user specified frontend name
virtual int startTx()
Activate internal Tx service loop.
PicoScenesFrameTxParameters & getUserSpecifiedTxParameters()
bool txServiceStarted
indicate whether Tx service is running
PicoScenesFrameTxParameters userSpecifiedTxParameters
user-specified Tx parameters
PicoScenesDeviceType getFrontEndType() const
Return the PicoScenesDeviceType of this frontend.
const std::string & getReferredInterfaceName() const
Get the user-specified frontend name.
The shared abstract frontend for SDR devices only.
double getRxGain() override
Get the total Rx gain. However, currently only QCA9300 and SDR frontend support this method.
void setTxpower(double txpower) override
Set the Tx power (in dB)
void setRxChainMask(const uint8_t rxChainMask) override
Set Rx chain mask. Setting Rx chain mask means to use only the selected Rx chains for signal receptio...
void refreshHardwareInfoForAGCSupport() override
Refresh AGC support information.
std::string getRxAntenna() override
Get the current Rx antenna.
virtual std::optional< double > getCurrentNoiseFloor()=0
double getTxpower() override
get the transmission power (Tx power)
virtual void prebuildSignals(ModularPicoScenesTxFrame &frame, const uint8_t numTxAntenna)
Generate the baseband signal via AbstractSDRFrontEnd::generateSignal and save the data into the prebu...
void setCarrierFrequencyOffset4TxEncoding(double carrierFrequencyOffset) override
Set the carrier frequency offset for Tx encoding.
std::optional< uint32_t > txSignalReplayDelay_ms
int transmitFramesInBatch(const std::vector< const ModularPicoScenesTxFrame * > &frames, const uint32_t repeat) override
Transmit frames in batch with precise inter-frame timing.
std::string getTxAntenna() override
Get the current Tx antenna.
virtual void clearTxStatus()=0
void setMasterClockRate(double masterClockRate) override
Set the master clock rate.
double getRxIqCrossTalkDegree() override
Get the I/Q crosstalk angle for Rx.
const std::vector< size_t > & userSpecifiedRxChannels() const override
Get the list of user-specified Rx channels.
std::optional< double > rxSamplingFrequencyOffset
void setSamplingFrequencyOffset4RxDecoding(double samplingFrequencyOffset) override
Set the sampling frequency offset for Rx decoding.
void setCarrierFrequency(double carrierFrequency) override
Get the RF carrier frequency.
std::optional< double > getCarrierFrequencyOffset4TxEncoding() override
Get the carrier frequency offset for Tx encoding.
void setCarrierFrequencyOffset4RxDecoding(double rxCarrierFrequencyOffsetV) override
Set the carrier frequency offset for Rx decoding.
void refreshHardwareInfoForClockAndTimeSources() override
Refresh clock and time sources information.
void setTxSplitHighLow(const bool split) override
Set whether to split high/low for Tx.
double getTxIqCrossTalkDegree() override
Get the I/Q crosstalk angle for Tx.
virtual SDRFrontEndTxStatus getLastTxStatus() const =0
void refreshHardwareInfoForSamplingRateRanges() override
Refresh sampling rate ranges information.
void setRxAntennas(const std::vector< std::string > &rxAnts) override
Set antennas for all Rx chains.
void setRxChannelBandwidthMode(ChannelBandwidthEnum rxCbw) override
Set the channel bandwidth mode for Rx.
void setTransferType(const SDRFrontEndTransferType transferTypeV) override
Set the transfer type.
void setTxSignalReplayDelayMs(const std::optional< uint32_t > &txSignalReplayDelayMs)
void setTxResampleRatio(double txResampleRatioV) override
Set the Tx resampling ratio.
void setRxResampleBypassFIR(const bool bypass) override
Set whether to bypass FIR filter in Rx resampling.
void setRxSignalDumpDelayedStartDuration(const double delayedStartDuration)
std::string getClockSource() override
Get the current clock source.
virtual int32_t transmitSignalInBatchedSingleBurst(const std::vector< std::vector< const void * > > &multipleSignals, const std::vector< int64_t > &signalLength, const std::vector< double > &postfixDurations, uint32_t repeat)=0
void setRxChannels(const std::vector< size_t > &rxChannelsN) override
Set the number of the active Rx channels, starting from 0.
std::vector< std::string > getTxAntennas() override
Get list of available Tx antennas.
void setRxOfdmSymbolOffset(const double rxOfdmSymbolOffset) override
Set the Rx OFDM symbol offset.
int setChannelAndBandwidth(double control, double bw, double center) override
Set the <contorlfreq-bandwidth-centerfreq> channel parameter.
double getTxResampleRatio() override
Get the Tx resampling ratio.
double getSDRFrontEndTime() override
Get the current SDR hardware time.
virtual int transmitSignalsContinuously(const std::vector< const void * > &multiChannelHeaders, int64_t numSignalsPerAntenna, bool burstStart, bool burstEnd)=0
std::vector< size_t > rxChannels
void setTxChainMask(const uint8_t txChainMask) override
Set Tx chain mask. Setting Tx chain mask means to use only the selected Tx chain for signal transmiss...
int transmitFramesInBatch(Frames &&... frames)
AbstractSDRFrontEnd(const std::string &referredInterfaceName)
Create AbstractSDRFrontEnd object according to the user-specified frontend name referredInterfaceName...
void setRxGain(double rxGain) override
Set the total Rx gain. However, currently only QCA9300 and SDR frontend support this method.
std::optional< std::string > rxSignalReplayFilePath
int setChannelAndBandwidth(std::optional< double > control, std::optional< double > bw, std::optional< double > center) override
Set the <contorlfreq-bandwidth-centerfreq> channel parameter, but with optional.
std::vector< size_t > getTxChannels() override
Get the numbers of the active Tx channels, starting from 0.
void setFilterBandwidth(double bw) override
Set the filter bandwidth.
ExtraInfo buildExtraInfo() override
Build an ExtraInfo object describing the current frontend status.
double getTxIqRatioDB() override
Get the I/Q ratio for Tx in dB.
void refreshHardwareInfoForSupportedAntennas() override
Refresh supported antennas information.
SDRFrontEndTransferType transferType
double getCarrierFrequency() override
Get the RF carrier frequency.
void setRxAntenna(const std::string &rxAnt) override
Set the Rx antenna.
void setTxAntenna(const std::string &txAnt) override
Set the Tx antenna.
std::vector< std::vector< size_t > > getRxPipelineChannels() override
double getSamplingRate() override
Get the baseband sampling rate.
std::optional< std::string > txSignalDumpFilePath
void setRxSensitivity(const double rxSensitivityV) override
Set the receiver sensitivity.
void setRxResampleRatio(double rxResampleRatioV) override
Set the Rx resampling ratio.
void setFullDuplex(const bool enableFullDuplex) override
Set full duplex mode.
virtual size_t transmitSignal(const CS16Vector &signals, const uint8_t numTxChannels, const double postfixDuration)
void refreshHardwareInfoForFrequencyRanges() override
Refresh frequency ranges information.
void setRxSignalDumpFilePath(const std::string &rxSignalDumpFilePathV)
double getRxOfdmSymbolOffset() override
Get the Rx OFDM symbol offset.
std::tuple< double, double, double > getChannelAndBandwidth() override
Get the <contorlfreq-bandwidth-centerfreq> channel parameter.
std::vector< std::string > getClockSources() override
Get list of current clock sources.
std::optional< double > rxCarrierFrequencyOffset
double getRxResampleRatio() override
Get the Rx resampling ratio.
std::vector< size_t > getRxChannels() override
Get the number of the active Rx channels, starting from 0.
void setTxSignalDumpFilePath(const std::string &txSignalDumpFilePathV)
void refreshHardwareInfoForFrontEndProfiles() override
Refresh frontend profiles information.
void refreshHardwareInfoForSensors() override
Refresh sensors information.
void setClockSources(const std::vector< std::string > &clockSources) override
Set clock sources for multiple motherboards.
uint8_t getRxChainMask() override
Get Rx chain mask.
std::optional< std::string > rxSignalDumpFilePath
std::optional< double > getSamplingFrequencyOffset4TxEncoding() override
Get the sampling frequency offset for Tx encoding.
void refreshHardwareInfoForMasterClockRates() override
Refresh master clock rates information.
const std::vector< size_t > & userSpecifiedTxChannels() const override
Get the list of user-specified Tx channels.
void setSamplingFrequencyOffset4TxEncoding(double samplingFrequencyOffset) override
Set the sampling frequency offset for Tx encoding.
void setRxMergeHighLow(const bool merge) override
Set whether to merge high/low for Rx.
void refreshHardwareInfoForGainRanges() override
Refresh gain ranges information.
ChannelBandwidthEnum rxCBW
void setTxIQMismatch(double iq_ratio_db, double iq_crosstalk_degree) override
Set I/Q mismatch parameters for Tx.
ChannelBandwidthEnum getRxChannelBandwidthMode() override
Get the current channel bandwidth mode for Rx.
uint8_t getTxChainMask() override
Get the Tx chain mask.
void setSignalReplayFilePath(const std::string &signalReplayFilePath)
bool isFullDuplexEnabled() override
Check if full duplex mode is enabled.
void setRxIQMismatch(double iq_ratio_db, double iq_crosstalk_degree) override
Set I/Q mismatch parameters for Rx.
void setTimeSources(const std::vector< std::string > &timeSources) override
Set time sources for multiple motherboards.
void setRxGain(double rxGain, uint8_t channel) override
Set the Rx gain for the specified channel. However, currently only QCA9300 and SDR frontend support t...
virtual int32_t transmitSignals(const std::vector< const void * > &signals, int64_t bufferLength, double postfixDuration)=0
void setPinelineConfig(std::vector< PipelineConfig > cfgs) override
void printStatus() override
Print a status string to command line.
void setRxPipelineChannels(std::vector< std::vector< size_t > > PipelineChannels) override
void refreshHardwareInfoForSupportedChannels() override
Refresh supported channels information.
std::string getTimeSource() override
Get the current time source.
double getFilterBandwidth() override
Get the current filter bandwidth.
double getNumSamplesSavedBeforeStart() override
Get the number of samples saved before start.
std::optional< double > getCarrierFrequencyOffset4RxDecoding() override
Get the carrier frequency offset for Rx decoding.
void setControlChannelFrequency(double controlFrequency) override
Get the control channel frequency of <contorlfreq-bandwidth-centerfreq> channel parameter.
std::vector< std::vector< size_t > > rxPipelineChannels
void refreshHardwareInfoForTemperatures() override
Refresh temperature readings.
virtual CS16Vector generateSignal(const ModularPicoScenesTxFrame &frame, uint8_t numTxAntenna)=0
Generate the baseband signal for ModularPicoScenesTxFrame frame with an optional numTxAntenna specify...
void setTxAntennas(const std::vector< std::string > &txAnts) override
Set antennas for all Tx chains.
void setNumThreads4RxDecoding(const uint8_t numThreads) override
Set the number of threads for Rx decoding.
std::vector< PipelineConfig > getPinelineConfig() override
std::vector< size_t > txChannels
double getMasterClockRate() override
Get the master clock rate.
double getRxSensitivity() override
Get the receiver sensitivity.
bool isRxResampleBypassFIR() override
Check if FIR filter is bypassed in Rx resampling.
std::vector< std::string > getRxAntennas() override
Get list of available Rx antennas.
uint16_t getChannelFlags() override
Get the old ChannelFlags descriptor.
void setClockSource(const std::string &clockSource) override
Set the clock source.
std::vector< PipelineConfig > rxPipelineConfigs
double getRxChannelBandwidth() override
Get the 802.11 Channel Bandwidth parameter (20e6/40e6/80e6/160e6/320e6) of the Rx path.
int transmit(const ModularPicoScenesTxFrame &frame) override
Transmit a ModularPicoScenesTxFrame frame via current frontend hardware.
std::optional< double > getSamplingFrequencyOffset4RxDecoding() override
Get the sampling frequency offset for Rx decoding.
void setTxChannels(const std::vector< size_t > &txChannelsN) override
This methods is fundamentally.
std::optional< double > rxSignalDumpDelayedStartDuration
virtual std::vector< CS16Vector > generateMultiChannelSignals(const ModularPicoScenesTxFrame &frame, const uint8_t numTxAntenna)
Generate the baseband signal via AbstractSDRFrontEnd::generateSignal, convert to SignalType type,...
void setSamplingRate(double samplingRate) override
set sampling rate (in Hz)
bool isRxMergeHighLow() override
Check if high/low merge is enabled for Rx.
void setTimeSource(const std::string &timeSource) override
Set the time source.
void setRxChannelBandwidth(double rxcbw) override
Set the Rx channel bandwidth parameter of the <contorlfreq-bandwidth-centerfreq> channel parameters.
void refreshHardwareInfoForBoards() override
Refresh boards information.
SDRFrontEndTransferType getTransferType() const override
Get the current transfer type.
void getNumSamplesSavedBeforeStart(const double numSamplesSavedBeforeStartV) override
Set the number of samples saved before start.
std::vector< std::string > getTimeSources() override
Get list of current time sources.
double getControlChannelFrequency() override
Get the control channel frequency of <contorlfreq-bandwidth-centerfreq> channel parameter.
bool isTxSplitHighLow() override
Check if high/low split is enabled for Tx.
double getRxIqRatioDB() override
Get the I/Q ratio for Rx in dB.
virtual size_t transmitSignal(const std::vector< CS16Vector > &multiChannelSignals, const double postfixDuration)
virtual int transmitSignalsFromFile(const std::string &signalSourceFile, uint64_t repeat)=0
Transmits baseband signals from a .bbsignals file.
static const std::string SDR_Tx2File
static std::shared_ptr< LicenseService > getInstance()
Class providing comprehensive hardware information for SDR devices.
Configuration class for SDR frontend hardware settings.
virtual void setRxSamplingRate(double rxRate)=0
Set the Rx sampling rate.
virtual void setTxSamplingRate(double txRate)=0
Set the Tx sampling rate.
virtual double getTxSamplingRate()=0
Get the current Tx sampling rate.
virtual void setRxCarrierFrequencies(std::vector< double > carrierFreqs)=0
Set carrier frequencies for Rx channels.
virtual void setTxCarrierFrequencies(std::vector< double > carrierFreqs)=0
Set carrier frequencies for Tx channels.
virtual std::vector< double > getTxCarrierFrequencies()=0
Get carrier frequencies for Tx channels.
virtual std::vector< double > getRxCarrierFrequencies()=0
Get carrier frequencies for Rx channels.
virtual double getRxSamplingRate()=0
Get the current Rx sampling rate.
OutputIterator fastCopyN(InputIterator first, std::size_t count, OutputIterator result)
Structure containing transmission status information for SDR frontend.