20 #include <SoapySDR/Device.hpp>
21 #include <SoapySDR/Types.hpp>
22 #include <SoapySDR/Formats.hpp>
24 #include <boost/algorithm/string/join.hpp>
29 #include "spdlog/spdlog.h"
32 if (
_sdr !=
nullptr) {
33 auto sdr = (SoapySDR::Device*)
_sdr;
34 sdr->deactivateStream((SoapySDR::Stream*)
_stream, 0, 0);
35 sdr->closeStream((SoapySDR::Stream*)
_stream);
36 SoapySDR::Device::unmake( sdr );
50 auto results = SoapySDR::Device::enumerate();
51 SoapySDR::Kwargs::iterator it;
53 for(
int i = 0; i < results.size(); ++i)
55 printf(
"Device #%d:\n", i);
56 for( it = results[i].begin(); it != results[i].end(); ++it)
58 printf(
"%s = %s\n", it->first.c_str(), it->second.c_str());
66 const char* write_sample_file) ->
bool {
67 if (sample_file !=
nullptr) {
68 if (0 == srsran_filesource_init(&file_source,
69 const_cast<char*
>(sample_file),
70 SRSRAN_COMPLEX_FLOAT_BIN)) {
71 _reading_from_file =
true;
73 spdlog::error(
"Could not open file {}", sample_file);
77 if (write_sample_file !=
nullptr) {
78 if (0 == srsran_filesink_init(&file_sink,
79 const_cast<char*
>(write_sample_file),
80 SRSRAN_COMPLEX_FLOAT_BIN)) {
81 _writing_to_file =
true;
83 spdlog::error(
"Could not open file {}", write_sample_file);
88 _device_args = SoapySDR::KwargsFromString(device_args);
89 _sdr = SoapySDR::Device::make(_device_args);
92 spdlog::error(
"SoapySDR: failed to open device with args {}", device_args);
97 _cfg.lookupValue(
"modem.sdr.ringbuffer_size_ms", _buffer_ms);
103 _buffer = std::make_unique<MultichannelRingbuffer>(
sizeof(cf_t) * buffer_size,
_rx_channels);
113 auto sdr = (SoapySDR::Device*)_sdr;
114 auto antenna_list = sdr->listAntennas(SOAPY_SDR_RX, idx);
115 if (std::find(antenna_list.begin(), antenna_list.end(),
antenna) != antenna_list.end()) {
116 sdr->setAntenna( SOAPY_SDR_RX, idx,
antenna);
117 _antenna = sdr->getAntenna( SOAPY_SDR_RX, idx);
120 spdlog::error(
"Unknown antenna \"{}\". Available: {}.",
antenna, boost::algorithm::join(antenna_list,
", ") );
126 auto sdr = (SoapySDR::Device*)_sdr;
127 sdr->setFrequency( SOAPY_SDR_RX, idx,
frequency);
132 auto sdr = (SoapySDR::Device*)_sdr;
133 sdr->setBandwidth( SOAPY_SDR_RX, idx,
bandwidth);
138 auto sdr = (SoapySDR::Device*)_sdr;
139 sdr->setSampleRate( SOAPY_SDR_RX, idx, rate);
144 auto sdr = (SoapySDR::Device*)_sdr;
145 if (sdr->hasGainMode(SOAPY_SDR_RX, idx)) {
147 sdr->setGainMode(SOAPY_SDR_RX, idx,
use_agc);
151 auto gain_range = sdr->getGainRange(SOAPY_SDR_RX, idx);
152 _min_gain = gain_range.minimum();
153 _max_gain = gain_range.maximum();
154 if (
gain >= gain_range.minimum() &&
gain <= gain_range.maximum()) {
155 sdr->setGain( SOAPY_SDR_RX, idx,
gain);
157 _gain = sdr->getGain( SOAPY_SDR_RX, idx);
161 spdlog::error(
"Invalid gain setting {}. Allowed range is: {} - {}.",
gain, gain_range.minimum(), gain_range.maximum());
175 if (_reading_from_file) {
179 if (_sdr ==
nullptr) {
183 spdlog::info(
"Tuning to {} MHz, filter bandwidth {} MHz, sample rate {}, gain {}, antenna path {} with AGC set to {}",
186 auto sdr = (SoapySDR::Device*)_sdr;
188 for (
auto ch = 0; ch < _rx_channels; ch++) {
190 set_gain(_use_agc,
gain, ch);
196 _frequency = sdr->getFrequency( SOAPY_SDR_RX, 0);
197 bandwidth = sdr->getBandwidth( SOAPY_SDR_RX, 0);
198 _sampleRate = sdr->getSampleRate( SOAPY_SDR_RX, 0);
200 spdlog::info(
"SDR tuned to {} MHz, filter bandwidth {} MHz, sample rate {}, gain {}, antenna path {}",
201 _frequency/1000000.0,
bandwidth/1000000.0, _sampleRate/1000000.0, _gain, _antenna);
204 auto sensors = sdr->listSensors();
205 if (std::find(sensors.begin(), sensors.end(),
"lms7_temp") != sensors.end()) {
206 _temp_sensor_available =
true;
207 _temp_sensor_key =
"lms7_temp";
214 if (
_sdr !=
nullptr) {
215 auto sdr = (SoapySDR::Device*)
_sdr;
223 spdlog::error(
"Failed to set up RX stream");
224 SoapySDR::Device::unmake( sdr );
227 sdr->activateStream( (SoapySDR::Stream*)
_stream, 0, 0, 0);
233 struct sched_param thread_param = {};
234 thread_param.sched_priority = 50;
235 _cfg.lookupValue(
"modem.sdr.reader_thread_priority_rt", thread_param.sched_priority);
237 spdlog::debug(
"Launching sample reader thread with realtime scheduling priority {}", thread_param.sched_priority);
239 int error = pthread_setschedparam(
_readerThread.native_handle(), SCHED_RR, &thread_param);
241 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));
248 if (
_sdr !=
nullptr) {
249 auto sdr = (SoapySDR::Device*)
_sdr;
250 sdr->deactivateStream((SoapySDR::Stream*)
_stream, 0, 0);
251 sdr->closeStream((SoapySDR::Stream*)
_stream);
259 std::array<void*, SRSRAN_MAX_CHANNELS> radio_buffers = {
nullptr };
263 if (
_buffer->free_size() < toRead *
sizeof(cf_t)) {
264 spdlog::debug(
"ringbuffer overflow");
265 std::this_thread::sleep_for(std::chrono::microseconds(1000));
268 size_t writeable = 0;
269 auto buffers =
_buffer->write_head(&writeable);
270 int writeable_samples = (int)floor(writeable /
sizeof(cf_t));
273 std::chrono::steady_clock::time_point entered = {};
274 entered = std::chrono::steady_clock::now();
287 std::chrono::microseconds sleep = (std::chrono::microseconds(required_time_us) -
288 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - entered));
289 std::this_thread::sleep_for(sleep);
291 auto sdr = (SoapySDR::Device*)
_sdr;
293 long long time_ns = 0;
295 read = sdr->readStream( (SoapySDR::Stream*)
_stream, buffers.data(), std::min(writeable_samples, toRead), flags, time_ns);
303 spdlog::debug(
"buffer: commited {}, requested {}, writeable {}, flags {}",
read, toRead, writeable_samples, flags);
306 spdlog::error(
"readStream returned {}",
read);
312 spdlog::debug(
"Sample reader thread exited");
318 std::chrono::steady_clock::time_point entered = {};
319 entered = std::chrono::steady_clock::now();
321 int64_t required_time_us = (1000000.0/_sampleRate) * nsamples;
322 size_t cnt = nsamples *
sizeof(cf_t);
324 if (_high_watermark_reached && _buffer->used_size() < (_sampleRate / 1000.0) * 10 *
sizeof(cf_t)) {
325 _high_watermark_reached =
false;
328 if (!_high_watermark_reached) {
329 while (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) *
sizeof(cf_t)) {
330 std::this_thread::sleep_for(std::chrono::microseconds(500));
332 spdlog::debug(
"Filled ringbuffer to half capacity");
333 _high_watermark_reached =
true;
336 std::vector<char*> buffers(_rx_channels);
337 for (
auto ch = 0; ch < _rx_channels; ch++) {
338 buffers[ch] = (
char*)data[ch];
340 _buffer->read(buffers, cnt);
342 if (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 4.0) *
sizeof(cf_t)) {
343 required_time_us += 500;
345 required_time_us -= 500;
348 spdlog::debug(
"took {}, read {} samples, adjusted required {} us, delta {} us, sleep adj {}, sleeping for {} us",
349 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - entered).count(),
351 std::chrono::microseconds(required_time_us).count(),
352 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read).count(),
354 (std::chrono::microseconds(_sleep_adjustment + required_time_us) - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - _last_read)).count());
356 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));
358 if (sleep.count() > 0) {
359 std::this_thread::sleep_for(sleep);
360 _sleep_adjustment = 0;
361 }
else if (sleep.count() > -100000) {
362 _sleep_adjustment = sleep.count();
365 _last_read = std::chrono::steady_clock::now();
371 if (!_buffer_ready) {
374 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)
Initializes the SDR interface and creates a ring buffer according to the params from Cfg.
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.