5G-MAG Reference Tools - MBMS Modem
MbsfnFrameProcessor.cpp
Go to the documentation of this file.
1 // 5G-MAG Reference Tools
2 // MBMS Modem 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 
23 std::map<uint8_t, uint16_t> MbsfnFrameProcessor::_sched_stops;
24 
27 
28 auto MbsfnFrameProcessor::init() -> bool {
29  _signal_buffer_max_samples = 3 * SRSRAN_SF_LEN_PRB(MAX_PRB);
30 
31  for (auto ch = 0; ch < _rx_channels; ch++) {
32  _signal_buffer_rx[ch] = srsran_vec_cf_malloc(_signal_buffer_max_samples);
33  if (!_signal_buffer_rx[ch]) {
34  spdlog::error("Could not allocate regular DL signal buffer\n");
35  return false;
36  }
37  }
38 
39  if (srsran_ue_dl_init(&_ue_dl, _signal_buffer_rx, MAX_PRB, _rx_channels) != 0) {
40  spdlog::error("Could not init ue_dl\n");
41  return false;;
42  }
43 
44  srsran_softbuffer_rx_init(&_softbuffer, 100);
45 
46  _ue_dl_cfg.snr_to_cqi_offset = 0;
47 
48  srsran_chest_dl_cfg_t* chest_cfg = &_ue_dl_cfg.chest_cfg;
49  bzero(chest_cfg, sizeof(srsran_chest_dl_cfg_t));
50  chest_cfg->filter_coef[0] = 0.1;
51  chest_cfg->filter_type = SRSRAN_CHEST_FILTER_TRIANGLE;
52  chest_cfg->noise_alg = SRSRAN_NOISE_ALG_EMPTY;
53  chest_cfg->rsrp_neighbour = false;
54  chest_cfg->sync_error_enable = false;
55  chest_cfg->estimator_alg = SRSRAN_ESTIMATOR_ALG_INTERPOLATE;
56  chest_cfg->cfo_estimate_enable = false;
57 
58  _ue_dl_cfg.cfg.pdsch.csi_enable = true;
59  _ue_dl_cfg.cfg.pdsch.max_nof_iterations = 8;
60  _ue_dl_cfg.cfg.pdsch.meas_evm_en = false;
61  _ue_dl_cfg.cfg.pdsch.decoder_type = SRSRAN_MIMO_DECODER_MMSE;
62  _ue_dl_cfg.cfg.pdsch.softbuffers.rx[0] = &_softbuffer;
63 
64  _pmch_cfg.pdsch_cfg.csi_enable = true;
65  _pmch_cfg.pdsch_cfg.max_nof_iterations = 8;
66  _pmch_cfg.pdsch_cfg.meas_evm_en = false;
67  _pmch_cfg.pdsch_cfg.decoder_type = SRSRAN_MIMO_DECODER_MMSE;
68 
69  _sf_cfg.sf_type = SRSRAN_SF_MBSFN;
70  return true;
71 }
72 
74  srsran_softbuffer_rx_free(&_softbuffer);
75  srsran_ue_dl_free(&_ue_dl);
76 }
77 
78 void MbsfnFrameProcessor::set_cell(srsran_cell_t cell) {
79  _cell = cell;
80  srsran_ue_dl_set_cell(&_ue_dl, cell);
81 }
82 
83 auto MbsfnFrameProcessor::process(uint32_t tti) -> int {
84  spdlog::trace("Processing MBSFN TTI {}", tti);
85 
86  uint32_t sfn = tti / 10;
87  uint8_t sf = tti % 10;
88 
89  unsigned mch_idx = 0;
90  _sf_cfg.tti = tti;
91  _pmch_cfg.area_id = _area_id;
92  srsran_mbsfn_cfg_t mbsfn_cfg = _phy.mbsfn_config_for_tti(tti, mch_idx);
93  _ue_dl_cfg.chest_cfg.mbsfn_area_id = _area_id;
94  srsran_ue_dl_set_mbsfn_area_id(&_ue_dl, mbsfn_cfg.mbsfn_area_id);
95 
96  if (!_cell.mbms_dedicated) {
97  srsran_ue_dl_set_non_mbsfn_region(&_ue_dl, mbsfn_cfg.non_mbsfn_region_length);
98  }
99 
100  if (sfn%50 == 0) {
101  if (mbsfn_cfg.is_mcch) {
102  _rest._mcch.errors = 0;
103  _rest._mcch.total = 1;
104  } else {
105  _rest._mch[mch_idx].errors = 0;
106  _rest._mch[mch_idx].total = 1;
107  }
108  }
109 
110  if (!mbsfn_cfg.enable) {
111  spdlog::trace("PMCH: tti {}: neither MCCH nor MCH enabled. Skipping subframe");
112  _mutex.unlock();
113  return -1;
114  }
115 
116  if (mbsfn_cfg.is_mcch) {
117  _rest._mcch.total++;
118  } else {
119  _rest._mch[mch_idx].total++;
120  }
121 
122  if (srsran_ue_dl_decode_fft_estimate(&_ue_dl, &_sf_cfg, &_ue_dl_cfg) < 0) {
123  if (mbsfn_cfg.is_mcch) {
124  _rest._mcch.errors++;
125  } else {
126  _rest._mch[mch_idx].errors++;
127  }
128  spdlog::error("Getting PDCCH FFT estimate");
129  _mutex.unlock();
130  return -1;
131  }
132 
133  srsran_configure_pmch(&_pmch_cfg, &_cell, &mbsfn_cfg);
134  srsran_ra_dl_compute_nof_re(&_cell, &_sf_cfg, &_pmch_cfg.pdsch_cfg.grant);
135 
136  _pmch_cfg.area_id = _area_id;
137 
138  srsran_softbuffer_rx_reset_cb(&_softbuffer, 1);
139 
140  srsran_pdsch_res_t pmch_dec = {};
141  _pmch_cfg.pdsch_cfg.softbuffers.rx[0] = &_softbuffer;
142  pmch_dec.payload = _payload_buffer;
143  srsran_softbuffer_rx_reset_tbs(_pmch_cfg.pdsch_cfg.softbuffers.rx[0], _pmch_cfg.pdsch_cfg.grant.tb[0].tbs);
144 
145  if (srsran_ue_dl_decode_pmch(&_ue_dl, &_sf_cfg, &_pmch_cfg, &pmch_dec) != 0) {
146  if (mbsfn_cfg.is_mcch) {
147  _rest._mcch.errors++;
148  } else {
149  _rest._mch[mch_idx].errors++;
150  }
151  spdlog::warn("Error decoding PMCH");
152  _mutex.unlock();
153  return -1;
154  }
155 
156  spdlog::trace("PMCH: tti: {}, l_crb={}, tbs={}, mcs={}, crc={}, snr={} dB, n_iter={}\n",
157  tti,
158  _pmch_cfg.pdsch_cfg.grant.nof_prb,
159  _pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8,
160  _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx,
161  pmch_dec.crc ? "OK" : "KO",
162  _ue_dl.chest_res.snr_db,
163  pmch_dec.avg_iterations_block);
164 
165  if (mbsfn_cfg.is_mcch) {
166  _rest._mcch.SetData(mch_data());
167  _rest._mcch.mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
168  } else {
169  _rest._mch[mch_idx].SetData(mch_data());
170  _rest._mch[mch_idx].mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
171  _rest._mch[mch_idx].present = true;
172  }
173 
174  if (pmch_dec.crc) {
175  mch_mac_msg.init_rx(
176  static_cast<uint32_t>(_pmch_cfg.pdsch_cfg.grant.tb[0].tbs) / 8);
177  mch_mac_msg.parse_packet(_payload_buffer);
178 
179  while (mch_mac_msg.next()) {
180  if (srsran::mch_lcid::MCH_SCHED_INFO == mch_mac_msg.get()->mch_ce_type()) {
181  uint16_t stop = 0;
182  uint8_t lcid = 0;
183  while (mch_mac_msg.get()->get_next_mch_sched_info(&lcid, &stop)) {
184  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
185  spdlog::debug("Scheduling stop for LCID {} in sf {}", lcid, stop);
186  _sched_stops[ lcid ] = stop;
187  }
188  } else if (mch_mac_msg.get()->is_sdu()) {
189  uint32_t lcid = mch_mac_msg.get()->get_sdu_lcid();
190  spdlog::trace("Processing MAC MCH PDU entered, lcid {}", lcid);
191 
192  if (lcid >= SRSRAN_N_MCH_LCIDS) {
193  spdlog::warn("Radio bearer id must be in [0:%d] - %d", SRSRAN_N_MCH_LCIDS, lcid);
194  if (mbsfn_cfg.is_mcch) {
195  _rest._mcch.errors++;
196  } else {
197  _rest._mch[mch_idx].errors++;
198  }
199  _mutex.unlock();
200  return -1;
201  }
202 
203  {
204  const std::lock_guard<std::mutex> lock(_rlc_mutex);
205  _phy._mcs = mbsfn_cfg.mbsfn_mcs;
206  _rlc.write_pdu_mch(mch_idx, lcid, mch_mac_msg.get()->get_sdu_ptr(), mch_mac_msg.get()->get_payload_size());
207  }
208  }
209  }
210  } else {
211  if (mbsfn_cfg.is_mcch) {
212  _rest._mcch.errors++;
213  } else {
214  _rest._mch[mch_idx].errors++;
215  }
216 
217  spdlog::warn("PMCH in TTI {} failed with CRC error", tti);
218  _mutex.unlock();
219  return -1;
220  }
221 
222  if (!mbsfn_cfg.is_mcch) {
223  for (uint32_t i = 0; i < _phy.mcch().nof_pmch_info; i++) {
224  unsigned fn_in_scheduling_period = sfn % srsran::enum_to_number(_phy.mcch().pmch_info_list[i].mch_sched_period);
225  unsigned sf_idx;
226  if (_cell.mbms_dedicated) {
227  sf_idx = fn_in_scheduling_period * 10 + sf - (fn_in_scheduling_period / 4) - 1;
228  } else {
229  sf_idx = fn_in_scheduling_period * 6 + (sf < 6 ? sf - 1 : sf - 3);
230  }
231  spdlog::debug("tti{}, sfn {}, sf {}, fn_in_scheduling_period {}, sf_idf {}", tti, sfn, sf, fn_in_scheduling_period, sf_idx);
232 
233  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
234  for (auto itr = _sched_stops.cbegin() ; itr != _sched_stops.cend() ;) {
235  if ( sf_idx >= itr->second ) {
236  const std::lock_guard<std::mutex> lock(_rlc_mutex);
237  spdlog::debug("Stopping LCID {} in tti {} (idx in rf {})", itr->first, tti, sf_idx);
238  _rlc.stop_mch(i, itr->first);
239  itr = _sched_stops.erase(itr);
240  } else {
241  itr = std::next(itr);
242  }
243  }
244  }
245  } else {
246  _rlc.stop_mch(0, 0);
247  _rest._mcch.present = true;
248  }
249  _mutex.unlock();
250  return mbsfn_cfg.is_mcch ? 0 : 1;
251 }
252 
253 void MbsfnFrameProcessor::configure_mbsfn(uint8_t area_id, srsran_scs_t subcarrier_spacing) {
254  _sf_cfg.subcarrier_spacing = subcarrier_spacing;
255  srsran_ue_dl_set_mbsfn_subcarrier_spacing(&_ue_dl, subcarrier_spacing);
256 
257 
258  srsran_ue_dl_set_mbsfn_area_id(&_ue_dl, area_id);
259  _area_id = area_id;
260  _mbsfn_configured = true;
261 }
262 
263 auto MbsfnFrameProcessor::mch_data() const -> std::vector<uint8_t> const {
264  const uint8_t* data = reinterpret_cast<uint8_t*>(_ue_dl.pmch.d);
265  return std::move(std::vector<uint8_t>( data, data + _pmch_cfg.pdsch_cfg.grant.nof_re * sizeof(cf_t)));
266 }
constexpr unsigned int MAX_PRB
Definition: Phy.h:35
srsran_softbuffer_rx_t _softbuffer
static std::map< uint8_t, uint16_t > _sched_stops
bool init()
Initialize signal- and softbuffers, init all underlying components.
void set_cell(srsran_cell_t cell)
Set the parameters for the cell (Nof PRB, etc).
srsran_dl_sf_cfg_t _sf_cfg
static std::mutex _rlc_mutex
virtual ~MbsfnFrameProcessor()
Default destructor.
int process(uint32_t tti)
Process the sample data in the signal buffer.
static std::mutex _sched_stop_mutex
const std::vector< uint8_t > mch_data() const
Get the constellation diagram data (I/Q data of the subcarriers after CE)
srsran_pmch_cfg_t _pmch_cfg
void configure_mbsfn(uint8_t area_id, srsran_scs_t subcarrier_spacing)
Set MBSFN parameters: area ID and subcarrier spacing.