20 #include <SoapySDR/Device.hpp>
21 #include <SoapySDR/Types.hpp>
22 #include <SoapySDR/Formats.hpp>
24 #include <boost/algorithm/string/join.hpp>
30 #include "spdlog/spdlog.h"
33 if (
_sdr !=
nullptr) {
34 auto sdr = (SoapySDR::Device*)
_sdr;
35 sdr->deactivateStream((SoapySDR::Stream*)
_stream, 0, 0);
36 sdr->closeStream((SoapySDR::Stream*)
_stream);
37 SoapySDR::Device::unmake( sdr );
51 auto results = SoapySDR::Device::enumerate();
52 SoapySDR::Kwargs::iterator it;
54 for(
int i = 0; i < results.size(); ++i)
56 printf(
"Device #%d:\n", i);
57 for( it = results[i].begin(); it != results[i].end(); ++it)
59 printf(
"%s = %s\n", it->first.c_str(), it->second.c_str());
67 const char* write_sample_file,
bool repeat_sample_file) ->
bool {
68 if (sample_file !=
nullptr) {
69 if (0 == srsran_filesource_init(&file_source,
70 const_cast<char*
>(sample_file),
71 SRSRAN_COMPLEX_FLOAT_BIN)) {
72 _reading_from_file =
true;
73 _repeat_sample_file = repeat_sample_file;
75 spdlog::error(
"Could not open file {}", sample_file);
79 if (write_sample_file !=
nullptr) {
80 if (0 == srsran_filesink_init(&file_sink,
81 const_cast<char*
>(write_sample_file),
82 SRSRAN_COMPLEX_FLOAT_BIN)) {
83 _writing_to_file =
true;
85 spdlog::error(
"Could not open file {}", write_sample_file);
89 _device_args = SoapySDR::KwargsFromString(device_args);
90 _sdr = SoapySDR::Device::make(_device_args);
93 spdlog::error(
"SoapySDR: failed to open device with args {}", device_args);
98 _cfg.lookupValue(
"modem.sdr.ringbuffer_size_ms", _buffer_ms);
104 _buffer = std::make_unique<MultichannelRingbuffer>(
sizeof(cf_t) * buffer_size,
_rx_channels);
116 auto sdr = (SoapySDR::Device*)_sdr;
117 auto antenna_list = sdr->listAntennas(SOAPY_SDR_RX, idx);
118 if (std::find(antenna_list.begin(), antenna_list.end(),
antenna) != antenna_list.end()) {
119 sdr->setAntenna( SOAPY_SDR_RX, idx,
antenna);
120 _antenna = sdr->getAntenna( SOAPY_SDR_RX, idx);
123 spdlog::error(
"Unknown antenna \"{}\". Available: {}.",
antenna, boost::algorithm::join(antenna_list,
", ") );
129 auto sdr = (SoapySDR::Device*)_sdr;
130 sdr->setFrequency( SOAPY_SDR_RX, idx,
frequency);
135 auto sdr = (SoapySDR::Device*)_sdr;
136 sdr->setBandwidth( SOAPY_SDR_RX, idx,
bandwidth);
141 auto sdr = (SoapySDR::Device*)_sdr;
142 sdr->setSampleRate( SOAPY_SDR_RX, idx, rate);
147 auto sdr = (SoapySDR::Device*)_sdr;
148 if (sdr->hasGainMode(SOAPY_SDR_RX, idx)) {
150 sdr->setGainMode(SOAPY_SDR_RX, idx,
use_agc);
154 auto gain_range = sdr->getGainRange(SOAPY_SDR_RX, idx);
155 _min_gain = gain_range.minimum();
156 _max_gain = gain_range.maximum();
157 if (
gain >= gain_range.minimum() &&
gain <= gain_range.maximum()) {
158 sdr->setGain( SOAPY_SDR_RX, idx,
gain);
160 _gain = sdr->getGain( SOAPY_SDR_RX, idx);
164 spdlog::error(
"Invalid gain setting {}. Allowed range is: {} - {}.",
gain, gain_range.minimum(), gain_range.maximum());
178 if (_reading_from_file) {
182 if (_sdr ==
nullptr) {
186 spdlog::info(
"Tuning to {} MHz, filter bandwidth {} MHz, sample rate {}, gain {}, antenna path {} with AGC set to {}",
189 auto sdr = (SoapySDR::Device*)_sdr;
191 for (
auto ch = 0; ch < _rx_channels; ch++) {
193 set_gain(_use_agc,
gain, ch);
199 _frequency = sdr->getFrequency( SOAPY_SDR_RX, 0);
200 bandwidth = sdr->getBandwidth( SOAPY_SDR_RX, 0);
201 _sampleRate = sdr->getSampleRate( SOAPY_SDR_RX, 0);
203 spdlog::info(
"SDR tuned to {} MHz, filter bandwidth {} MHz, sample rate {}, gain {}, antenna path {}",
204 _frequency/1000000.0,
bandwidth/1000000.0, _sampleRate/1000000.0, _gain, _antenna);
207 auto sensors = sdr->listSensors();
208 if (std::find(sensors.begin(), sensors.end(),
"lms7_temp") != sensors.end()) {
209 _temp_sensor_available =
true;
210 _temp_sensor_key =
"lms7_temp";
217 if (
_sdr !=
nullptr) {
218 auto sdr = (SoapySDR::Device*)
_sdr;
223 sdr->setHardwareTime(0);
227 spdlog::error(
"Failed to set up RX stream");
228 SoapySDR::Device::unmake( sdr );
231 sdr->activateStream( (SoapySDR::Stream*)
_stream, SOAPY_SDR_HAS_TIME, 100000000, 0);
237 struct sched_param thread_param = {};
238 thread_param.sched_priority = 50;
239 int min_prio = sched_get_priority_min(SCHED_RR);
240 int max_prio = sched_get_priority_max(SCHED_RR);
242 if (min_prio == -1 or max_prio == -1)
243 spdlog::error(
"Something went wrong, error in sched_get_priority_min/max");
245 _cfg.lookupValue(
"modem.sdr.reader_thread_priority_rt", thread_param.sched_priority);
247 spdlog::debug(
"Launching sample reader thread with realtime scheduling priority {}, available priorities, max: {}, min: {}", thread_param.sched_priority, max_prio, min_prio);
249 int error = pthread_setschedparam(
_readerThread.native_handle(), SCHED_RR, &thread_param);
251 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));
259 if (
_sdr !=
nullptr) {
260 auto sdr = (SoapySDR::Device*)
_sdr;
261 sdr->deactivateStream((SoapySDR::Stream*)
_stream, 0, 0);
262 sdr->closeStream((SoapySDR::Stream*)
_stream);
269 std::array<void*, SRSRAN_MAX_CHANNELS> radio_buffers = {
nullptr };
273 if (
_buffer->free_size() < toRead *
sizeof(cf_t)) {
274 spdlog::debug(
"ringbuffer overflow");
275 std::this_thread::sleep_for(std::chrono::microseconds(1000));
278 size_t writeable = 0;
279 size_t writeable_write = 0;
280 auto buffers =
_buffer->write_head(&writeable);
281 auto buffers_write =
_buffer_write->write_head(&writeable_write);
282 int writeable_samples = (int)floor(writeable /
sizeof(cf_t));
283 int writeable_write_samples = (int)floor(writeable_write /
sizeof(cf_t));
286 std::chrono::steady_clock::time_point entered = {};
287 entered = std::chrono::steady_clock::now();
294 spdlog::info(
"EOF, exiting...");
305 std::chrono::microseconds sleep = (std::chrono::microseconds(required_time_us) -
306 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - entered));
307 std::this_thread::sleep_for(sleep);
309 auto sdr = (SoapySDR::Device*)
_sdr;
311 long long time_ns = 0;
312 auto rbuff = buffers.data();
313 auto wbuff = buffers_write.data();
315 read = sdr->readStream( (SoapySDR::Stream*)
_stream, buffers.data(), std::min(writeable_samples, toRead), flags, time_ns);
321 memcpy(wbuff[i], rbuff[i], std::min(writeable_write_samples,
read) *
sizeof(cf_t));
328 int toWrite_samples =
_buffer_write->used_size() /
sizeof(cf_t);
332 spdlog::debug(
"buffer: commited {}, requested {}, writeable {}, writeable_write {}, flags {}",
read, toRead, writeable_samples, writeable_write_samples, flags);
335 spdlog::error(
"readStream returned {}",
read);
336 _buffer->commit( toRead *
sizeof(cf_t) );
342 spdlog::debug(
"Sample reader thread exited");
348 std::chrono::steady_clock::time_point entered = {};
349 entered = std::chrono::steady_clock::now();
351 int64_t required_time_us = (1000000.0/_sampleRate) * nsamples;
352 size_t cnt = nsamples *
sizeof(cf_t);
354 if (_high_watermark_reached && _buffer->used_size() < (_sampleRate / 1000.0) * 10 *
sizeof(cf_t)) {
355 _high_watermark_reached =
false;
358 if (!_high_watermark_reached) {
359 while (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) *
sizeof(cf_t)) {
360 std::this_thread::sleep_for(std::chrono::microseconds(500));
362 spdlog::debug(
"Filled ringbuffer to half capacity");
363 _high_watermark_reached =
true;
366 std::vector<char*> buffers(_rx_channels);
367 for (
auto ch = 0; ch < _rx_channels; ch++) {
368 buffers[ch] = (
char*)data[ch];
370 _buffer->read(buffers, cnt);
372 if (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 4.0) *
sizeof(cf_t)) {
373 required_time_us += 500;
375 required_time_us -= 500;
378 spdlog::debug(
"took {}, read {} samples, adjusted required {} us, delta {} us, sleep adj {}, sleeping for {} us",
379 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - entered).count(),
381 std::chrono::microseconds(required_time_us).count(),
382 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read).count(),
384 (std::chrono::microseconds(_sleep_adjustment + required_time_us) - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read)).count());
386 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));
388 if (sleep.count() > 0) {
389 std::this_thread::sleep_for(sleep);
390 _sleep_adjustment = 0;
391 }
else if (sleep.count() > -100000) {
392 _sleep_adjustment = sleep.count();
395 _last_read = std::chrono::steady_clock::now();
401 if (!_buffer_ready) {
404 return static_cast<double>(_buffer->used_size()) /
static_cast<double>(_buffer->capacity());
void stop()
Stop reading samples from the SDR.
const libconfig::Config & _cfg
void start()
Start reading samples from the SDR.
double get_buffer_level()
Get current ringbuffer level (0 = empty .
bool _high_watermark_reached
bool init(const std::string &device_args, const char *sample_file, const char *write_sample_file, bool repeat_sample_file)
Initializes the SDR interface and creates a ring buffer according to the params from Cfg.
std::unique_ptr< MultichannelRingbuffer > _buffer_write
void enumerateDevices()
Prints a list of all available SDR devices.
std::thread _readerThread
int get_samples(cf_t *data[SRSRAN_MAX_CHANNELS], uint32_t nsamples, srsran_timestamp_t *rx_time)
Store nsamples count samples into the buffer at data.
srsran_filesource_t file_source
bool set_gain(bool use_agc, double gain, uint8_t idx)
srsran_filesink_t file_sink
void clear_buffer()
Clear all samples from the rx buffers.
std::map< std::string, std::string > _device_args
bool set_sample_rate(uint32_t rate, uint8_t idx)
bool set_antenna(const std::string &antenna, uint8_t idx)
bool tune(uint32_t frequency, uint32_t sample_rate, uint32_t bandwidth, double gain, const std::string &antenna, bool use_agc)
Tune the SDR to the desired frequency, and set gain, filter and antenna parameters.
std::unique_ptr< MultichannelRingbuffer > _buffer
bool set_filter_bw(uint32_t bandwidth, uint8_t idx)
virtual ~SdrReader()
Default destructor.
bool set_frequency(uint32_t frequency, uint8_t idx)
static std::string antenna
Antenna input to be used.
static unsigned sample_rate
Sample rate of the SDR.
static uint32_t bandwidth
Low pass filter bandwidth for the SDR.
static unsigned frequency
Center freqeuncy the SDR is tuned to.
static double gain
Overall system gain for the SDR.