19 #include "LimeSdrReader.h"
24 #include "spdlog/spdlog.h"
27 if (_sdr !=
nullptr) {
28 LMS_StopStream(&_stream);
29 LMS_DestroyStream(_sdr, &_stream);
33 if (_reading_from_file) {
34 srslte_filesource_free(&file_source);
37 if (_writing_to_file) {
38 srslte_filesink_free(&file_sink);
43 const char* write_sample_file) ->
bool {
44 if (sample_file !=
nullptr) {
45 if (0 == srslte_filesource_init(&file_source,
46 const_cast<char*
>(sample_file),
47 SRSLTE_COMPLEX_FLOAT_BIN)) {
48 _reading_from_file =
true;
50 spdlog::error(
"Could not open file {}", sample_file);
54 int device_count = LMS_GetDeviceList(
nullptr);
55 if (device_count < 0) {
56 spdlog::error(
"LMS_GetDeviceList() : {}", LMS_GetLastErrorMessage());
60 lms_info_str_t device_list[device_count];
61 if (LMS_GetDeviceList(device_list) < 0) {
62 spdlog::error(
"LMS_GetDeviceList() : {}", LMS_GetLastErrorMessage());
66 if (LMS_Open(&_sdr, device_list[device_index],
nullptr) != 0) {
67 spdlog::error(
"LMS_Open() : {}", LMS_GetLastErrorMessage());
71 if (LMS_Init(_sdr) != 0) {
72 spdlog::error(
"LMS_Init() : {}", LMS_GetLastErrorMessage());
75 if (LMS_EnableChannel(_sdr, LMS_CH_RX, 0,
true) != 0) {
76 spdlog::error(
"LMS_EnableChannel() : {}", LMS_GetLastErrorMessage());
80 if (LMS_EnableChannel(_sdr, LMS_CH_TX, 0,
true) != 0) {
81 spdlog::error(
"LMS_EnableChannel() : {}", LMS_GetLastErrorMessage());
86 _stream.fifoSize = 1024*1024;
87 _stream.throughputVsLatency = 1;
88 _stream.isTx = LMS_CH_RX;
89 _stream.dataFmt = lms_stream_t::LMS_FMT_F32;
90 if (LMS_SetupStream(_sdr, &_stream) != 0) {
91 spdlog::error(
"LMS_SetupStream() : {}", LMS_GetLastErrorMessage());
95 if (write_sample_file !=
nullptr) {
96 if (0 == srslte_filesink_init(&file_sink,
97 const_cast<char*
>(write_sample_file),
98 SRSLTE_COMPLEX_FLOAT_BIN)) {
99 _writing_to_file =
true;
101 spdlog::error(
"Could not open file {}", write_sample_file);
107 _cfg.lookupValue(
"sdr.ringbuffer_size_ms", _buffer_ms);
108 unsigned int buffer_size = 16384 * _buffer_ms;
111 error = _buffer.initialize(
sizeof(cf_t) * buffer_size);
113 spdlog::error(
"Cannot allocate ringbuffer: {}", errno);
116 _buffer_ready =
true;
122 _high_watermark_reached =
false;
126 return _sdr !=
nullptr;
130 uint32_t bandwidth,
double gain,
const std::string& antenna) ->
bool {
133 _frequency = frequency;
134 _filterBw = bandwidth;
137 _high_watermark_reached =
false;
139 if (_reading_from_file) {
140 _sampleRate = sample_rate;
144 if (_sdr ==
nullptr) {
148 spdlog::info(
"Tuning Lime SDR Mini to {} MHz, filter bandwidth {} MHz, sample rate {}, gain {}, antenna path {}",
149 frequency/1000000.0, bandwidth/1000000.0, sample_rate/1000000.0, gain, antenna);
151 if (LMS_SetLOFrequency(_sdr, LMS_CH_RX, 0, frequency) != 0) {
152 spdlog::error(
"LMS_SetLOFrequency() : {}", LMS_GetLastErrorMessage());
156 if ( LMS_GetLOFrequency(_sdr, LMS_CH_RX, 0, &freq) != 0 ) {
157 spdlog::error(
"LMS_GetLOFrequency() : {}", LMS_GetLastErrorMessage());
160 spdlog::info(
"LO frequency set to {} MHz", freq / 1e6);
164 int nrAntennas = LMS_GetAntennaList(_sdr,
false, 0,
nullptr);
165 lms_name_t list[nrAntennas];
166 LMS_GetAntennaList(_sdr,
false, 0, list);
167 int antennaFound = 0;
168 for (
int i = 0; i < nrAntennas; i++) {
169 if (strcmp(list[i], antenna.c_str()) == 0) {
171 if (LMS_SetAntenna(_sdr,
false, 0, i) != 0) {
172 spdlog::error(
"LMS_SetAntenna() : {}", LMS_GetLastErrorMessage());
178 if (antennaFound == 0) {
179 spdlog::error(
"Antenna not found: {}", LMS_GetLastErrorMessage());
183 if (LMS_SetSampleRate(_sdr, sample_rate, 0) != 0) {
184 spdlog::error(
"LMS_SetSampleRate() : {}", LMS_GetLastErrorMessage());
188 double rf_rate = NAN;
189 if (LMS_GetSampleRate(_sdr, LMS_CH_RX, 0, &_sampleRate, &rf_rate) != 0) {
190 spdlog::error(
"LMS_GetSampleRate() : {}", LMS_GetLastErrorMessage());
193 spdlog::info(
"Sample rate on host interface {} MHz, RF ADC {} MHz", _sampleRate / 1e6, rf_rate / 1e6);
199 if (LMS_SetNormalizedGain(_sdr, LMS_CH_RX, 0, gain) != 0) {
200 spdlog::error(
"LMS_SetNormalizedGain() : {}", LMS_GetLastErrorMessage());
205 float_type setgain = NAN;
206 unsigned int gaindB = 0;
207 if (LMS_GetNormalizedGain(_sdr, LMS_CH_RX, 0, &setgain) != 0) {
208 spdlog::error(
"LMS_GetNr() : {}", LMS_GetLastErrorMessage());
211 if (LMS_GetGaindB(_sdr, LMS_CH_RX, 0, &gaindB) != 0) {
212 spdlog::error(
"LMS_GetGaindB(() : {}", LMS_GetLastErrorMessage());
216 spdlog::info(
"RX gain set to {} / {} dB", setgain, gaindB);
219 if (LMS_GetLPFBWRange(_sdr, LMS_CH_RX, &range) != 0) {
220 spdlog::error(
"LMS_GetLPFBWRange() : {}", LMS_GetLastErrorMessage());
223 spdlog::debug(
"LPF BW range {} - {} MHz, step {} Hz", range.min / 1e6, range.max / 1e6, range.step);
225 if (LMS_SetLPFBW(_sdr, LMS_CH_RX, 0, bandwidth*1.2) != 0) {
226 spdlog::error(
"LMS_SetLPFBW() : {}", LMS_GetLastErrorMessage());
230 if (LMS_Calibrate(_sdr, LMS_CH_RX, 0, bandwidth, 0) != 0) {
231 spdlog::error(
"LMS_Calibrate() : {}", LMS_GetLastErrorMessage());
240 LMS_StartStream(&_stream);
243 _readerThread = std::thread{&LimeSdrReader::read,
this};
244 struct sched_param thread_param = {};
245 thread_param.sched_priority = 50;
246 _cfg.lookupValue(
"sdr.reader_thread_priority_rt", thread_param.sched_priority);
248 spdlog::debug(
"Launching sample reader thread with realtime scheduling priority {}", thread_param.sched_priority);
250 int error = pthread_setschedparam(_readerThread.native_handle(), SCHED_RR, &thread_param);
252 spdlog::warn(
"Cannot set reader thread priority to realtime: {}. Thread will run at default priority with a high probability of dropped samples and loss of synchronisation.", strerror(error));
258 LMS_StopStream(&_stream);
260 _readerThread.join();
264 void LimeSdrReader::read() {
265 std::array<void*, SRSLTE_MAX_CHANNELS> radio_buffers = {
nullptr };
267 int toRead = ceil(_sampleRate / 1000.0);
268 if (_buffer.free_size() < toRead *
sizeof(cf_t)) {
269 spdlog::debug(
"ringbuffer overflow");
270 std::this_thread::sleep_for(std::chrono::microseconds(1000));
273 if (_reading_from_file) {
274 std::chrono::steady_clock::time_point entered = {};
275 entered = std::chrono::steady_clock::now();
276 int64_t required_time_us = (1000000.0/_sampleRate) * toRead;
278 radio_buffers[0] = _buffer.write_head();
279 read = srslte_filesource_read_multi(&file_source, radio_buffers.data(), toRead, 1);
281 srslte_filesource_seek(&file_source, 0);
285 _buffer.commit( read *
sizeof(cf_t) );
288 std::chrono::microseconds sleep = (std::chrono::microseconds(required_time_us) -
289 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - entered));
290 std::this_thread::sleep_for(sleep);
292 read = LMS_RecvStream(&_stream, _buffer.write_head(), toRead,
nullptr,
295 if (_writing_to_file) {
296 srslte_filesink_write(&file_sink, _buffer.write_head(), read);
298 _buffer.commit( read *
sizeof(cf_t) );
303 spdlog::debug(
"Sample reader thread exited");
309 std::chrono::steady_clock::time_point entered = {};
310 entered = std::chrono::steady_clock::now();
312 int64_t required_time_us = (1000000.0/_sampleRate) * nsamples;
313 size_t cnt = nsamples *
sizeof(cf_t);
315 if (_high_watermark_reached && _buffer.size() < (_sampleRate / 1000.0) * 10 *
sizeof(cf_t)) {
316 _high_watermark_reached =
false;
319 if (!_high_watermark_reached) {
320 while (_buffer.size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) *
sizeof(cf_t)) {
321 std::this_thread::sleep_for(std::chrono::microseconds(500));
323 spdlog::debug(
"Filled ringbuffer to half capacity");
324 _high_watermark_reached =
true;
327 memcpy(data, _buffer.read_head(), cnt);
328 _buffer.consume(cnt);
330 if (_buffer.size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) *
sizeof(cf_t)) {
331 required_time_us += 500;
333 required_time_us -= 500;
336 spdlog::debug(
"read {} samples, adjusted required {} us, delta {} us, sleep adj {}, sleeping for {} us",
338 std::chrono::microseconds(required_time_us).count(),
339 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read).count(),
341 (std::chrono::microseconds(_sleep_adjustment + required_time_us) - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read)).count());
343 std::chrono::microseconds sleep = (std::chrono::microseconds(_sleep_adjustment + required_time_us) - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read));
345 if (sleep.count() > 0) {
346 std::this_thread::sleep_for(sleep);
347 _sleep_adjustment = 0;
348 }
else if (sleep.count() > -100000) {
349 _sleep_adjustment = sleep.count();
352 _last_read = std::chrono::steady_clock::now();