OBECA Receive Process
MbsfnFrameProcessor.cpp
1 // OBECA - Open Broadcast Edge Cache Appliance
2 // Receive Process
3 //
4 // Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG)
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU Affero General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU Affero General Public License for more details.
15 //
16 // You should have received a copy of the GNU Affero General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #include "MbsfnFrameProcessor.h"
21 #include "spdlog/spdlog.h"
22 #include "srslte/interfaces/ue_interfaces.h"
23 
24 std::map<uint8_t, uint16_t> MbsfnFrameProcessor::_sched_stops;
25 
26 std::mutex MbsfnFrameProcessor::_sched_stop_mutex;
27 std::mutex MbsfnFrameProcessor::_rlc_mutex;
28 
29 auto MbsfnFrameProcessor::init() -> bool {
30  _signal_buffer_max_samples = 3 * SRSLTE_SF_LEN_PRB(MAX_PRB);
31 
32  _signal_buffer_rx[0] = srslte_vec_cf_malloc(_signal_buffer_max_samples);
33  if (_signal_buffer_rx[0] == nullptr) {
34  spdlog::error("Could not allocate regular DL signal buffer\n");
35  return false;
36  }
37 
38  if (srslte_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, 1) != 0) {
39  spdlog::error("Could not init ue_dl\n");
40  return false;;
41  }
42 
43  srslte_softbuffer_rx_init(&_softbuffer, 100);
44 
45  _ue_dl_cfg.snr_to_cqi_offset = 0;
46 
47  srslte_chest_dl_cfg_t* chest_cfg = &_ue_dl_cfg.chest_cfg;
48  bzero(chest_cfg, sizeof(srslte_chest_dl_cfg_t));
49  chest_cfg->filter_coef[0] = 0.1;
50  chest_cfg->filter_type = SRSLTE_CHEST_FILTER_TRIANGLE;
51  chest_cfg->noise_alg = SRSLTE_NOISE_ALG_EMPTY;
52  chest_cfg->rsrp_neighbour = false;
53  chest_cfg->sync_error_enable = false;
54  chest_cfg->estimator_alg = SRSLTE_ESTIMATOR_ALG_INTERPOLATE;
55  chest_cfg->cfo_estimate_enable = false;
56 
57  _ue_dl_cfg.cfg.pdsch.csi_enable = true;
58  _ue_dl_cfg.cfg.pdsch.max_nof_iterations = 8;
59  _ue_dl_cfg.cfg.pdsch.meas_evm_en = false;
60  _ue_dl_cfg.cfg.pdsch.decoder_type = SRSLTE_MIMO_DECODER_MMSE;
61  _ue_dl_cfg.cfg.pdsch.softbuffers.rx[0] = &_softbuffer;
62 
63  _pmch_cfg.pdsch_cfg.csi_enable = true;
64  _pmch_cfg.pdsch_cfg.max_nof_iterations = 8;
65  _pmch_cfg.pdsch_cfg.meas_evm_en = false;
66  _pmch_cfg.pdsch_cfg.decoder_type = SRSLTE_MIMO_DECODER_MMSE;
67 
68  _sf_cfg.sf_type = SRSLTE_SF_MBSFN;
69  return true;
70 }
71 
73  srslte_softbuffer_rx_free(&_softbuffer);
74  srslte_ue_dl_free(&_ue_dl);
75 }
76 
77 void MbsfnFrameProcessor::set_cell(srslte_cell_t cell) {
78  _cell = cell;
79  srslte_ue_dl_set_cell(&_ue_dl, cell);
80 }
81 
82 auto MbsfnFrameProcessor::process(uint32_t tti) -> int {
83  spdlog::trace("Processing MBSFN TTI {}", tti);
84 
85  uint32_t sfn = tti / 10;
86  uint8_t sf = tti % 10;
87 
88  unsigned area = 0;
89  _sf_cfg.tti = tti;
90  _pmch_cfg.area_id = _area_id;
91  srslte_mbsfn_cfg_t mbsfn_cfg = _phy.mbsfn_config_for_tti(tti, area);
92  _ue_dl_cfg.chest_cfg.mbsfn_area_id = _area_id;
93 
94  if (sfn%50 == 0) {
95  if (mbsfn_cfg.is_mcch) {
96  _rest._mcch.errors = 0;
97  _rest._mcch.total = 1;
98  } else {
99  _rest._mch[area].errors = 0;
100  _rest._mch[area].total = 1;
101  }
102  }
103 
104  if (!mbsfn_cfg.enable) {
105  _mutex.unlock();
106  return -1;
107  }
108 
109  if (mbsfn_cfg.is_mcch) {
110  _rest._mcch.total++;
111  } else {
112  _rest._mch[area].total++;
113  }
114 
115  if (srslte_ue_dl_decode_fft_estimate(&_ue_dl, &_sf_cfg, &_ue_dl_cfg) < 0) {
116  if (mbsfn_cfg.is_mcch) {
117  _rest._mcch.errors++;
118  } else {
119  _rest._mch[area].errors++;
120  }
121  spdlog::error("Getting PDCCH FFT estimate");
122  _mutex.unlock();
123  return -1;
124  }
125 
126  srslte_configure_pmch(&_pmch_cfg, &_cell, &mbsfn_cfg);
127  srslte_ra_dl_compute_nof_re(&_cell, &_sf_cfg, &_pmch_cfg.pdsch_cfg.grant);
128 
129  _pmch_cfg.area_id = _area_id;
130 
131  srslte_softbuffer_rx_reset_cb(&_softbuffer, 1);
132 
133  srslte_pdsch_res_t pmch_dec = {};
134  _pmch_cfg.pdsch_cfg.softbuffers.rx[0] = &_softbuffer;
135  pmch_dec.payload = _payload_buffer;
136  srslte_softbuffer_rx_reset_tbs(_pmch_cfg.pdsch_cfg.softbuffers.rx[0], _pmch_cfg.pdsch_cfg.grant.tb[0].tbs);
137 
138  if (srslte_ue_dl_decode_pmch(&_ue_dl, &_sf_cfg, &_pmch_cfg, &pmch_dec) != 0) {
139  if (mbsfn_cfg.is_mcch) {
140  _rest._mcch.errors++;
141  } else {
142  _rest._mch[area].errors++;
143  }
144  spdlog::warn("Error decoding PMCH");
145  _mutex.unlock();
146  return -1;
147  }
148 
149  spdlog::trace("PMCH: tti: {}, l_crb={}, tbs={}, mcs={}, crc={}, snr={} dB, n_iter={}\n",
150  tti,
151  _pmch_cfg.pdsch_cfg.grant.nof_prb,
152  _pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8,
153  _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx,
154  pmch_dec.crc ? "OK" : "KO",
155  _ue_dl.chest_res.snr_db,
156  pmch_dec.avg_iterations_block);
157 
158  if (mbsfn_cfg.is_mcch) {
159  _rest._mcch.SetData(mch_data());
160  _rest._mcch.mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
161  _rest._mcch.ber = _softbuffer.ber;
162  } else {
163  _rest._mch[area].SetData(mch_data());
164  _rest._mch[area].mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
165  _rest._mch[area].present = true;
166  _rest._mch[area].ber = _softbuffer.ber;
167  }
168 
169  if (pmch_dec.crc) {
170  mch_mac_msg.init_rx(
171  static_cast<uint32_t>(_pmch_cfg.pdsch_cfg.grant.tb[0].tbs) / 8);
172  mch_mac_msg.parse_packet(_payload_buffer);
173 
174  while (mch_mac_msg.next()) {
175  if (srslte::mch_lcid::MCH_SCHED_INFO == mch_mac_msg.get()->mch_ce_type()) {
176  uint16_t stop = 0;
177  uint8_t lcid = 0;
178  while (mch_mac_msg.get()->get_next_mch_sched_info(&lcid, &stop)) {
179  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
180  spdlog::debug("Scheduling stop for LCID {} in sf {}", lcid, stop);
181  _sched_stops[ lcid ] = stop;
182  }
183  } else if (mch_mac_msg.get()->is_sdu()) {
184  uint32_t lcid = mch_mac_msg.get()->get_sdu_lcid();
185  spdlog::trace("Processing MAC MCH PDU entered, lcid {}", lcid);
186 
187  if (lcid >= SRSLTE_N_MCH_LCIDS) {
188  spdlog::warn("Radio bearer id must be in [0:%d] - %d", SRSLTE_N_MCH_LCIDS, lcid);
189  if (mbsfn_cfg.is_mcch) {
190  _rest._mcch.errors++;
191  } else {
192  _rest._mch[area].errors++;
193  }
194  _mutex.unlock();
195  return -1;
196  }
197 
198  {
199  const std::lock_guard<std::mutex> lock(_rlc_mutex);
200  _phy._mcs = mbsfn_cfg.mbsfn_mcs;
201  _rlc.write_pdu_mch(lcid, mch_mac_msg.get()->get_sdu_ptr(), mch_mac_msg.get()->get_payload_size());
202  }
203  }
204  }
205  } else {
206  if (mbsfn_cfg.is_mcch) {
207  _rest._mcch.errors++;
208  } else {
209  _rest._mch[area].errors++;
210  }
211 
212  spdlog::warn("PMCH in TTI {} failed with CRC error", tti);
213  _mutex.unlock();
214  return -1;
215  }
216 
217  if (!mbsfn_cfg.is_mcch) {
218  for (uint32_t i = 0; i < _phy.mcch().nof_pmch_info; i++) {
219  unsigned fn_in_scheduling_period = sfn % srslte::enum_to_number(_phy.mcch().pmch_info_list[i].mch_sched_period);
220  unsigned sf_idx = fn_in_scheduling_period * 10 + sf - (fn_in_scheduling_period / 4) - 1;
221 
222  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
223  for (auto itr = _sched_stops.cbegin() ; itr != _sched_stops.cend() ;) {
224  if ( sf_idx >= itr->second ) {
225  const std::lock_guard<std::mutex> lock(_rlc_mutex);
226  spdlog::debug("Stopping LCID {} in tti {} (idx in rf {})", itr->first, tti, sf_idx);
227  _rlc.stop_mch(itr->first);
228  itr = _sched_stops.erase(itr);
229  } else {
230  itr = std::next(itr);
231  }
232  }
233  }
234  } else {
235  _rest._mcch.present = true;
236  }
237  _mutex.unlock();
238  return mbsfn_cfg.is_mcch ? 0 : 1;
239 }
240 
241 void MbsfnFrameProcessor::configure_mbsfn(uint8_t area_id, srslte_scs_t subcarrier_spacing) {
242  _sf_cfg.subcarrier_spacing = subcarrier_spacing;
243  srslte_ue_dl_set_mbsfn_subcarrier_spacing(&_ue_dl, subcarrier_spacing);
244  srslte_ue_dl_set_mbsfn_area_id(&_ue_dl, area_id);
245  _area_id = area_id;
246  _mbsfn_configured = true;
247 }
248 
249 auto MbsfnFrameProcessor::mch_data() const -> std::vector<uint8_t> const {
250  const uint8_t* data = reinterpret_cast<uint8_t*>(_ue_dl.pmch.d);
251  return std::move(std::vector<uint8_t>( data, data + _pmch_cfg.pdsch_cfg.grant.nof_re * sizeof(cf_t)));
252 }
MbsfnFrameProcessor::mch_data
const std::vector< uint8_t > mch_data() const
Get the constellation diagram data (I/Q data of the subcarriers after CE)
Definition: MbsfnFrameProcessor.cpp:249
MbsfnFrameProcessor::set_cell
void set_cell(srslte_cell_t cell)
Set the parameters for the cell (Nof PRB, etc).
Definition: MbsfnFrameProcessor.cpp:77
MbsfnFrameProcessor::init
bool init()
Initialize signal- and softbuffers, init all underlying components.
Definition: MbsfnFrameProcessor.cpp:29
MbsfnFrameProcessor::~MbsfnFrameProcessor
virtual ~MbsfnFrameProcessor()
Default destructor.
Definition: MbsfnFrameProcessor.cpp:72
MbsfnFrameProcessor::configure_mbsfn
void configure_mbsfn(uint8_t area_id, srslte_scs_t subcarrier_spacing)
Set MBSFN parameters: area ID and subcarrier spacing.
Definition: MbsfnFrameProcessor.cpp:241
MbsfnFrameProcessor::process
int process(uint32_t tti)
Process the sample data in the signal buffer.
Definition: MbsfnFrameProcessor.cpp:82