From f271d84f2066110bfa60b53e9e4ed01c29475e7e Mon Sep 17 00:00:00 2001 From: Klaus Kuehnhammer Date: Mon, 21 Feb 2022 11:43:22 +0100 Subject: [PATCH] Support for MIMO / dual RX streams from BladeRF (#26) - Replace existing Doxygen file in the root folder - Add workflows folder to the existing .github folder * Rel-9 / Mixed Mode baseline support * Update .gitmodules Changed repo name and URI to srsRAN * removed old subodule path * New path for submodule * Pass MCH idx through RLC to allow correct assignment of detected MCast addresses in gw * Rebased changes for rt-mbms-modem from srsLTE to srsRAN. Added spldlog as include-only to avoid a conflict with srsRAN's included libfmt. * Update README.md Remove dependency on system libspdlog * Support for MIMO / multiple RX channels. Also replaces the MMAPping ringbuffer implementation so that valgrind can now be used again. * Added new parameter for RX channel count, disabled measurement file creation and GPS integration in the default config file. --- CMakeLists.txt | 5 +- include/ring_buffer.h | 492 --------------------------------- lib/srsran | 2 +- src/CasFrameProcessor.cpp | 15 +- src/CasFrameProcessor.h | 11 +- src/MbsfnFrameProcessor.cpp | 12 +- src/MbsfnFrameProcessor.h | 14 +- src/MultichannelRingbuffer.cpp | 102 +++++++ src/MultichannelRingbuffer.h | 48 ++++ src/Phy.cpp | 20 +- src/Phy.h | 6 +- src/SdrReader.cpp | 181 +++++++----- src/SdrReader.h | 35 ++- src/main.cpp | 32 ++- supporting_files/5gmag-rt.conf | 5 +- 15 files changed, 358 insertions(+), 622 deletions(-) delete mode 100644 include/ring_buffer.h create mode 100644 src/MultichannelRingbuffer.cpp create mode 100644 src/MultichannelRingbuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c2e90..f3f9e37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ set(CMAKE_CXX_CLANG_TIDY clang-tidy --format-style=google --checks=clang-diagnos add_executable(modem src/main.cpp src/SdrReader.cpp src/Phy.cpp src/CasFrameProcessor.cpp src/MbsfnFrameProcessor.cpp src/Rrc.cpp - src/Gw.cpp src/RestHandler.cpp src/MeasurementFileWriter.cpp) + src/Gw.cpp src/RestHandler.cpp src/MeasurementFileWriter.cpp src/MultichannelRingbuffer.cpp) target_link_libraries( modem LINK_PUBLIC @@ -63,9 +63,6 @@ target_link_libraries( modem ssl crypto SoapySDR - - debug - lsan ) diff --git a/include/ring_buffer.h b/include/ring_buffer.h deleted file mode 100644 index b897c41..0000000 --- a/include/ring_buffer.h +++ /dev/null @@ -1,492 +0,0 @@ -// https://github.com/lava/linear_ringbuffer -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace bev { - -// # Linear Ringbuffer -// -// This is an implementation of a ringbuffer that will always expose its contents -// as a flat array using the mmap trick. It is mainly useful for interfacing with -// C APIs, where this feature can vastly simplify program logic by eliminating all -// special case handling when reading or writing data that wraps around the edge -// of the ringbuffer. -// -// -// # Data Layout -// -// From the outside, the ringbuffer contents always look like a flat array -// due to the mmap trick: -// -// -// (head) <-- size --> (tail) -// v v -// /-----------------------------------|-------------------------------\ -// | buffer area | mmapped clone of buffer area | -// \-----------------------------------|-------------------------------/ -// <---------- capacity -------------> -// -// -// --->(tail) (head) -----~~~~> -// v v -// /-----------------------------------|-------------------------------\ -// | buffer area | mmapped clone of buffer area | -// \-----------------------------------|-------------------------------/ -// -// -// # Usage -// -// The buffer provides two (pointer, length)-pairs that can be passed to C APIs, -// `(write_head(), free_size())` and `(read_head(), size())`. -// -// The general idea is to pass the appropriate one to a C function expecting -// a pointer and size, and to afterwards call `commit()` to adjust the write -// head or `consume()` to adjust the read head. -// -// Writing into the buffer: -// -// bev::linear_ringbuffer rb; -// FILE* f = fopen("input.dat", "r"); -// ssize_t n = ::read(fileno(f), rb.write_head(), rb.free_size()); -// rb.commit(n); -// -// Reading from the buffer: -// -// bev::linear_ringbuffer rb; -// FILE* f = fopen("output.dat", "w"); -// ssize_t n = ::write(fileno(f), rb.read_head(), rb.size(); -// rb.consume(n); -// -// If there are multiple readers/writers, it is the calling code's -// responsibility to ensure that the reads/writes and the calls to -// produce/consume appear atomic to the buffer, otherwise data loss -// can occur. -// -// # Errors and Exceptions -// -// The ringbuffer provides two way of initialization, one using exceptions -// and one using error codes. After initialization is completed, all -// operations on the buffer are `noexcept` and will never return an error. -// -// To use error codes, the `linear_ringbuffer(delayed_init {})` constructor. -// can be used. In this case, the internal buffers are not allocated until -// `linear_ringbuffer::initialize()` is called, and all other member function -// must not be called before the buffers have been initialized. -// -// bev::linear_ringbuffer rb(linear_ringbuffer::delayed_init {}); -// int error = rb.initialize(MIN_BUFSIZE); -// if (error) { -// [...] -// } -// -// The possible error codes returned by `initialize()` are: -// -// ENOMEM - The system ran out of memory, file descriptors, or the maximum -// number of mappings would have been exceeded. -// -// EINVAL - The `minsize` argument was 0, or 2*`minsize` did overflow. -// -// EAGAIN - Another thread allocated memory in the area that was intended -// to use for the second copy of the buffer. Callers are encouraged -// to try again. -// -// If exceptions are preferred, the `linear_ringbuffer(int minsize)` -// constructor will attempt to initialize the internal buffers immediately and -// throw a `bev::initialization_error` on failure, which is an exception class -// derived from `std::runtime_error`. The error code as described above is -// stored in the `errno_` member of the exception. -// -// -// # Concurrency -// -// It is safe to be use the buffer concurrently for a single reader and a -// single writer, but mutiple readers or multiple writers must serialize -// their accesses with a mutex. -// -// If the ring buffer is used in a single-threaded application, the -// `linear_ringbuffer_st` class can be used to avoid paying for atomic -// increases and decreases of the internal size. -// -// -// # Implementation Notes -// -// Note that only unsigned chars are allowed as the element type. While we could -// in principle add an arbitrary element type as an additional argument, there -// would be comparatively strict requirements: -// -// - It needs to be trivially relocatable -// - The size needs to exactly divide PAGE_SIZE -// -// Since the main use case is interfacing with C APIs, it seems more pragmatic -// to just let the caller cast their data to `void*` rather than supporting -// arbitrary element types. -// -// The initialization of the buffer is subject to failure, and sadly this cannot -// be avoided. [1] There are two sources of errors: -// -// 1) Resource exhaustion. The maximum amount of available memory, file -// descriptors, memory mappings etc. may be exceeded. This is similar to any -// other container type. -// -// 2) To allocate the ringbuffer storage, first a memory region twice the -// required size is mapped, then it is shrunk by half and a copy of the first -// half of the buffer is mapped into the (now empty) second half. -// If some other thread is creating its own mapping in the second half after -// the buffer has been shrunk but before the second half has been mapped, this -// will fail. To ensure success, allocate the buffers before branching into -// multi-threaded code. -// -// [1] Technically, we could use `MREMAP_FIXED` to enforce creation of the -// second buffer, but at the cost of potentially unmapping random mappings made -// by other threads, which seems much worse than just failing. I've spent some -// time scouring the manpages and implementation of `mmap()` for a technique to -// make it work atomically but came up empty. If there is one, please tell me. -// - -template -class linear_ringbuffer_ { -public: - typedef unsigned char value_type; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef value_type* iterator; - typedef const value_type* const_iterator; - typedef std::ptrdiff_t difference_type; - typedef std::size_t size_type; - - struct delayed_init {}; - - // "640KiB should be enough for everyone." - // - Not Bill Gates. - linear_ringbuffer_(size_t minsize = 640*1024); - ~linear_ringbuffer_(); - - // Noexcept initialization interface, see description above. - linear_ringbuffer_(const delayed_init) noexcept; - int initialize(size_t minsize) noexcept; - - void commit(size_t n) noexcept; - void consume(size_t n) noexcept; - iterator read_head() noexcept; - iterator write_head() noexcept; - void clear() noexcept; - - bool empty() const noexcept; - size_t size() const noexcept; - size_t capacity() const noexcept; - size_t free_size() const noexcept; - const_iterator begin() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator end() const noexcept; - const_iterator cend() const noexcept; - - // Plumbing - - linear_ringbuffer_(linear_ringbuffer_&& other) noexcept; - linear_ringbuffer_& operator=(linear_ringbuffer_&& other) noexcept; - void swap(linear_ringbuffer_& other) noexcept; - - linear_ringbuffer_(const linear_ringbuffer_&) = delete; - linear_ringbuffer_& operator=(const linear_ringbuffer_&) = delete; - -private: - unsigned char* buffer_; - size_t capacity_; - size_t head_; - size_t tail_; - Size size_; -}; - - -template -void swap( - linear_ringbuffer_& lhs, - linear_ringbuffer_& rhs) noexcept; - - -struct initialization_error : public std::runtime_error -{ - initialization_error(int error); - int error; -}; - - -using linear_ringbuffer_st = linear_ringbuffer_; -using linear_ringbuffer_mt = linear_ringbuffer_>; -using linear_ringbuffer = linear_ringbuffer_mt; - - -// Implementation. - -template -void linear_ringbuffer_::commit(size_t n) noexcept { - assert(n <= (capacity_-size_)); - tail_ = (tail_ + n) % capacity_; - size_ += n; -} - - -template -void linear_ringbuffer_::consume(size_t n) noexcept { - assert(n <= size_); - head_ = (head_ + n) % capacity_; - size_ -= n; -} - - -template -void linear_ringbuffer_::clear() noexcept { - tail_ = head_ = size_ = 0; -} - - -template -size_t linear_ringbuffer_::size() const noexcept { - return size_; -} - - -template -bool linear_ringbuffer_::empty() const noexcept { - return size_ == 0; -} - - -template -size_t linear_ringbuffer_::capacity() const noexcept { - return capacity_; -} - - -template -size_t linear_ringbuffer_::free_size() const noexcept { - return capacity_ - size_; -} - - -template -auto linear_ringbuffer_::cbegin() const noexcept -> const_iterator -{ - return buffer_ + head_; -} - - -template -auto linear_ringbuffer_::begin() const noexcept -> const_iterator -{ - return cbegin(); -} - - -template -auto linear_ringbuffer_::read_head() noexcept -> iterator -{ - return buffer_ + head_; -} - - -template -auto linear_ringbuffer_::cend() const noexcept -> const_iterator -{ - // Fix up `end` if needed so that [begin, end) is always a - // valid range. - return head_ < tail_ ? - buffer_ + tail_ : - buffer_ + tail_ + capacity_; -} - - -template -auto linear_ringbuffer_::end() const noexcept -> const_iterator -{ - return cend(); -} - - -template -auto linear_ringbuffer_::write_head() noexcept -> iterator -{ - return buffer_ + tail_; -} - - -template -linear_ringbuffer_::linear_ringbuffer_(const delayed_init) noexcept - : buffer_(nullptr) - , capacity_(0) - , head_(0) - , tail_(0) - , size_(0) -{} - - -template -linear_ringbuffer_::linear_ringbuffer_(size_t minsize) - : buffer_(nullptr) - , capacity_(0) - , head_(0) - , tail_(0) - , size_(0) -{ - int res = this->initialize(minsize); - if (res == -1) { - throw initialization_error {errno}; - } -} - - -template -linear_ringbuffer_::linear_ringbuffer_(linear_ringbuffer_&& other) noexcept -{ - linear_ringbuffer_ tmp(delayed_init {}); - tmp.swap(other); - this->swap(tmp); -} - - -template -auto linear_ringbuffer_::operator=(linear_ringbuffer_&& other) noexcept - -> linear_ringbuffer_& -{ - linear_ringbuffer_ tmp(delayed_init {}); - tmp.swap(other); - this->swap(tmp); - return *this; -} - - -template -int linear_ringbuffer_::initialize(size_t minsize) noexcept -{ -#ifdef PAGESIZE - static constexpr unsigned int PAGE_SIZE = PAGESIZE; -#else - static const unsigned int PAGE_SIZE = ::sysconf(_SC_PAGESIZE); -#endif - - // Use `char*` instead of `void*` because we need to do arithmetic on them. - unsigned char* addr =nullptr; - unsigned char* addr2=nullptr; - - // Technically, we could also report sucess here since a zero-length - // buffer can't be legally used anyways. - if (minsize == 0) { - errno = EINVAL; - return -1; - } - - // Round up to nearest multiple of page size. - int bytes = minsize & ~(PAGE_SIZE-1); - if (minsize % PAGE_SIZE) { - bytes += PAGE_SIZE; - } - - // Check for overflow. - if (bytes*2u < bytes) { - errno = EINVAL; - return -1; - } - - // Allocate twice the buffer size - addr = static_cast(::mmap(NULL, 2*bytes, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)); - - if (addr == MAP_FAILED) { - goto errout; - } - - // Shrink to actual buffer size. - addr = static_cast(::mremap(addr, 2*bytes, bytes, 0)); - if (addr == MAP_FAILED) { - goto errout; - } - - // Create the second copy right after the shrinked buffer. - addr2 = static_cast(::mremap(addr, 0, bytes, MREMAP_MAYMOVE, - addr+bytes)); - - if (addr2 == MAP_FAILED) { - goto errout; - } - - if (addr2 != addr+bytes) { - errno = EAGAIN; - goto errout; - } - - // Sanity check. - *(char*)addr = 'x'; - assert(*(char*)addr2 == 'x'); - - *(char*)addr2 = 'y'; - assert(*(char*)addr == 'y'); - - capacity_ = bytes; - buffer_ = addr; - - return 0; - -errout: - int error = errno; - // We actually have to check for non-null here, since even if `addr` is - // null, `bytes` might be large enough that this overlaps some actual - // mappings. - if (addr) { - ::munmap(addr, bytes); - } - if (addr2) { - ::munmap(addr2, bytes); - } - errno = error; - return -1; -} - - -template -linear_ringbuffer_::~linear_ringbuffer_() -{ - // Either `buffer_` and `capacity_` are both initialized properly, - // or both are zero. - ::munmap(buffer_, capacity_); - ::munmap(buffer_+capacity_, capacity_); -} - - -template -void linear_ringbuffer_::swap(linear_ringbuffer_& other) noexcept -{ - using std::swap; - swap(buffer_, other.buffer_); - swap(capacity_, other.capacity_); - swap(tail_, other.tail_); - swap(head_, other.head_); - swap(size_, other.size_); -} - - -template -void swap( - linear_ringbuffer_& lhs, - linear_ringbuffer_& rhs) noexcept -{ - lhs.swap(rhs); -} - - -inline initialization_error::initialization_error(int errno_) - : std::runtime_error(::strerror(errno_)) - , error(errno_) -{} - -} // namespace bev diff --git a/lib/srsran b/lib/srsran index ede768d..96e6590 160000 --- a/lib/srsran +++ b/lib/srsran @@ -1 +1 @@ -Subproject commit ede768de3da53549c246bbb18125602b7c264f2b +Subproject commit 96e6590e18d2af4abba37d8b2abad2e0445f7ed3 diff --git a/src/CasFrameProcessor.cpp b/src/CasFrameProcessor.cpp index 27c2b17..066a451 100644 --- a/src/CasFrameProcessor.cpp +++ b/src/CasFrameProcessor.cpp @@ -24,13 +24,15 @@ auto CasFrameProcessor::init() -> bool { _signal_buffer_max_samples = 3 * SRSRAN_SF_LEN_PRB(MAX_PRB); - _signal_buffer_rx[0] = srsran_vec_cf_malloc(_signal_buffer_max_samples); - if (!_signal_buffer_rx[0]) { - spdlog::error("Could not allocate regular DL signal buffer\n"); - return false; + for (auto ch = 0; ch < _rx_channels; ch++) { + _signal_buffer_rx[ch] = srsran_vec_cf_malloc(_signal_buffer_max_samples); + if (!_signal_buffer_rx[ch]) { + spdlog::error("Could not allocate regular DL signal buffer\n"); + return false; + } } - if (srsran_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, 1)) { + if (srsran_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, _rx_channels)) { spdlog::error("Could not init ue_dl\n"); return false;; } @@ -139,9 +141,10 @@ auto CasFrameProcessor::process(uint32_t tti) -> bool { } } + _rest._pdsch.SetData(pdsch_data()); + // Decode PDSCH.. auto ret = srsran_ue_dl_decode_pdsch(&_ue_dl, &_sf_cfg, &_ue_dl_cfg.cfg.pdsch, pdsch_res); - spdlog::debug("decode_pdsch returned {} \n", ret); if (ret) { spdlog::error("Error decoding PDSCH\n"); _rest._pdsch.errors++; diff --git a/src/CasFrameProcessor.h b/src/CasFrameProcessor.h index 3b64a31..d5eddbb 100644 --- a/src/CasFrameProcessor.h +++ b/src/CasFrameProcessor.h @@ -44,11 +44,13 @@ class CasFrameProcessor { * @param rlc RLC reference * @param rest RESTful API handler reference */ - CasFrameProcessor(const libconfig::Config& cfg, Phy& phy, srsran::rlc& rlc, RestHandler& rest) + CasFrameProcessor(const libconfig::Config& cfg, Phy& phy, srsran::rlc& rlc, RestHandler& rest, unsigned rx_channels) : _cfg(cfg) - , _phy(phy) - , _rest(rest) - , _rlc(rlc) {} + , _phy(phy) + , _rest(rest) + , _rlc(rlc) + , _rx_channels(rx_channels) + {} /** * Default destructor. @@ -127,4 +129,5 @@ class CasFrameProcessor { srsran_cell_t _cell; std::mutex _mutex; + unsigned _rx_channels; }; diff --git a/src/MbsfnFrameProcessor.cpp b/src/MbsfnFrameProcessor.cpp index 72bc26b..d1094bc 100644 --- a/src/MbsfnFrameProcessor.cpp +++ b/src/MbsfnFrameProcessor.cpp @@ -28,13 +28,15 @@ std::mutex MbsfnFrameProcessor::_rlc_mutex; auto MbsfnFrameProcessor::init() -> bool { _signal_buffer_max_samples = 3 * SRSRAN_SF_LEN_PRB(MAX_PRB); - _signal_buffer_rx[0] = srsran_vec_cf_malloc(_signal_buffer_max_samples); - if (_signal_buffer_rx[0] == nullptr) { - spdlog::error("Could not allocate regular DL signal buffer\n"); - return false; + for (auto ch = 0; ch < _rx_channels; ch++) { + _signal_buffer_rx[ch] = srsran_vec_cf_malloc(_signal_buffer_max_samples); + if (!_signal_buffer_rx[ch]) { + spdlog::error("Could not allocate regular DL signal buffer\n"); + return false; + } } - if (srsran_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, 1) != 0) { + if (srsran_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, _rx_channels) != 0) { spdlog::error("Could not init ue_dl\n"); return false;; } diff --git a/src/MbsfnFrameProcessor.h b/src/MbsfnFrameProcessor.h index 235b351..76c4ab7 100644 --- a/src/MbsfnFrameProcessor.h +++ b/src/MbsfnFrameProcessor.h @@ -47,12 +47,14 @@ class MbsfnFrameProcessor { * @param log_h srsLTE log handle for the MCH MAC msg decoder * @param rest RESTful API handler reference */ - MbsfnFrameProcessor(const libconfig::Config& cfg, srsran::rlc& rlc, Phy& phy, srslog::basic_logger& log_h, RestHandler& rest ) + MbsfnFrameProcessor(const libconfig::Config& cfg, srsran::rlc& rlc, Phy& phy, srslog::basic_logger& log_h, RestHandler& rest, unsigned rx_channels ) : _cfg(cfg) - , _rlc(rlc) - , _phy(phy) - , _rest(rest) - , mch_mac_msg(20, log_h) {} + , _rlc(rlc) + , _phy(phy) + , _rest(rest) + , mch_mac_msg(20, log_h) + , _rx_channels(rx_channels) + {} /** * Default destructor. @@ -149,6 +151,8 @@ class MbsfnFrameProcessor { RestHandler& _rest; + unsigned _rx_channels; + static std::mutex _sched_stop_mutex; static std::map _sched_stops; diff --git a/src/MultichannelRingbuffer.cpp b/src/MultichannelRingbuffer.cpp new file mode 100644 index 0000000..f0601d0 --- /dev/null +++ b/src/MultichannelRingbuffer.cpp @@ -0,0 +1,102 @@ +// 5G-MAG Reference Tools +// MBMS Modem Process +// +// Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +#include "MultichannelRingbuffer.h" + +#include +#include "spdlog/spdlog.h" + +MultichannelRingbuffer::MultichannelRingbuffer(size_t size, size_t channels) + : _size( size ) + , _channels( channels ) + , _used( 0 ) + , _head( 0 ) +{ + for (auto ch = 0; ch < _channels; ch++) { + auto buf = (char*)malloc(_size); + if (buf == nullptr) { + throw "Could not allocate memory"; + } + _buffers.push_back(buf); + } + spdlog::debug("Created {}-channel ringbuffer with size {}", _channels, _size ); +} + +MultichannelRingbuffer::~MultichannelRingbuffer() +{ + for (auto buffer : _buffers) { + if (buffer) free(buffer); + } +} + +auto MultichannelRingbuffer::write_head(size_t* writeable) -> std::vector +{ +// _mutex.lock(); + std::lock_guard lock(_mutex); + std::vector buffers(_channels, nullptr); + if (_size == _used) { + *writeable = 0; + } else { + auto tail = (_head + _used) % _size; + if (tail < _head) { + *writeable = _head - tail; + } else { + *writeable = _size - tail; + } + for (auto ch = 0; ch < _channels; ch++) { + buffers[ch] = (void*)(_buffers[ch] + tail); + } + } + + return buffers; +} + +auto MultichannelRingbuffer::commit(size_t written) -> void +{ + assert(written >= 0); + assert(written <= free_size()); + std::lock_guard lock(_mutex); + _used += written; +// _mutex.unlock(); +} + +auto MultichannelRingbuffer::read(std::vector dest, size_t size) -> void +{ + assert(dest.size() >= _channels); + assert(size <= used_size()); + assert(size >= 0); + + std::lock_guard lock(_mutex); + auto end = (_head + size) % _size; + + if (end <= _head) { + auto first_part = _size - _head; + auto second_part = size - first_part; + for (auto ch = 0; ch < _channels; ch++) { + memcpy(dest[ch], _buffers[ch] + _head, first_part); + memcpy(dest[ch] + first_part, _buffers[ch], second_part); + } + } else { + for (auto ch = 0; ch < _channels; ch++) { + memcpy(dest[ch], _buffers[ch] + _head, size); + } + } + _head = (_head + size) % _size; + _used -= size; +} diff --git a/src/MultichannelRingbuffer.h b/src/MultichannelRingbuffer.h new file mode 100644 index 0000000..79abc84 --- /dev/null +++ b/src/MultichannelRingbuffer.h @@ -0,0 +1,48 @@ +// 5G-MAG Reference Tools +// MBMS Modem Process +// +// Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +#pragma once +#include +#include +#include + +class MultichannelRingbuffer { + public: + explicit MultichannelRingbuffer(size_t size, size_t channels); + virtual ~MultichannelRingbuffer(); + + inline size_t free_size() { std::lock_guard lock(_mutex); return _size - _used; } + inline size_t used_size() { std::lock_guard lock(_mutex); return _used; } + inline size_t capacity() { std::lock_guard lock(_mutex);return _size; } + + inline void clear() {std::lock_guard lock(_mutex); _head = _used = 0; }; + + std::vector write_head(size_t* writeable); + void commit(size_t written); + + void read(std::vector dest, size_t bytes); + + private: + std::vector _buffers; + size_t _size; + size_t _channels; + size_t _head; + size_t _used; + std::mutex _mutex; +}; diff --git a/src/Phy.cpp b/src/Phy.cpp index 03c5bfa..b412042 100644 --- a/src/Phy.cpp +++ b/src/Phy.cpp @@ -29,7 +29,7 @@ static auto receive_callback(void* obj, cf_t* data[SRSRAN_MAX_CHANNELS], // NOLINT uint32_t nsamples, srsran_timestamp_t* rx_time) -> int { - return (static_cast(obj))->_sample_cb(data[0], nsamples, rx_time); // NOLINT + return (static_cast(obj))->_sample_cb(data, nsamples, rx_time); // NOLINT } const uint32_t kMaxBufferSamples = 2 * 15360; @@ -40,13 +40,15 @@ const uint32_t kSubframesPerFrame = 10; const uint32_t kMaxCellsToDiscover = 3; Phy::Phy(const libconfig::Config& cfg, get_samples_t cb, uint8_t cs_nof_prb, - int8_t override_nof_prb) + int8_t override_nof_prb, uint8_t rx_channels) : _cfg(cfg), _sample_cb(std::move(std::move(cb))), _cs_nof_prb(cs_nof_prb), - _override_nof_prb(override_nof_prb) { + _override_nof_prb(override_nof_prb), + _rx_channels(rx_channels) { _buffer_max_samples = kMaxBufferSamples; _mib_buffer[0] = static_cast(malloc(_buffer_max_samples * sizeof(cf_t))); // NOLINT + _mib_buffer[1] = static_cast(malloc(_buffer_max_samples * sizeof(cf_t))); // NOLINT } Phy::~Phy() { @@ -188,26 +190,26 @@ auto Phy::set_cell() -> void { } auto Phy::init() -> bool { - if (srsran_ue_cellsearch_init_multi_prb_cp(&_cell_search, 8, receive_callback, 1, - this, _cs_nof_prb, true) != 0) { + if (srsran_ue_cellsearch_init_multi_prb_cp(&_cell_search, 8, receive_callback, _rx_channels, + this, _cs_nof_prb, _search_extended_cp) != 0) { spdlog::error("Phy: error while initiating UE cell search\n"); return false; } srsran_ue_cellsearch_set_nof_valid_frames(&_cell_search, 4); - if (srsran_ue_sync_init_multi(&_ue_sync, MAX_PRB, false, receive_callback, 1, + if (srsran_ue_sync_init_multi(&_ue_sync, MAX_PRB, false, receive_callback, _rx_channels, this) != 0) { spdlog::error("Cannot init ue_sync"); return false; } - if (srsran_ue_mib_sync_init_multi_prb(&_mib_sync, receive_callback, 1, this, + if (srsran_ue_mib_sync_init_multi_prb(&_mib_sync, receive_callback, _rx_channels, this, _cs_nof_prb) != 0) { spdlog::error("Cannot init ue_mib_sync"); return false; } - if (srsran_ue_mib_init(&_mib, _mib_buffer[0], 50) != 0) { + if (srsran_ue_mib_init(&_mib, _mib_buffer[0], MAX_PRB) != 0) { spdlog::error("Cannot init ue_mib"); return false; } @@ -304,8 +306,8 @@ auto Phy::is_cas_subframe(unsigned tti) -> bool // This is subframe 0 in a radio frame divisible by 4, and hence a CAS frame. return tti%40 == 0; } else { + unsigned sfn = tti / 10; return (tti%10 == 0 || tti%10 == 5); - //if (sfn%8 == 0 && (tti%10 == 0 || tti%10 == 5)) { } } diff --git a/src/Phy.h b/src/Phy.h index 4bd2338..3d7614d 100644 --- a/src/Phy.h +++ b/src/Phy.h @@ -44,7 +44,7 @@ class Phy { /** * Definition of the callback function used to fetch samples from the SDR */ - typedef std::function get_samples_t; + typedef std::function get_samples_t; /** * Default constructor. @@ -54,7 +54,7 @@ class Phy { * @param cs_nof_prb Nr of PRBs to use during cell search * @param override_nof_prb If set, overrides the nof PRB received in the MIB */ - Phy(const libconfig::Config& cfg, get_samples_t cb, uint8_t cs_nof_prb, int8_t override_nof_prb); + Phy(const libconfig::Config& cfg, get_samples_t cb, uint8_t cs_nof_prb, int8_t override_nof_prb, uint8_t rx_channels); /** * Default destructor. @@ -239,4 +239,6 @@ class Phy { std::map< uint32_t, std::map< int, std::string >> _dests; int8_t _override_nof_prb; + uint8_t _rx_channels; + bool _search_extended_cp = true; }; diff --git a/src/SdrReader.cpp b/src/SdrReader.cpp index 3a16318..cd04a6b 100644 --- a/src/SdrReader.cpp +++ b/src/SdrReader.cpp @@ -85,8 +85,8 @@ auto SdrReader::init(const std::string& device_args, const char* sample_file, } } - auto args = SoapySDR::KwargsFromString(device_args); - _sdr = SoapySDR::Device::make(args); + _device_args = SoapySDR::KwargsFromString(device_args); + _sdr = SoapySDR::Device::make(_device_args); if (_sdr == nullptr) { spdlog::error("SoapySDR: failed to open device with args {}", device_args); @@ -95,37 +95,83 @@ auto SdrReader::init(const std::string& device_args, const char* sample_file, } _cfg.lookupValue("modem.sdr.ringbuffer_size_ms", _buffer_ms); - unsigned int buffer_size = 16384/*15360*/ * _buffer_ms; - // at 10MHz BW / 15.36Mhz sample rate - int error = 0; - error = _buffer.initialize(sizeof(cf_t) * buffer_size); - if (error != 0) { - spdlog::error("Cannot allocate ringbuffer: {}", errno); - return false; - } - _buffer_ready = true; return true; } +void SdrReader::init_buffer() { + auto buffer_size = (unsigned int)ceil(_sampleRate/1000.0 * _buffer_ms); + _buffer = std::make_unique(sizeof(cf_t) * buffer_size, _rx_channels); + _buffer_ready = true; +} + void SdrReader::clear_buffer() { - _buffer.clear(); + _buffer->clear(); _high_watermark_reached = false; } -auto SdrReader::setSampleRate(unsigned /*sample_rate*/) -> bool { - return _sdr != nullptr; +auto SdrReader::set_antenna(const std::string& antenna, uint8_t idx) -> bool { + auto sdr = (SoapySDR::Device*)_sdr; + auto antenna_list = sdr->listAntennas(SOAPY_SDR_RX, idx); + if (std::find(antenna_list.begin(), antenna_list.end(), antenna) != antenna_list.end()) { + sdr->setAntenna( SOAPY_SDR_RX, idx, antenna); + _antenna = sdr->getAntenna( SOAPY_SDR_RX, idx); + return true; + } else { + spdlog::error("Unknown antenna \"{}\". Available: {}.", antenna, boost::algorithm::join(antenna_list, ", ") ); + return false; + } +} + +auto SdrReader::set_frequency(uint32_t frequency, uint8_t idx) -> bool { + auto sdr = (SoapySDR::Device*)_sdr; + sdr->setFrequency( SOAPY_SDR_RX, idx, frequency); + return true; +} + +auto SdrReader::set_filter_bw(uint32_t bandwidth, uint8_t idx) -> bool { + auto sdr = (SoapySDR::Device*)_sdr; + sdr->setBandwidth( SOAPY_SDR_RX, idx, bandwidth); + return true; +} + +auto SdrReader::set_sample_rate(uint32_t rate, uint8_t idx) -> bool { + auto sdr = (SoapySDR::Device*)_sdr; + sdr->setSampleRate( SOAPY_SDR_RX, idx, rate); + return true; +} + +auto SdrReader::set_gain(bool use_agc, double gain, uint8_t idx) -> bool { + auto sdr = (SoapySDR::Device*)_sdr; + if (sdr->hasGainMode(SOAPY_SDR_RX, idx)) { +// spdlog::info("{} AGC", use_agc ? "Enabling" : "Disabling"); + sdr->setGainMode(SOAPY_SDR_RX, idx, use_agc); + } else if (use_agc) { +// spdlog::info("AGC is not supported by this device, please set gain manually"); + } + auto gain_range = sdr->getGainRange(SOAPY_SDR_RX, idx); + _min_gain = gain_range.minimum(); + _max_gain = gain_range.maximum(); + if (gain >= gain_range.minimum() && gain <= gain_range.maximum()) { + sdr->setGain( SOAPY_SDR_RX, idx, gain); + if (idx == 0) { + _gain = sdr->getGain( SOAPY_SDR_RX, idx); + } + return true; + } else { + spdlog::error("Invalid gain setting {}. Allowed range is: {} - {}.", gain, gain_range.minimum(), gain_range.maximum()); + return false; + } } auto SdrReader::tune(uint32_t frequency, uint32_t sample_rate, uint32_t bandwidth, double gain, const std::string& antenna) -> bool { _frequency = frequency; _filterBw = bandwidth; + _sampleRate = sample_rate; - _buffer.clear(); - _high_watermark_reached = false; + init_buffer(); if (_reading_from_file) { - _sampleRate = sample_rate; return true; } @@ -138,34 +184,14 @@ auto SdrReader::tune(uint32_t frequency, uint32_t sample_rate, auto sdr = (SoapySDR::Device*)_sdr; - auto antenna_list = sdr->listAntennas(SOAPY_SDR_RX, 0); - if (std::find(antenna_list.begin(), antenna_list.end(), antenna) != antenna_list.end()) { - sdr->setAntenna( SOAPY_SDR_RX, 0, antenna); - _antenna = sdr->getAntenna( SOAPY_SDR_RX, 0); - } else { - spdlog::error("Unknown antenna \"{}\". Available: {}.", antenna, boost::algorithm::join(antenna_list, ", ") ); - return false; + for (auto ch = 0; ch < _rx_channels; ch++) { + set_antenna(antenna, ch); + set_gain(_use_agc, gain, ch); + set_frequency(frequency, ch); + set_filter_bw(bandwidth, ch); + set_sample_rate(sample_rate, ch); } - if (sdr->hasGainMode(SOAPY_SDR_RX, 0)) { - spdlog::info("Disabling AGC"); - sdr->setGainMode(SOAPY_SDR_RX, 0, false); - } - auto gain_range = sdr->getGainRange(SOAPY_SDR_RX, 0); - _min_gain = gain_range.minimum(); - _max_gain = gain_range.maximum(); - if (gain >= gain_range.minimum() && gain <= gain_range.maximum()) { - sdr->setGain( SOAPY_SDR_RX, 0, gain); - _gain = sdr->getGain( SOAPY_SDR_RX, 0); - } else { - spdlog::error("Invalid gain setting {}. Allowed range is: {} - {}.", gain, gain_range.minimum(), gain_range.maximum()); - return false; - } - - sdr->setFrequency( SOAPY_SDR_RX, 0, frequency); - sdr->setBandwidth( SOAPY_SDR_RX, 0, bandwidth); - sdr->setSampleRate( SOAPY_SDR_RX, 0, sample_rate); - _frequency = sdr->getFrequency( SOAPY_SDR_RX, 0); bandwidth = sdr->getBandwidth( SOAPY_SDR_RX, 0); _sampleRate = sdr->getSampleRate( SOAPY_SDR_RX, 0); @@ -186,7 +212,11 @@ auto SdrReader::tune(uint32_t frequency, uint32_t sample_rate, void SdrReader::start() { if (_sdr != nullptr) { auto sdr = (SoapySDR::Device*)_sdr; - _stream = sdr->setupStream( SOAPY_SDR_RX, SOAPY_SDR_CF32); + std::vector channels(_rx_channels); + for (auto ch = 0; ch < _rx_channels; ch++) { + channels[ch] = ch; + } + _stream = sdr->setupStream( SOAPY_SDR_RX, SOAPY_SDR_CF32, channels, _device_args); if( _stream == nullptr) { spdlog::error("Failed to set up RX stream"); @@ -221,31 +251,36 @@ void SdrReader::stop() { } _readerThread.join(); - _buffer.clear(); + clear_buffer(); } void SdrReader::read() { std::array radio_buffers = { nullptr }; while (_running) { int toRead = ceil(_sampleRate / 1000.0); - if (_buffer.free_size() < toRead * sizeof(cf_t)) { + //int toRead = 254; + if (_buffer->free_size() < toRead * sizeof(cf_t)) { spdlog::debug("ringbuffer overflow"); std::this_thread::sleep_for(std::chrono::microseconds(1000)); } else { int read = 0; + size_t writeable = 0; + auto buffers = _buffer->write_head(&writeable); + int writeable_samples = (int)floor(writeable / sizeof(cf_t)); + if (_reading_from_file) { std::chrono::steady_clock::time_point entered = {}; entered = std::chrono::steady_clock::now(); - int64_t required_time_us = (1000000.0/_sampleRate) * toRead; - radio_buffers[0] = _buffer.write_head(); - read = srsran_filesource_read_multi(&file_source, radio_buffers.data(), toRead, 1); + read = srsran_filesource_read_multi(&file_source, buffers.data(), std::min(writeable_samples, toRead), (int)_rx_channels); if ( read == 0 ) { srsran_filesource_seek(&file_source, 0); } + read = read / _rx_channels; + int64_t required_time_us = (1000000.0/_sampleRate) * read; - if (read> 0) { - _buffer.commit( read * sizeof(cf_t) ); + if (read > 0) { + _buffer->commit( read * sizeof(cf_t) ); } std::chrono::microseconds sleep = (std::chrono::microseconds(required_time_us) - @@ -253,17 +288,22 @@ void SdrReader::read() { std::this_thread::sleep_for(sleep); } else { auto sdr = (SoapySDR::Device*)_sdr; - int flags; - long long time_ns; - void *buffs[] = {_buffer.write_head()}; // NOLINT + int flags = 0; + long long time_ns = 0; + + read = sdr->readStream( (SoapySDR::Stream*)_stream, buffers.data(), std::min(writeable_samples, toRead), flags, time_ns); - read = sdr->readStream( (SoapySDR::Stream*)_stream, buffs, toRead, flags, time_ns); if (read> 0) { if (_writing_to_file && _write_samples) { - srsran_filesink_write(&file_sink, _buffer.write_head(), read); + srsran_filesink_write_multi(&file_sink, buffers.data(), read, (int)_rx_channels); } - _buffer.commit( read * sizeof(cf_t) ); + _buffer->commit( read * sizeof(cf_t) ); + spdlog::debug("buffer: commited {}, requested {}, writeable {}, flags {}", read, toRead, writeable_samples, flags); + } + else { + spdlog::error("readStream returned {}", read); + _buffer->commit(0); } } } @@ -271,7 +311,7 @@ void SdrReader::read() { spdlog::debug("Sample reader thread exited"); } -auto SdrReader::getSamples(cf_t* data, uint32_t nsamples, +auto SdrReader::get_samples(cf_t* data[SRSRAN_MAX_CHANNELS], uint32_t nsamples, //NOLINT srsran_timestamp_t * /*rx_time*/) -> int { std::chrono::steady_clock::time_point entered = {}; @@ -280,29 +320,32 @@ auto SdrReader::getSamples(cf_t* data, uint32_t nsamples, int64_t required_time_us = (1000000.0/_sampleRate) * nsamples; size_t cnt = nsamples * sizeof(cf_t); - if (_high_watermark_reached && _buffer.size() < (_sampleRate / 1000.0) * 10 * sizeof(cf_t)) { + if (_high_watermark_reached && _buffer->used_size() < (_sampleRate / 1000.0) * 10 * sizeof(cf_t)) { _high_watermark_reached = false; } if (!_high_watermark_reached) { - spdlog::debug("size {}", _buffer.size() ); - while (_buffer.size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) * sizeof(cf_t)) { + while (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) * sizeof(cf_t)) { std::this_thread::sleep_for(std::chrono::microseconds(500)); } spdlog::debug("Filled ringbuffer to half capacity"); _high_watermark_reached = true; } - memcpy(data, _buffer.read_head(), cnt); - _buffer.consume(cnt); + std::vector buffers(_rx_channels); + for (auto ch = 0; ch < _rx_channels; ch++) { + buffers[ch] = (char*)data[ch]; + } + _buffer->read(buffers, cnt); - if (_buffer.size() < (_sampleRate / 1000.0) * (_buffer_ms / 2.0) * sizeof(cf_t)) { + if (_buffer->used_size() < (_sampleRate / 1000.0) * (_buffer_ms / 4.0) * sizeof(cf_t)) { required_time_us += 500; } else { required_time_us -= 500; } - spdlog::debug("read {} samples, adjusted required {} us, delta {} us, sleep adj {}, sleeping for {} us", + spdlog::debug("took {}, read {} samples, adjusted required {} us, delta {} us, sleep adj {}, sleeping for {} us", + std::chrono::duration_cast(std::chrono::steady_clock::now() - entered).count(), nsamples, std::chrono::microseconds(required_time_us).count(), std::chrono::duration_cast(std::chrono::steady_clock::now() - _last_read).count(), @@ -321,3 +364,11 @@ auto SdrReader::getSamples(cf_t* data, uint32_t nsamples, _last_read = std::chrono::steady_clock::now(); return 0; } + +auto SdrReader::get_buffer_level() -> double +{ + if (!_buffer_ready) { + return 0; + } + return static_cast(_buffer->used_size()) / static_cast(_buffer->capacity()); +} diff --git a/src/SdrReader.h b/src/SdrReader.h index 98de6a7..7bba121 100644 --- a/src/SdrReader.h +++ b/src/SdrReader.h @@ -19,13 +19,14 @@ #pragma once - -#include #include +#include #include +#include #include #include #include "srsran/srsran.h" +#include "MultichannelRingbuffer.h" /** * Interface to the SDR stick. @@ -40,11 +41,11 @@ class SdrReader { * * @param cfg Config singleton reference */ - explicit SdrReader(const libconfig::Config& cfg) - : _buffer(bev::linear_ringbuffer::delayed_init {}) - , _overflows(0) + explicit SdrReader(const libconfig::Config& cfg, size_t rx_channels) + : _overflows(0) , _underflows(0) , _cfg(cfg) + , _rx_channels(rx_channels) , _readerThread{} {} /** @@ -62,11 +63,6 @@ class SdrReader { */ bool init(const std::string& device_args, const char* sample_file, const char* write_sample_file); - /** - * Adjust the sample rate - */ - bool setSampleRate(unsigned sample_rate); - /** * Tune the SDR to the desired frequency, and set gain, filter and antenna parameters. */ @@ -94,7 +90,7 @@ class SdrReader { * @param nsamples sample count * @param rx_time unused */ - int getSamples(cf_t* data, uint32_t nsamples, srsran_timestamp_t* rx_time); + int get_samples(cf_t* data[SRSRAN_MAX_CHANNELS], uint32_t nsamples, srsran_timestamp_t* rx_time); /** * Get current sample rate @@ -119,7 +115,7 @@ class SdrReader { /** * Get current ringbuffer level (0 = empty .. 1 = full) */ - double get_buffer_level() { if (!_buffer_ready) { return 0; } return static_cast(_buffer.size()) / static_cast(_buffer.capacity()); } + double get_buffer_level(); /** * Get current antenna port @@ -145,15 +141,24 @@ class SdrReader { void disableSampleFileWriting() { _write_samples = false; } private: + void init_buffer(); + bool set_gain(bool use_agc, double gain, uint8_t idx); + bool set_sample_rate(uint32_t rate, uint8_t idx); + bool set_filter_bw(uint32_t bandwidth, uint8_t idx); + bool set_antenna(const std::string& antenna, uint8_t idx); + bool set_frequency(uint32_t frequency, uint8_t idx); void read(); void* _sdr = nullptr; void* _stream = nullptr; const libconfig::Config& _cfg; - bev::linear_ringbuffer _buffer; + + std::unique_ptr _buffer; + std::thread _readerThread; bool _running; + unsigned _rx_channels = 1; double _sampleRate; double _frequency; unsigned _filterBw; @@ -184,4 +189,8 @@ class SdrReader { bool _temp_sensor_available = false; std::string _temp_sensor_key = {}; + + std::map _device_args; + + bool _use_agc = false; }; diff --git a/src/main.cpp b/src/main.cpp index 25ae665..5f9e1d0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -245,8 +245,10 @@ auto main(int argc, char **argv) -> int { spdlog::info("5g-mag-rt modem v{}.{}.{} starting up", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); // Init and tune the SDR - spdlog::info("Initialising SDR"); - SdrReader sdr(cfg); + auto rx_channels = 1; + cfg.lookupValue("modem.sdr.rx_channels", rx_channels); + spdlog::info("Initialising SDR with {} RX channel(s)", rx_channels); + SdrReader sdr(cfg, rx_channels); if (arguments.list_sdr_devices) { sdr.enumerateDevices(); exit(0); @@ -315,9 +317,10 @@ auto main(int argc, char **argv) -> int { // Create the layer components: Phy, RLC, RRC and GW Phy phy( cfg, - std::bind(&SdrReader::getSamples, &sdr, _1, _2, _3), // NOLINT + std::bind(&SdrReader::get_samples, &sdr, _1, _2, _3), // NOLINT arguments.file_bw ? arguments.file_bw * 5 : 25, - arguments.override_nof_prb); + arguments.override_nof_prb, + rx_channels); phy.init(); @@ -361,7 +364,7 @@ auto main(int argc, char **argv) -> int { RestHandler rest_handler(cfg, uri, state, sdr, phy, set_params); // Initialize one CAS and thered_cnt MBSFN frame processors - CasFrameProcessor cas_processor(cfg, phy, rlc, rest_handler); + CasFrameProcessor cas_processor(cfg, phy, rlc, rest_handler, rx_channels); if (!cas_processor.init()) { spdlog::error("Failed to create CAS processor. Exiting."); exit(1); @@ -369,7 +372,7 @@ auto main(int argc, char **argv) -> int { std::vector mbsfn_processors; for (int i = 0; i < thread_cnt; i++) { - auto p = new MbsfnFrameProcessor(cfg, rlc, phy, mac_log, rest_handler); + auto p = new MbsfnFrameProcessor(cfg, rlc, phy, mac_log, rest_handler, rx_channels); if (!p->init()) { spdlog::error("Failed to create MBSFN processor. Exiting."); exit(1); @@ -428,6 +431,8 @@ auto main(int argc, char **argv) -> int { bandwidth = (cas_nof_prb * 200000) * 1.2; sdr.tune(frequency, new_srate, bandwidth, gain, antenna); + + sdr.start(); } spdlog::debug("Synchronizing subframe"); @@ -457,7 +462,7 @@ auto main(int argc, char **argv) -> int { // We're locked on to the cell, and have succesfully received the MIB at the target sample rate. spdlog::info("Decoded MIB at target sample rate, TTI is {}. Subframe synchronized.", phy.tti()); - // Set the cell parameters in the CAS and MBSFN processors + // Set the cell parameters in the CAS processor cas_processor.set_cell(phy.cell()); for (int i = 0; i < thread_cnt; i++) { @@ -485,14 +490,13 @@ auto main(int argc, char **argv) -> int { // on a thread from the pool. if (!restart && phy.get_next_frame(cas_processor.rx_buffer(), cas_processor.rx_buffer_size())) { spdlog::debug("sending tti {} to regular processor", tti); - pool.push([ObjectPtr = &cas_processor, tti] { - ObjectPtr->process(tti); + pool.push([ObjectPtr = &cas_processor, tti, &rest_handler] { + if (ObjectPtr->process(tti)) { + // Set constellation diagram data and rx params for CAS in the REST API handler + rest_handler.add_cinr_value(ObjectPtr->cinr_db()); + } }); - // Set constellation diagram data and rx params for CAS in the REST API handler - rest_handler._ce_values = std::move(cas_processor.ce_values()); - rest_handler._pdsch.SetData(cas_processor.pdsch_data()); - rest_handler.add_cinr_value(cas_processor.cinr_db()); if (phy.nof_mbsfn_prb() != mbsfn_nof_prb) { @@ -509,13 +513,13 @@ auto main(int argc, char **argv) -> int { bandwidth = (mbsfn_nof_prb * 200000) * 1.2; sdr.tune(frequency, new_srate, bandwidth, gain, antenna); - sdr.start(); // ... configure the PHY and CAS processor to decode a narrow CAS and wider MBSFN, and move back to syncing state // after reconfiguring and restarting the SDR. phy.set_cell(); cas_processor.set_cell(phy.cell()); + sdr.start(); spdlog::info("Synchronizing subframe after PRB extension"); state = syncing; } diff --git a/supporting_files/5gmag-rt.conf b/supporting_files/5gmag-rt.conf index 3a13cba..e3c8086 100644 --- a/supporting_files/5gmag-rt.conf +++ b/supporting_files/5gmag-rt.conf @@ -7,6 +7,7 @@ modem: { normalized_gain = 40.0; device_args = "driver=lime"; antenna = "LNAW"; + rx_channels = 1; ringbuffer_size_ms = 200; reader_thread_priority_rt = 50; @@ -30,12 +31,12 @@ modem: { } measurement_file: { - enabled: true; + enabled: false; file_path: "/tmp/modem_measurements.csv"; interval_secs: 10; gpsd: { - enabled: true; + enabled: false; host: "localhost"; port: "2947"; }