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}