SDRDevice API example
This page of the documentation shows the usage example using the new SDRDevice API.
More examples written with this API can be found in the src/examples
directory.
Example dualRXTX
written with the new API
1/**
2 @file dualRXTX.cpp
3 @author Lime Microsystems (www.limemicro.com)
4 @brief minimal RX loopback to Tx example
5 */
6#include "limesuiteng/limesuiteng.hpp"
7#include <iostream>
8#include <chrono>
9#include <string_view>
10#include <cmath>
11#include <csignal>
12#ifdef USE_GNU_PLOT
13 #include "gnuPlotPipe.h"
14#endif
15
16using namespace lime;
17using namespace std::literals::string_view_literals;
18
19static const double frequencyLO = 1.5e9;
20float sampleRate = 10e6;
21static uint8_t chipIndex = 0; // device might have several RF chips
22
23bool stopProgram(false);
24void intHandler(int dummy)
25{
26 std::cout << "Stopping\n"sv;
27 stopProgram = true;
28}
29
30static LogLevel logVerbosity = LogLevel::Error;
31static void LogCallback(LogLevel lvl, const std::string& msg)
32{
33 if (lvl > logVerbosity)
34 return;
35 std::cout << msg << std::endl;
36}
37
38int main(int argc, char** argv)
39{
40 lime::registerLogHandler(LogCallback);
41 auto handles = DeviceRegistry::enumerate();
42 if (handles.size() == 0)
43 {
44 std::cout << "No devices found\n"sv;
45 return -1;
46 }
47 std::cout << "Devices found :"sv << std::endl;
48 for (size_t i = 0; i < handles.size(); i++)
49 std::cout << i << ": "sv << handles[i].Serialize() << std::endl;
50 std::cout << std::endl;
51
52 // Use first available device
53 SDRDevice* device = DeviceRegistry::makeDevice(handles.at(0));
54 if (!device)
55 {
56 std::cout << "Failed to connect to device"sv << std::endl;
57 return -1;
58 }
59 device->SetMessageLogCallback(LogCallback);
60 device->Init();
61
62 // RF parameters
63 SDRConfig config;
64 for (int c = 0; c < 2; ++c) // MIMO
65 {
66 config.channel[c].rx.enabled = true;
67 config.channel[c].rx.centerFrequency = frequencyLO;
68 config.channel[c].rx.sampleRate = sampleRate;
69 config.channel[c].rx.oversample = 2;
70 config.channel[c].rx.lpf = 0;
71 config.channel[c].rx.path = 2; // TODO: replace with string names
72 config.channel[c].rx.calibrate = false;
73
74 config.channel[c].tx.enabled = true;
75 config.channel[c].tx.sampleRate = sampleRate;
76 config.channel[c].tx.oversample = 2;
77 config.channel[c].tx.path = 2; // TODO: replace with string names
78 config.channel[c].tx.centerFrequency = frequencyLO;
79 config.channel[c].tx.calibrate = false;
80 }
81
82 // Samples data streaming configuration
83 StreamConfig streamCfg;
84
85 streamCfg.channels[TRXDir::Rx] = { 0, 1 };
86 streamCfg.channels[TRXDir::Tx] = { 0, 1 };
87
88 streamCfg.format = DataFormat::F32;
89 streamCfg.linkFormat = DataFormat::I16;
90
91 signal(SIGINT, intHandler);
92
93 const int samplesInBuffer = 256 * 4;
94 complex32f_t** rxSamples = new complex32f_t*[2]; // allocate two channels for simplicity
95 for (int i = 0; i < 2; ++i)
96 rxSamples[i] = new complex32f_t[samplesInBuffer];
97
98#ifdef USE_GNU_PLOT
99 GNUPlotPipe gp;
100 gp.write("set size square\n set xrange[-1:1]\n set yrange[-1:1]\n");
101#endif
102
103 std::cout << "Configuring device ...\n"sv;
104 try
105 {
106 auto t1 = std::chrono::high_resolution_clock::now();
107 device->Configure(config, chipIndex);
108 auto t2 = std::chrono::high_resolution_clock::now();
109 std::cout << "SDR configured in "sv << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << "ms\n"sv;
110 } catch (std::runtime_error& e)
111 {
112 std::cout << "Failed to configure settings: "sv << e.what() << std::endl;
113 return -1;
114 } catch (std::logic_error& e)
115 {
116 std::cout << "Failed to configure settings: "sv << e.what() << std::endl;
117 return -1;
118 }
119 std::unique_ptr<RFStream> stream = device->StreamCreate(streamCfg, chipIndex);
120 stream->Start();
121 std::cout << "Stream started ...\n"sv;
122
123 auto startTime = std::chrono::high_resolution_clock::now();
124 auto t1 = startTime;
125 auto t2 = t1;
126
127 int totalSamplesReceived = 0;
128 uint32_t totalSamplesSent = 0;
129 float maxSignalAmplitude = 0;
130
131 StreamMeta rxMeta{};
132 while (std::chrono::high_resolution_clock::now() - startTime < std::chrono::seconds(10) && !stopProgram)
133 {
134 uint32_t samplesRead = stream->StreamRx(rxSamples, samplesInBuffer, &rxMeta);
135 totalSamplesReceived += samplesRead;
136
137 // process samples
138 for (uint32_t n = 0; n < samplesRead; ++n)
139 {
140 float amplitude = pow(rxSamples[0][n].real(), 2) + pow(rxSamples[0][n].imag(), 2);
141 if (amplitude > maxSignalAmplitude)
142 maxSignalAmplitude = amplitude;
143 }
144
145 StreamMeta txMeta{};
146 txMeta.timestamp = rxMeta.timestamp + samplesInBuffer * 64;
147 txMeta.waitForTimestamp = true;
148 txMeta.flushPartialPacket = false;
149 uint32_t samplesSent = stream->StreamTx(rxSamples, samplesInBuffer, &txMeta);
150 if (samplesSent < 0)
151 {
152 std::cout << "Failure to send\n"sv;
153 break;
154 }
155 totalSamplesSent += samplesSent;
156
157 t2 = std::chrono::high_resolution_clock::now();
158 if (t2 - t1 > std::chrono::seconds(1))
159 {
160 t1 = t2;
161 std::cout << "Total samples received: "sv << totalSamplesReceived << " signal amplitude: "sv
162 << std::sqrt(maxSignalAmplitude) << " total samples sent: "sv << totalSamplesSent << std::endl;
163
164#ifdef USE_GNU_PLOT
165 gp.write("plot '-' with points title 'ch 0'");
166 for (std::size_t c = 1; c < streamCfg.channels.at(TRXDir::Rx).size(); ++c)
167 gp.writef(", '-' with points title 'ch %lu'\n", c);
168 for (std::size_t c = 0; c < streamCfg.channels.at(TRXDir::Rx).size(); ++c)
169 {
170 for (uint32_t n = 0; n < samplesInBuffer; ++n)
171 gp.writef("%f %f\n", rxSamples[c][n].real(), rxSamples[c][n].imag());
172 gp.write("e\n");
173 gp.flush();
174 }
175#endif
176 maxSignalAmplitude = 0;
177 }
178 }
179 stream.reset();
180 DeviceRegistry::freeDevice(device);
181
182 for (int i = 0; i < 2; ++i)
183 delete[] rxSamples[i];
184 delete[] rxSamples;
185 return 0;
186}