LMS API example
This page of the documentation shows the usage example using the old LMS API.
More examples written with this API can be found in the src/examples/legacy
directory.
Example dualRXTX
written with the legacy LimeSuite API
1/**
2 @file dualRXTX.cpp
3 @author Lime Microsystems (www.limemicro.com)
4 @brief Dual channel RX/TX example
5 */
6#include "lime/LimeSuite.h"
7#include <iostream>
8#include <chrono>
9#ifdef USE_GNU_PLOT
10 #include "gnuPlotPipe.h"
11#endif
12
13using namespace std;
14
15//Device structure, should be initialize to NULL
16lms_device_t* device = NULL;
17
18int error()
19{
20 cout << "Error; exiting!" << endl;
21 if (device != NULL)
22 LMS_Close(device);
23 exit(-1);
24}
25
26int main(int argc, char** argv)
27{
28 //Find devices
29 int n;
30 lms_info_str_t list[8]; //should be large enough to hold all detected devices
31 if ((n = LMS_GetDeviceList(list)) < 0) //NULL can be passed to only get number of devices
32 error();
33
34 cout << "Devices found: " << n << endl; //print number of devices
35 if (n < 1)
36 return -1;
37
38 //open the first device
39 if (LMS_Open(&device, list[0], NULL))
40 error();
41
42 //Initialize device with default configuration
43 //Do not use if you want to keep existing configuration
44 //Use LMS_LoadConfig(device, "/path/to/file.ini") to load config from INI
45 if (LMS_Init(device) != 0)
46 error();
47
48 //Get number of channels
49 if ((n = LMS_GetNumChannels(device, LMS_CH_RX)) < 0)
50 error();
51 cout << "Number of RX channels: " << n << endl;
52 if ((n = LMS_GetNumChannels(device, LMS_CH_TX)) < 0)
53 error();
54 cout << "Number of TX channels: " << n << endl;
55
56 //Enable RX channel
57 //Channels are numbered starting at 0
58 if (LMS_EnableChannel(device, LMS_CH_RX, 0, true) != 0)
59 error();
60 if (LMS_EnableChannel(device, LMS_CH_RX, 1, true) != 0)
61 error();
62 //Enable TX channels
63 if (LMS_EnableChannel(device, LMS_CH_TX, 0, true) != 0)
64 error();
65 if (LMS_EnableChannel(device, LMS_CH_TX, 1, true) != 0)
66 error();
67
68 //Set RX center frequency to 1 GHz
69 if (LMS_SetLOFrequency(device, LMS_CH_RX, 0, 1e9) != 0)
70 error();
71 if (LMS_SetLOFrequency(device, LMS_CH_RX, 1, 1e9) != 0)
72 error();
73 //Set TX center frequency to 1.2 GHz
74 //Automatically selects antenna port
75 if (LMS_SetLOFrequency(device, LMS_CH_TX, 0, 1.2e9) != 0)
76 error();
77 if (LMS_SetLOFrequency(device, LMS_CH_TX, 1, 1.2e9) != 0)
78 error();
79
80 //Set sample rate to 10 MHz, preferred oversampling in RF 4x
81 //This set sampling rate for all channels
82 if (LMS_SetSampleRate(device, 10e6, 4) != 0)
83 error();
84
85 //Set RX gain
86 if (LMS_SetNormalizedGain(device, LMS_CH_RX, 0, 0.7) != 0)
87 error();
88 if (LMS_SetNormalizedGain(device, LMS_CH_RX, 1, 0.7) != 0)
89 error();
90 //Set TX gain
91 if (LMS_SetNormalizedGain(device, LMS_CH_TX, 0, 0.4) != 0)
92 error();
93 if (LMS_SetNormalizedGain(device, LMS_CH_TX, 1, 0.4) != 0)
94 error();
95
96 //Enable test signals generation in RX channels
97 //To receive data from RF, remove these lines or change signal to LMS_TESTSIG_NONE
98 if (LMS_SetTestSignal(device, LMS_CH_RX, 0, LMS_TESTSIG_NCODIV4, 0, 0) != 0)
99 error();
100 if (LMS_SetTestSignal(device, LMS_CH_RX, 1, LMS_TESTSIG_NCODIV8F, 0, 0) != 0)
101 error();
102
103 //Streaming Setup
104
105 const int chCount = 2; //number of RX/TX streams
106 lms_stream_t rx_streams[chCount];
107 lms_stream_t tx_streams[chCount];
108 //Initialize streams
109 //All streams setups should be done before starting streams. New streams cannot be set-up if at least stream is running.
110 for (int i = 0; i < chCount; ++i)
111 {
112 rx_streams[i].channel = i; //channel number
113 rx_streams[i].fifoSize = 1024 * 1024; //fifo size in samples
114 rx_streams[i].throughputVsLatency = 0.5; //some middle ground
115 rx_streams[i].isTx = false; //RX channel
116 rx_streams[i].dataFmt = lms_stream_t::LMS_FMT_I12; //12-bit integers
117 if (LMS_SetupStream(device, &rx_streams[i]) != 0)
118 error();
119 tx_streams[i].channel = i; //channel number
120 tx_streams[i].fifoSize = 1024 * 1024; //fifo size in samples
121 tx_streams[i].throughputVsLatency = 0.5; //some middle ground
122 tx_streams[i].isTx = true; //TX channel
123 tx_streams[i].dataFmt = lms_stream_t::LMS_FMT_I12; //12-bit integers
124 if (LMS_SetupStream(device, &tx_streams[i]) != 0)
125 error();
126 }
127
128 //Initialize data buffers
129 const int bufersize = 1024 * 8; //complex samples per buffer
130 int16_t* buffers[chCount];
131 for (int i = 0; i < chCount; ++i)
132 {
133 buffers[i] = new int16_t[bufersize * 2]; //buffer to hold complex values (2*samples))
134 }
135
136 //Start streaming
137 for (int i = 0; i < chCount; ++i)
138 {
139 LMS_StartStream(&rx_streams[i]);
140 LMS_StartStream(&tx_streams[i]);
141 }
142
143 //Streaming
144
145 lms_stream_meta_t rx_metadata; //Use metadata for additional control over sample receive function behavior
146 rx_metadata.flushPartialPacket = false; //currently has no effect in RX
147 rx_metadata.waitForTimestamp = false; //currently has no effect in RX
148
149 lms_stream_meta_t tx_metadata; //Use metadata for additional control over sample send function behavior
150 tx_metadata.flushPartialPacket = false; //do not force sending of incomplete packet
151 tx_metadata.waitForTimestamp = true; //Enable synchronization to HW timestamp
152
153#ifdef USE_GNU_PLOT
154 GNUPlotPipe gp;
155 gp.write("set size square\n set xrange[-2050:2050]\n set yrange[-2050:2050]\n");
156#endif
157 auto t1 = chrono::high_resolution_clock::now();
158 auto t2 = t1;
159
160 while (chrono::high_resolution_clock::now() - t1 < chrono::seconds(10)) //run for 10 seconds
161 {
162 for (int i = 0; i < chCount; ++i)
163 {
164 int samplesRead;
165 //Receive samples
166 samplesRead = LMS_RecvStream(&rx_streams[i], buffers[i], bufersize, &rx_metadata, 1000);
167 //Send samples with 1024*256 sample delay from RX (waitForTimestamp is enabled)
168 tx_metadata.timestamp = rx_metadata.timestamp + 1024 * 256;
169 LMS_SendStream(&tx_streams[i], buffers[i], samplesRead, &tx_metadata, 1000);
170 }
171
172 //Print stats every 1s
173 if (chrono::high_resolution_clock::now() - t2 > chrono::seconds(1))
174 {
175 t2 = chrono::high_resolution_clock::now();
176#ifdef USE_GNU_PLOT
177 //Plot samples
178 gp.write("plot '-' with points title 'ch 0'");
179 for (int i = 1; i < chCount; ++i)
180 gp.write(", '-' with points title 'ch 1'\n");
181 for (int i = 0; i < chCount; ++i)
182 {
183 for (uint32_t j = 0; j < bufersize / 8; ++j)
184 gp.writef("%i %i\n", buffers[i][2 * j], buffers[i][2 * j + 1]);
185 gp.write("e\n");
186 gp.flush();
187 }
188#endif
189 //Print stats
190 lms_stream_status_t status;
191 LMS_GetStreamStatus(rx_streams, &status); //Obtain RX stream stats
192 cout << "RX rate: " << status.linkRate / 1e6 << " MB/s\n"; //link data rate (both channels))
193 cout << "RX 0 FIFO: " << 100 * status.fifoFilledCount / status.fifoSize << "%" << endl; //percentage of RX 0 fifo filled
194
195 LMS_GetStreamStatus(tx_streams, &status); //Obtain TX stream stats
196 cout << "TX rate: " << status.linkRate / 1e6 << " MB/s\n"; //link data rate (both channels))
197 cout << "TX 0 FIFO: " << 100 * status.fifoFilledCount / status.fifoSize << "%" << endl; //percentage of TX 0 fifo filled
198 }
199 }
200
201 //Stop streaming
202 for (int i = 0; i < chCount; ++i)
203 {
204 LMS_StopStream(&rx_streams[i]); //stream is stopped but can be started again with LMS_StartStream()
205 LMS_StopStream(&tx_streams[i]);
206 }
207 for (int i = 0; i < chCount; ++i)
208 {
209 LMS_DestroyStream(device, &rx_streams[i]); //stream is deallocated and can no longer be used
210 LMS_DestroyStream(device, &tx_streams[i]);
211 delete[] buffers[i];
212 }
213
214 //Close device
215 LMS_Close(device);
216
217 return 0;
218}