5G-MAG Reference Tools - MBMS Modem
main.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 
31 #include <argp.h>
32 
33 #include <cstdlib>
34 #include <libconfig.h++>
35 
36 #include "CasFrameProcessor.h"
37 #include "Gw.h"
38 #include "SdrReader.h"
39 #include "MbsfnFrameProcessor.h"
40 #include "MeasurementFileWriter.h"
41 #include "Phy.h"
42 #include "RestHandler.h"
43 #include "Rrc.h"
44 #include "Version.h"
45 #include "spdlog/async.h"
46 #include "spdlog/spdlog.h"
47 #include "spdlog/sinks/syslog_sink.h"
48 #include "srsran/srsran.h"
49 #include "srsran/upper/pdcp.h"
50 #include "srsran/rlc/rlc.h"
51 #include "thread_pool.hpp"
52 
53 using libconfig::Config;
54 using libconfig::FileIOException;
55 using libconfig::ParseException;
56 
57 using std::placeholders::_1;
58 using std::placeholders::_2;
59 using std::placeholders::_3;
60 
61 static void print_version(FILE *stream, struct argp_state *state);
62 void (*argp_program_version_hook)(FILE *, struct argp_state *) = print_version;
63 const char *argp_program_bug_address = "5G-MAG Reference Tools <reference-tools@5g-mag.com>";
64 static char doc[] = "5G-MAG-RT MBMS Modem Process"; // NOLINT
65 
66 static struct argp_option options[] = { // NOLINT
67  {"config", 'c', "FILE", 0, "Configuration file (default: /etc/5gmag-rt.conf)", 0},
68  {"log-level", 'l', "LEVEL", 0,
69  "Log verbosity: 0 = trace, 1 = debug, 2 = info, 3 = warn, 4 = error, 5 = "
70  "critical, 6 = none. Default: 2.",
71  0},
72  {"srsran-log-level", 's', "LEVEL", 0,
73  "Log verbosity for srsran: 0 = debug, 1 = info, 2 = warn, 3 = error, 4 = "
74  "none, Default: 4.",
75  0},
76  {"sample-file", 'f', "FILE", 0,
77  "Sample file in 4 byte float interleaved format to read I/Q data from. If "
78  "present, the data from this file will be decoded instead of live SDR "
79  "data. The channel bandwith must be specified with the --file-bandwidth "
80  "flag, and the sample rate of the file must be suitable for this "
81  "bandwidth.",
82  0},
83  {"write-sample-file", 'w', "FILE", 0,
84  "Create a sample file in 4 byte float interleaved format containing the "
85  "raw received I/Q data.",
86  0},
87  {"file-bandwidth", 'b', "BANDWIDTH (MHz)", 0,
88  "If decoding data from a file, specify the channel bandwidth of the "
89  "recorded data in MHz here (e.g. 5)",
90  0},
91  {"override_nof_prb", 'p', "# PRB", 0,
92  "Override the number of PRB received in the MIB", 0},
93  {"sdr_devices", 'd', nullptr, 0,
94  "Prints a list of all available SDR devices", 0},
95  {nullptr, 0, nullptr, 0, nullptr, 0}};
96 
100 struct arguments {
101  const char *config_file = {};
102  unsigned log_level = 2;
103  unsigned srs_log_level = 4;
104  int8_t override_nof_prb = -1;
105  const char *sample_file = {};
106  uint8_t file_bw = 0;
107  const char
109  bool list_sdr_devices = false;
110 };
111 
115 static auto parse_opt(int key, char *arg, struct argp_state *state) -> error_t {
116  auto arguments = static_cast<struct arguments *>(state->input);
117  switch (key) {
118  case 'c':
119  arguments->config_file = arg;
120  break;
121  case 'l':
122  arguments->log_level = static_cast<unsigned>(strtoul(arg, nullptr, 10));
123  break;
124  case 's':
126  static_cast<unsigned>(strtoul(arg, nullptr, 10));
127  break;
128  case 'f':
129  arguments->sample_file = arg;
130  break;
131  case 'w':
133  break;
134  case 'b':
135  arguments->file_bw = static_cast<uint8_t>(strtoul(arg, nullptr, 10));
136  break;
137  case 'p':
139  static_cast<int8_t>(strtol(arg, nullptr, 10));
140  break;
141  case 'd':
142  arguments->list_sdr_devices = true;
143  break;
144  case ARGP_KEY_ARG:
145  argp_usage(state);
146  break;
147  default:
148  return ARGP_ERR_UNKNOWN;
149  }
150  return 0;
151 }
152 
153 static struct argp argp = {options, parse_opt, nullptr, doc,
154  nullptr, nullptr, nullptr};
155 
159 void print_version(FILE *stream, struct argp_state * /*state*/) {
160  fprintf(stream, "%s.%s.%s\n", std::to_string(VERSION_MAJOR).c_str(),
161  std::to_string(VERSION_MINOR).c_str(),
162  std::to_string(VERSION_PATCH).c_str());
163 }
164 
165 static Config cfg;
167 static unsigned sample_rate = 7680000;
168 static unsigned search_sample_rate = 7680000;
169 static unsigned frequency = 667000000;
170 static uint32_t bandwidth = 10000000;
171 static double gain = 0.9;
172 static std::string antenna = "LNAW";
173 static bool use_agc = false;
174 
175 static unsigned mbsfn_nof_prb = 0;
176 static unsigned cas_nof_prb = 0;
177 
186 static bool restart = false;
187 
198 void set_params(const std::string& ant, unsigned fc, double g, unsigned sr, unsigned bw) {
199  sample_rate = sr;
200  frequency = fc;
201  bandwidth = bw;
202  antenna = ant;
203  gain = g;
204  spdlog::info("RESTful API requesting new parameters: fc {}, bw {}, rate {}, gain {}, antenna {}",
206 
207  restart = true;
208 }
209 
217 auto main(int argc, char **argv) -> int {
218  struct arguments arguments;
219  /* Default values */
220  arguments.config_file = "/etc/5gmag-rt.conf";
221  arguments.sample_file = nullptr;
222  arguments.write_sample_file = nullptr;
223  argp_parse(&argp, argc, argv, 0, nullptr, &arguments);
224 
225  // Read and parse the configuration file
226  try {
227  cfg.readFile(arguments.config_file);
228  } catch(const FileIOException &fioex) {
229  spdlog::error("I/O error while reading config file at {}. Exiting.", arguments.config_file);
230  exit(1);
231  } catch(const ParseException &pex) {
232  spdlog::error("Config parse error at {}:{} - {}. Exiting.",
233  pex.getFile(), pex.getLine(), pex.getError());
234  exit(1);
235  }
236 
237  // Set up logging
238  std::string ident = "modem";
239  auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID | LOG_PERROR | LOG_CONS );
240 
241  spdlog::set_level(
242  static_cast<spdlog::level::level_enum>(arguments.log_level));
243  spdlog::set_pattern("[%H:%M:%S.%f %z] [%^%l%$] [thr %t] %v");
244 
245  spdlog::set_default_logger(syslog_logger);
246  spdlog::info("5g-mag-rt modem v{}.{}.{} starting up", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
247 
248  // Init and tune the SDR
249  auto rx_channels = 1;
250  cfg.lookupValue("modem.sdr.rx_channels", rx_channels);
251  spdlog::info("Initialising SDR with {} RX channel(s)", rx_channels);
252  SdrReader sdr(cfg, rx_channels);
254  sdr.enumerateDevices();
255  exit(0);
256  }
257 
258  std::string sdr_dev = "driver=lime";
259  cfg.lookupValue("modem.sdr.device_args", sdr_dev);
260  if (!sdr.init(sdr_dev, arguments.sample_file, arguments.write_sample_file)) {
261  spdlog::error("Failed to initialize I/Q data source.");
262  exit(1);
263  }
264 
265  cfg.lookupValue("modem.sdr.search_sample_rate_hz", sample_rate);
267 
268  unsigned long long center_frequency = frequency;
269  if (!cfg.lookupValue("modem.sdr.center_frequency_hz", center_frequency)) {
270  spdlog::error("Unable to parse center_frequency_hz - values must have a ‘L’ character appended");
271  exit(1);
272  }
273  // We needed unsigned long long for correct parsing,
274  // but unsigned is required
275  if (center_frequency <= UINT_MAX) {
276  frequency = static_cast<unsigned>(center_frequency);
277  } else {
278  spdlog::error("Configured center_frequency_hz is {}, maximal value supported is {}.",
279  center_frequency, UINT_MAX);
280  exit(1);
281  }
282 
283 
284  cfg.lookupValue("modem.sdr.normalized_gain", gain);
285  cfg.lookupValue("modem.sdr.antenna", antenna);
286  cfg.lookupValue("modem.sdr.use_agc", use_agc);
287 
289  spdlog::error("Failed to set initial center frequency. Exiting.");
290  exit(1);
291  }
292 
293  set_srsran_verbose_level(arguments.log_level <= 1 ? SRSRAN_VERBOSE_DEBUG : SRSRAN_VERBOSE_NONE);
294  srsran_use_standard_symbol_size(true);
295 
296  // Create a thread pool for the frame processors
297  unsigned thread_cnt = 4;
298  cfg.lookupValue("modem.phy.threads", thread_cnt);
299  int phy_prio = 10;
300  cfg.lookupValue("modem.phy.thread_priority_rt", phy_prio);
301  thread_pool pool{ thread_cnt + 1, phy_prio };
302 
303  // Elevate execution to real time scheduling
304  struct sched_param thread_param = {};
305  thread_param.sched_priority = 20;
306  cfg.lookupValue("modem.phy.main_thread_priority_rt", thread_param.sched_priority);
307 
308  spdlog::info("Raising main thread to realtime scheduling priority {}", thread_param.sched_priority);
309 
310  int error = pthread_setschedparam(pthread_self(), SCHED_RR, &thread_param);
311  if (error != 0) {
312  spdlog::error("Cannot set main thread priority to realtime: {}. Thread will run at default priority.", strerror(error));
313  }
314 
315  bool enable_measurement_file = false;
316  cfg.lookupValue("modem.measurement_file.enabled", enable_measurement_file);
317  MeasurementFileWriter measurement_file(cfg);
318 
319  // Create the layer components: Phy, RLC, RRC and GW
320  Phy phy(
321  cfg,
322  std::bind(&SdrReader::get_samples, &sdr, _1, _2, _3), // NOLINT
323  arguments.file_bw ? arguments.file_bw * 5 : 25,
325  rx_channels);
326 
327  phy.init();
328 
329  srsran::pdcp pdcp(nullptr, "PDCP");
330  srsran::rlc rlc("RLC");
331  srsran::timer_handler timers;
332 
333  Rrc rrc(cfg, phy, rlc);
334  Gw gw(cfg, phy);
335  gw.init();
336 
337  rlc.init(&pdcp, &rrc, &timers, 0 /* RB_ID_SRB0 */);
338  pdcp.init(&rlc, &rrc, &gw);
339 
340  auto srs_level = srslog::basic_levels::none;
341  switch (arguments.srs_log_level) {
342  case 0: srs_level = srslog::basic_levels::debug; break;
343  case 1: srs_level = srslog::basic_levels::info; break;
344  case 2: srs_level = srslog::basic_levels::warning; break;
345  case 3: srs_level = srslog::basic_levels::error; break;
346  case 4: srs_level = srslog::basic_levels::none; break;
347  }
348 
349  // Configure srsLTE logging
350  auto& mac_log = srslog::fetch_basic_logger("MAC", false);
351  mac_log.set_level(srs_level);
352  auto& phy_log = srslog::fetch_basic_logger("PHY", false);
353  phy_log.set_level(srs_level);
354  auto& rlc_log = srslog::fetch_basic_logger("RLC", false);
355  rlc_log.set_level(srs_level);
356  auto& asn1_log = srslog::fetch_basic_logger("ASN1", false);
357  asn1_log.set_level(srs_level);
358 
359 
360  state_t state = searching;
361 
362  // Create the RESTful API handler
363  std::string uri = "http://0.0.0.0:3010/modem-api/";
364  cfg.lookupValue("modem.restful_api.uri", uri);
365  spdlog::info("Starting RESTful API handler at {}", uri);
366  RestHandler rest_handler(cfg, uri, state, sdr, phy, set_params);
367 
368  // Initialize one CAS and thered_cnt MBSFN frame processors
369  CasFrameProcessor cas_processor(cfg, phy, rlc, rest_handler, rx_channels);
370  if (!cas_processor.init()) {
371  spdlog::error("Failed to create CAS processor. Exiting.");
372  exit(1);
373  }
374 
375  std::vector<MbsfnFrameProcessor*> mbsfn_processors;
376  for (int i = 0; i < thread_cnt; i++) {
377  auto p = new MbsfnFrameProcessor(cfg, rlc, phy, mac_log, rest_handler, rx_channels);
378  if (!p->init()) {
379  spdlog::error("Failed to create MBSFN processor. Exiting.");
380  exit(1);
381  }
382  mbsfn_processors.push_back(p);
383  }
384 
385  // Start receiving sample data
386  sdr.start();
387 
388  uint32_t tti = 0;
389 
390  uint32_t measurement_interval = 5;
391  cfg.lookupValue("modem.measurement_file.interval_secs", measurement_interval);
392  measurement_interval *= 1000;
393  uint32_t tick = 0;
394 
395  // Initial state: searching a cell
396  state = searching;
397 
398  // Start the main processing loop
399  for (;;) {
400  if (state == searching) {
401  if (restart) {
402  sdr.stop();
403  sample_rate = search_sample_rate; // sample rate for searching
405  sdr.start();
406  }
407 
408  // We're at the search sample rate, and there's no point in creating a sample file. Stop the sample writer, if enabled.
410 
411  // In searching state, clear the receive buffer and try to find a cell at the configured frequency and synchronize with it
412  restart = false;
413  sdr.clear_buffer();
414  bool cell_found = phy.cell_search();
415  if (cell_found) {
416  // A cell has been found. We now know the required number of PRB = bandwidth of the carrier. Set the approproiate
417  // sample rate...
418  cas_nof_prb = mbsfn_nof_prb = phy.nr_prb();
419 
421  // Samples files are recorded at a fixed sample rate that can be determined from the bandwidth command line argument.
422  // If we're decoding from file, do not readjust the rate to match the CAS PRBs, but stay at this rate and instead configure the
423  // PHY to decode a narrow CAS from a wider channel.
426  phy.set_cell();
427  } else {
428  // When decoding from the air, configure the SDR accordingly
429  unsigned new_srate = srsran_sampling_freq_hz(cas_nof_prb);
430  spdlog::info("Setting sample rate {} Mhz for {} PRB / {} Mhz channel width", new_srate/1000000.0, phy.nr_prb(),
431  phy.nr_prb() * 0.2);
432  sdr.stop();
433 
434  bandwidth = (cas_nof_prb * 200000) * 1.2;
435  sdr.tune(frequency, new_srate, bandwidth, gain, antenna, use_agc);
436 
437 
438  sdr.start();
439  }
440  spdlog::debug("Synchronizing subframe");
441  // ... and move to syncing state.
442  state = syncing;
443  } else {
444  sleep(1);
445  }
446  } else if (state == syncing) {
447  // In syncing state, we already know the cell we want to camp on, and the SDR is tuned to the required
448  // sample rate for its number of PRB / bandwidth. We now synchronize PSS/SSS and receive the MIB once again
449  // at this sample rate.
450  unsigned max_frames = 200;
451  bool sfn_sync = false;
452  while (!sfn_sync && max_frames-- > 0) {
453  sfn_sync = phy.synchronize_subframe();
454  }
455 
456  if (max_frames == 0 && !sfn_sync) {
457  // Failed. Back to square one: search state.
458  spdlog::warn("Synchronization failed. Going back to search state.");
459  state = searching;
460  sleep(1);
461  }
462 
463  if (sfn_sync) {
464  // We're locked on to the cell, and have succesfully received the MIB at the target sample rate.
465  spdlog::info("Decoded MIB at target sample rate, TTI is {}. Subframe synchronized.", phy.tti());
466 
467  // Set the cell parameters in the CAS processor
468  cas_processor.set_cell(phy.cell());
469 
470  for (int i = 0; i < thread_cnt; i++) {
471  mbsfn_processors[i]->unlock();
472  }
473 
474  // Get the initial TTI / subframe ID (= system frame number * 10 + subframe number)
475  tti = phy.tti();
476  // Reset the RRC
477  rrc.reset();
478 
479  // Ready to receive actual data. Go to processing state.
480  state = processing;
481 
482  // If sample file creation is enabled, start writing out samples now that we're at the target sample rate
484  }
485  } else { // processing
486  int mb_idx = 0;
487  while (state == processing) {
488  tti = (tti + 1) % 10240; // Clamp the TTI
489  unsigned sfn = tti / 10;
490  if (phy.is_cas_subframe(tti)) {
491  // Get the samples from the SDR interface, hand them to a CAS processor, and start it
492  // on a thread from the pool.
493  if (!restart && phy.get_next_frame(cas_processor.rx_buffer(), cas_processor.rx_buffer_size())) {
494  spdlog::debug("sending tti {} to regular processor", tti);
495  pool.push([ObjectPtr = &cas_processor, tti, &rest_handler] {
496  if (ObjectPtr->process(tti)) {
497  // Set constellation diagram data and rx params for CAS in the REST API handler
498  rest_handler.add_cinr_value(ObjectPtr->cinr_db());
499  }
500  });
501 
502 
503  if (phy.nof_mbsfn_prb() != mbsfn_nof_prb)
504  {
505  // Handle the non-LTE bandwidths (6, 7 and 8 MHz). In these cases, CAS stays at the original bandwidth, but the MBSFN
506  // portion of the frames can be wider. We need to...
507 
509 
510  // ...adjust the SDR's sample rate to fit the wider MBSFN bandwidth...
511  unsigned new_srate = srsran_sampling_freq_hz(mbsfn_nof_prb);
512  spdlog::info("Setting sample rate {} Mhz for MBSFN with {} PRB / {} Mhz channel width", new_srate/1000000.0, mbsfn_nof_prb,
513  mbsfn_nof_prb * 0.2);
514  sdr.stop();
515 
516  bandwidth = (mbsfn_nof_prb * 200000) * 1.2;
517  sdr.tune(frequency, new_srate, bandwidth, gain, antenna, use_agc);
518 
519  // ... configure the PHY and CAS processor to decode a narrow CAS and wider MBSFN, and move back to syncing state
520  // after reconfiguring and restarting the SDR.
521  phy.set_cell();
522  cas_processor.set_cell(phy.cell());
523 
524  sdr.start();
525  spdlog::info("Synchronizing subframe after PRB extension");
526  state = syncing;
527  }
528  } else {
529  // Failed to receive data, or sync lost. Go back to searching state.
530  sdr.stop();
531  sample_rate = search_sample_rate; // sample rate for searching
533  sdr.start();
534  rrc.reset();
535  phy.reset();
536 
537  sleep(1);
538  state = searching;
539  }
540  } else {
541  // All other frames in FeMBMS dedicated mode are MBSFN frames.
542  spdlog::debug("sending tti {} to mbsfn proc {}", tti, mb_idx);
543 
544  // Get the samples from the SDR interface, hand them to an MNSFN processor, and start it
545  // on a thread from the pool. Getting the buffer pointer from the pool also locks this processor.
546  if (!restart && phy.get_next_frame(mbsfn_processors[mb_idx]->get_rx_buffer_and_lock(), mbsfn_processors[mb_idx]->rx_buffer_size())) {
547  if (phy.mcch_configured() && phy.is_mbsfn_subframe(tti)) {
548  // If data frm SIB1/SIB13 has been received in CAS, configure the processors accordingly
549  if (!mbsfn_processors[mb_idx]->mbsfn_configured()) {
550  srsran_scs_t scs = SRSRAN_SCS_15KHZ;
551  switch (phy.mbsfn_subcarrier_spacing()) {
552  case Phy::SubcarrierSpacing::df_15kHz: scs = SRSRAN_SCS_15KHZ; break;
553  case Phy::SubcarrierSpacing::df_7kHz5: scs = SRSRAN_SCS_7KHZ5; break;
554  case Phy::SubcarrierSpacing::df_1kHz25: scs = SRSRAN_SCS_1KHZ25; break;
555  }
556  auto cell = phy.cell();
557  cell.nof_prb = cell.mbsfn_prb;
558  mbsfn_processors[mb_idx]->set_cell(cell);
559  mbsfn_processors[mb_idx]->configure_mbsfn(phy.mbsfn_area_id(), scs);
560  }
561  pool.push([ObjectPtr = mbsfn_processors[mb_idx], tti] {
562  ObjectPtr->process(tti);
563  });
564  } else {
565  // Nothing to do yet, we lack the data from SIB1/SIB13
566  // Discard the samples and unlock the processor.
567  mbsfn_processors[mb_idx]->unlock();
568  }
569  } else {
570  // Failed to receive data, or sync lost. Go back to searching state.
571  spdlog::warn("Synchronization lost while processing. Going back to searching state.");
572  sdr.stop();
573  sample_rate = search_sample_rate; // sample rate for searching
575  sdr.start();
576 
577  state = searching;
578  sleep(1);
579  rrc.reset();
580  phy.reset();
581  }
582  mb_idx = static_cast<int>((mb_idx + 1) % thread_cnt);
583  }
584 
585  tick++;
586  if (tick%measurement_interval == 0) {
587  // It's time to output rx info to the measurement file and to syslog.
588  // Collect the relevant info and write it out.
589  std::vector<std::string> cols;
590 
591  spdlog::info("CINR {:.2f} dB", rest_handler.cinr_db() );
592  cols.push_back(std::to_string(rest_handler.cinr_db()));
593 
594  spdlog::info("PDSCH: MCS {}, BLER {}, BER {}",
595  rest_handler._pdsch.mcs,
596  ((rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0)),
597  rest_handler._pdsch.ber);
598  cols.push_back(std::to_string(rest_handler._pdsch.mcs));
599  cols.push_back(std::to_string(((rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0))));
600  cols.push_back(std::to_string(rest_handler._pdsch.ber));
601 
602  spdlog::info("MCCH: MCS {}, BLER {}, BER {}",
603  rest_handler._mcch.mcs,
604  ((rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0)),
605  rest_handler._mcch.ber);
606 
607  cols.push_back(std::to_string(rest_handler._mcch.mcs));
608  cols.push_back(std::to_string(((rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0))));
609  cols.push_back(std::to_string(rest_handler._mcch.ber));
610 
611  auto mch_info = phy.mch_info();
612  int mch_idx = 0;
613  std::for_each(std::begin(mch_info), std::end(mch_info), [&cols, &mch_idx, &rest_handler](Phy::mch_info_t const& mch) {
614  spdlog::info("MCH {}: MCS {}, BLER {}, BER {}",
615  mch_idx,
616  mch.mcs,
617  (rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0),
618  rest_handler._mch[mch_idx].ber);
619  cols.push_back(std::to_string(mch_idx));
620  cols.push_back(std::to_string(mch.mcs));
621  cols.push_back(std::to_string((rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0)));
622  cols.push_back(std::to_string(rest_handler._mch[mch_idx].ber));
623 
624  int mtch_idx = 0;
625  std::for_each(std::begin(mch.mtchs), std::end(mch.mtchs), [&mtch_idx](Phy::mtch_info_t const& mtch) {
626  spdlog::info(" MTCH {}: LCID {}, TMGI 0x{}, {}",
627  mtch_idx,
628  mtch.lcid,
629  mtch.tmgi,
630  mtch.dest);
631  mtch_idx++;
632  });
633  mch_idx++;
634  });
635  spdlog::info("-----");
636  if (enable_measurement_file) {
637  measurement_file.WriteLogValues(cols);
638  }
639  }
640  }
641  }
642  }
643 
644  // Main loop ended by signal. Free the MBSFN processors, and bail.
645  for (int i = 0; i < thread_cnt; i++) {
646  delete( mbsfn_processors[i] );
647  }
648 exit:
649  return 0;
650 }
state_t
Definition: RestHandler.h:39
@ syncing
Definition: RestHandler.h:39
@ searching
Definition: RestHandler.h:39
@ processing
Definition: RestHandler.h:39
Frame processor for CAS subframes.
cf_t ** rx_buffer()
Get a handle of the signal buffer to store samples for processing in.
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).
uint32_t rx_buffer_size()
Size of the signal buffer.
Network gateway component.
Definition: Gw.h:36
void init()
Creates the TUN interface according to params from Cfg.
Definition: Gw.cpp:99
Frame processor for MBSFN subframes.
Writes measurement data / current reception parameters to a file.
void WriteLogValues(const std::vector< std::string > &values)
Write a line containing the passed values.
The PHY component.
Definition: Phy.h:42
bool mcch_configured()
Return true if MCCH has been configured.
Definition: Phy.h:133
SubcarrierSpacing mbsfn_subcarrier_spacing()
Definition: Phy.h:185
unsigned nr_prb()
Get the current number of PRB.
Definition: Phy.h:98
uint8_t mbsfn_area_id()
Returns the current MBSFN area ID.
Definition: Phy.h:138
const std::vector< mch_info_t > & mch_info()
Definition: Phy.h:175
bool get_next_frame(cf_t **buffer, uint32_t size)
Get the sample data for the next subframe.
Definition: Phy.cpp:220
srsran_cell_t cell()
Get the current cell (with params adjusted for MBSFN)
Definition: Phy.h:91
bool synchronize_subframe()
Synchronizes PSS/SSS and tries to deocode the MIB.
Definition: Phy.cpp:59
uint8_t nof_mbsfn_prb()
Get number of PRB in MBSFN/PMCH.
Definition: Phy.h:153
void set_cell()
Definition: Phy.cpp:183
void reset()
Clear configuration values.
Definition: Phy.h:128
bool is_cas_subframe(unsigned tti)
Definition: Phy.cpp:303
uint32_t tti()
Get the current subframe TTI.
Definition: Phy.h:103
bool init()
Initialize the underlying components.
Definition: Phy.cpp:192
bool cell_search()
Search for a cell.
Definition: Phy.cpp:91
void set_nof_mbsfn_prb(uint8_t prb)
Override number of PRB in MBSFN/PMCH.
Definition: Phy.h:158
bool is_mbsfn_subframe(unsigned tti)
Definition: Phy.cpp:314
The RESTful API handler.
Definition: RestHandler.h:44
float cinr_db()
Current CINR value.
Definition: RestHandler.h:113
ChannelInfo _mcch
RX info for MCCH.
Definition: RestHandler.h:103
std::map< uint32_t, ChannelInfo > _mch
RX info for MCHs.
Definition: RestHandler.h:108
ChannelInfo _pdsch
RX info for PDSCH.
Definition: RestHandler.h:98
Simple RRC component between PHY and RLC.
Definition: Rrc.h:34
void reset()
Definition: Rrc.h:57
Interface to the SDR stick.
Definition: SdrReader.h:37
void stop()
Stop reading samples from the SDR.
Definition: SdrReader.cpp:245
void start()
Start reading samples from the SDR.
Definition: SdrReader.cpp:213
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.
Definition: SdrReader.cpp:65
void disableSampleFileWriting()
If sample file creation is enabled, writing samples stops after this call.
Definition: SdrReader.h:139
void enumerateDevices()
Prints a list of all available SDR devices.
Definition: SdrReader.cpp:48
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.
Definition: SdrReader.cpp:315
void clear_buffer()
Clear all samples from the rx buffers.
Definition: SdrReader.cpp:107
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.
Definition: SdrReader.cpp:166
void enableSampleFileWriting()
If sample file creation is enabled, writing samples starts after this call.
Definition: SdrReader.h:134
static Config cfg
Global configuration object.
Definition: main.cpp:165
static std::string antenna
Antenna input to be used.
Definition: main.cpp:172
static unsigned mbsfn_nof_prb
Definition: main.cpp:175
static bool use_agc
Definition: main.cpp:173
static unsigned sample_rate
Sample rate of the SDR.
Definition: main.cpp:167
static void print_version(FILE *stream, struct argp_state *state)
Print the program version in MAJOR.MINOR.PATCH format.
Definition: main.cpp:159
auto main(int argc, char **argv) -> int
Main entry point for the program.
Definition: main.cpp:217
static bool restart
Restart flag.
Definition: main.cpp:186
static unsigned cas_nof_prb
Definition: main.cpp:176
static uint32_t bandwidth
Low pass filter bandwidth for the SDR.
Definition: main.cpp:170
static unsigned search_sample_rate
Sample rate of the SDR.
Definition: main.cpp:168
const char * argp_program_bug_address
Definition: main.cpp:63
void set_params(const std::string &ant, unsigned fc, double g, unsigned sr, unsigned bw)
Set new SDR parameters and initialize resynchronisation.
Definition: main.cpp:198
static unsigned frequency
Center freqeuncy the SDR is tuned to.
Definition: main.cpp:169
static struct argp argp
Definition: main.cpp:153
static struct argp_option options[]
Definition: main.cpp:66
static double gain
Overall system gain for the SDR.
Definition: main.cpp:171
void(* argp_program_version_hook)(FILE *, struct argp_state *)
Definition: main.cpp:62
static auto parse_opt(int key, char *arg, struct argp_state *state) -> error_t
Parses the command line options into the arguments struct.
Definition: main.cpp:115
static char doc[]
Definition: main.cpp:64
std::vector< mtch_info_t > mtchs
Definition: Phy.h:172
Holds all options passed on the command line.
Definition: main.cpp:100
uint8_t file_bw
bandwidth of the sample file
Definition: main.cpp:106
const char * config_file
file path of the config file.
Definition: main.cpp:101
const char * write_sample_file
file path of the created sample file.
Definition: main.cpp:108
unsigned srs_log_level
srsLTE log level
Definition: main.cpp:103
int8_t override_nof_prb
ovride PRB number
Definition: main.cpp:104
unsigned log_level
log level
Definition: main.cpp:102
bool list_sdr_devices
Definition: main.cpp:109
const char * sample_file
file path of the sample file.
Definition: main.cpp:105