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_NONE;
52  chest_cfg->noise_alg = SRSRAN_NOISE_ALG_EMPTY;
53  chest_cfg->rsrp_neighbour = false;
54  chest_cfg->sync_error_enable = true;
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 (!mbsfn_cfg.enable) {
101  spdlog::trace("PMCH: tti {}: neither MCCH nor MCH enabled. Skipping subframe");
102  _mutex.unlock();
103  return -1;
104  }
105 
106  if (mbsfn_cfg.is_mcch) {
107  _rest._mcch.total++;
108  } else {
109  _rest._mch[mch_idx].total++;
110  }
111 
112  if (srsran_ue_dl_decode_fft_estimate(&_ue_dl, &_sf_cfg, &_ue_dl_cfg) < 0) {
113  if (mbsfn_cfg.is_mcch) {
114  _rest._mcch.errors++;
115  } else {
116  _rest._mch[mch_idx].errors++;
117  }
118  spdlog::error("Getting PDCCH FFT estimate");
119  _mutex.unlock();
120  return -1;
121  }
122 
123  srsran_configure_pmch(&_pmch_cfg, &_cell, &mbsfn_cfg);
124  srsran_ra_dl_compute_nof_re(&_cell, &_sf_cfg, &_pmch_cfg.pdsch_cfg.grant);
125 
126  _pmch_cfg.area_id = _area_id;
127 
128  srsran_softbuffer_rx_reset_cb(&_softbuffer, 1);
129 
130  srsran_pdsch_res_t pmch_dec = {};
131  _pmch_cfg.pdsch_cfg.softbuffers.rx[0] = &_softbuffer;
132  pmch_dec.payload = _payload_buffer;
133  srsran_softbuffer_rx_reset_tbs(_pmch_cfg.pdsch_cfg.softbuffers.rx[0], _pmch_cfg.pdsch_cfg.grant.tb[0].tbs);
134 
135  if (srsran_ue_dl_decode_pmch(&_ue_dl, &_sf_cfg, &_pmch_cfg, &pmch_dec) != 0) {
136  if (mbsfn_cfg.is_mcch) {
137  _rest._mcch.errors++;
138  } else {
139  _rest._mch[mch_idx].errors++;
140  }
141  spdlog::warn("Error decoding PMCH");
142  _mutex.unlock();
143  return -1;
144  }
145 
146  spdlog::trace("PMCH: tti: {}, l_crb={}, tbs={}, mcs={}, crc={}, snr={} dB, n_iter={}\n",
147  tti,
148  _pmch_cfg.pdsch_cfg.grant.nof_prb,
149  _pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8,
150  _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx,
151  pmch_dec.crc ? "OK" : "KO",
152  _ue_dl.chest_res.snr_db,
153  pmch_dec.avg_iterations_block);
154 
155  if (mbsfn_cfg.is_mcch) {
156  _rest._mcch.SetData(mch_data());
157  _rest._mcch.mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
158  } else {
159  _rest._mch[mch_idx].SetData(mch_data());
160  _rest._mch[mch_idx].mcs = _pmch_cfg.pdsch_cfg.grant.tb[0].mcs_idx;
161  _rest._mch[mch_idx].present = true;
162  }
163 
164  if (pmch_dec.crc) {
165  mch_mac_msg.init_rx(
166  static_cast<uint32_t>(_pmch_cfg.pdsch_cfg.grant.tb[0].tbs) / 8);
167  mch_mac_msg.parse_packet(_payload_buffer);
168 
169  while (mch_mac_msg.next()) {
170  if (srsran::mch_lcid::MCH_SCHED_INFO == mch_mac_msg.get()->mch_ce_type()) {
171  uint16_t stop = 0;
172  uint8_t lcid = 0;
173  while (mch_mac_msg.get()->get_next_mch_sched_info(&lcid, &stop)) {
174  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
175  spdlog::debug("Scheduling stop for LCID {} in sf {}", lcid, stop);
176  _sched_stops[ lcid ] = stop;
177  }
178  } else if (mch_mac_msg.get()->is_sdu()) {
179  uint32_t lcid = mch_mac_msg.get()->get_sdu_lcid();
180  spdlog::trace("Processing MAC MCH PDU entered, lcid {}", lcid);
181 
182  if (lcid >= SRSRAN_N_MCH_LCIDS) {
183  spdlog::warn("Radio bearer id must be in [0:%d] - %d", SRSRAN_N_MCH_LCIDS, lcid);
184  if (mbsfn_cfg.is_mcch) {
185  _rest._mcch.errors++;
186  } else {
187  _rest._mch[mch_idx].errors++;
188  }
189  _mutex.unlock();
190  return -1;
191  }
192 
193  {
194  _phy._mcs = mbsfn_cfg.mbsfn_mcs;
195  const std::lock_guard<std::mutex> lock(_rlc_mutex);
196  _rlc.write_pdu_mch(mch_idx, lcid, mch_mac_msg.get()->get_sdu_ptr(), mch_mac_msg.get()->get_payload_size());
197  }
198  }
199  }
200  } else {
201  if (mbsfn_cfg.is_mcch) {
202  _rest._mcch.errors++;
203  } else {
204  _rest._mch[mch_idx].errors++;
205  }
206 
207  spdlog::trace("PMCH in TTI {} failed with CRC error", tti);
208  _mutex.unlock();
209  return -1;
210  }
211 
212  if (!mbsfn_cfg.is_mcch) {
213  for (uint32_t i = 0; i < _phy.mcch().nof_pmch_info; i++) {
214  unsigned fn_in_scheduling_period = sfn % srsran::enum_to_number(_phy.mcch().pmch_info_list[i].mch_sched_period);
215  unsigned sf_idx;
216  if (_cell.mbms_dedicated) {
217  sf_idx = fn_in_scheduling_period * 10 + sf - (fn_in_scheduling_period / 4) - 1;
218  } else {
219  sf_idx = fn_in_scheduling_period * 6 + (sf < 6 ? sf - 1 : sf - 3);
220  }
221  spdlog::debug("tti{}, sfn {}, sf {}, fn_in_scheduling_period {}, sf_idf {}", tti, sfn, sf, fn_in_scheduling_period, sf_idx);
222 
223  const std::lock_guard<std::mutex> lock(_sched_stop_mutex);
224  for (auto itr = _sched_stops.cbegin() ; itr != _sched_stops.cend() ;) {
225  if ( sf_idx >= itr->second ) {
226  spdlog::debug("Stopping LCID {} in tti {} (idx in rf {})", itr->first, tti, sf_idx);
227  const std::lock_guard<std::mutex> lock(_rlc_mutex);
228  if (!_allow_rrc_sn_across_periods) {
229  _rlc.stop_mch(i, itr->first);
230  }
231  itr = _sched_stops.erase(itr);
232  } else {
233  itr = std::next(itr);
234  }
235  }
236  }
237  } else {
238  const std::lock_guard<std::mutex> lock(_rlc_mutex);
239  _rlc.stop_mch(0, 0);
240  _rest._mcch.present = true;
241  }
242  _mutex.unlock();
243  return mbsfn_cfg.is_mcch ? 0 : 1;
244 }
245 
246 void MbsfnFrameProcessor::configure_mbsfn(uint8_t area_id, srsran_scs_t subcarrier_spacing) {
247  _sf_cfg.subcarrier_spacing = subcarrier_spacing;
248  srsran_ue_dl_set_mbsfn_subcarrier_spacing(&_ue_dl, subcarrier_spacing);
249 
250 
251  srsran_ue_dl_set_mbsfn_area_id(&_ue_dl, area_id);
252  _area_id = area_id;
253  _mbsfn_configured = true;
254 }
255 
256 auto MbsfnFrameProcessor::mch_data() const -> std::vector<uint8_t> const {
257  const uint8_t* data = reinterpret_cast<uint8_t*>(_ue_dl.pmch.d);
258  return std::move(std::vector<uint8_t>( data, data + _pmch_cfg.pdsch_cfg.grant.nof_re * sizeof(cf_t)));
259 }
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.