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 <signal.h>
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 << "Stoppping\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 stream;
84
85 stream.channels[TRXDir::Rx] = { 0, 1 };
86 stream.channels[TRXDir::Tx] = { 0, 1 };
87
88 stream.format = DataFormat::F32;
89 stream.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
111 device->StreamSetup(stream, chipIndex);
112 device->StreamStart(chipIndex);
113
114 } catch (std::runtime_error& e)
115 {
116 std::cout << "Failed to configure settings: "sv << e.what() << std::endl;
117 return -1;
118 } catch (std::logic_error& e)
119 {
120 std::cout << "Failed to configure settings: "sv << e.what() << std::endl;
121 return -1;
122 }
123 std::cout << "Stream started ...\n"sv;
124
125 auto startTime = std::chrono::high_resolution_clock::now();
126 auto t1 = startTime;
127 auto t2 = t1;
128
129 int totalSamplesReceived = 0;
130 uint32_t totalSamplesSent = 0;
131 float maxSignalAmplitude = 0;
132
133 StreamMeta rxMeta;
134 while (std::chrono::high_resolution_clock::now() - startTime < std::chrono::seconds(10) && !stopProgram)
135 {
136 uint32_t samplesRead = device->StreamRx(chipIndex, rxSamples, samplesInBuffer, &rxMeta);
137 totalSamplesReceived += samplesRead;
138
139 // process samples
140 for (uint32_t n = 0; n < samplesRead; ++n)
141 {
142 float amplitude = pow(rxSamples[0][n].real(), 2) + pow(rxSamples[0][n].imag(), 2);
143 if (amplitude > maxSignalAmplitude)
144 maxSignalAmplitude = amplitude;
145 }
146
147 StreamMeta txMeta;
148 txMeta.timestamp = rxMeta.timestamp + samplesInBuffer * 64;
149 txMeta.waitForTimestamp = true;
150 txMeta.flushPartialPacket = false;
151 uint32_t samplesSent = device->StreamTx(chipIndex, rxSamples, samplesInBuffer, &txMeta);
152 if (samplesSent < 0)
153 {
154 std::cout << "Failure to send\n"sv;
155 break;
156 }
157 totalSamplesSent += samplesSent;
158
159 t2 = std::chrono::high_resolution_clock::now();
160 if (t2 - t1 > std::chrono::seconds(1))
161 {
162 t1 = t2;
163 std::cout << "Total samples received: "sv << totalSamplesReceived << " signal amplitude: "sv
164 << std::sqrt(maxSignalAmplitude) << " total samples sent: "sv << totalSamplesSent << std::endl;
165
166#ifdef USE_GNU_PLOT
167 gp.write("plot '-' with points title 'ch 0'");
168 for (std::size_t c = 1; c < stream.channels.at(TRXDir::Rx).size(); ++c)
169 gp.writef(", '-' with points title 'ch %i'\n", c);
170 for (std::size_t c = 0; c < stream.channels.at(TRXDir::Rx).size(); ++c)
171 {
172 for (uint32_t n = 0; n < samplesInBuffer; ++n)
173 gp.writef("%f %f\n", rxSamples[c][n].real(), rxSamples[c][n].imag());
174 gp.write("e\n");
175 gp.flush();
176 }
177#endif
178 maxSignalAmplitude = 0;
179 }
180 }
181 DeviceRegistry::freeDevice(device);
182
183 for (int i = 0; i < 2; ++i)
184 delete[] rxSamples[i];
185 delete[] rxSamples;
186 return 0;
187}