5G-MAG Reference Tools - MBMS Modem
Classes | Functions | Variables
main.cpp File Reference

Contains the program entry point, command line parameter handling, and the main runloop for data processing. More...

#include <argp.h>
#include <cstdlib>
#include <libconfig.h++>
#include "CasFrameProcessor.h"
#include "Gw.h"
#include "SdrReader.h"
#include "MbsfnFrameProcessor.h"
#include "MeasurementFileWriter.h"
#include "Phy.h"
#include "RestHandler.h"
#include "Rrc.h"
#include "Version.h"
#include "spdlog/async.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/syslog_sink.h"
#include "srsran/srsran.h"
#include "srsran/upper/pdcp.h"
#include "srsran/rlc/rlc.h"
#include "thread_pool.hpp"
Include dependency graph for main.cpp:

Go to the source code of this file.

Classes

struct  arguments
 Holds all options passed on the command line. More...
 

Functions

static void print_version (FILE *stream, struct argp_state *)
 Print the program version in MAJOR.MINOR.PATCH format. More...
 
static auto parse_opt (int key, char *arg, struct argp_state *state) -> error_t
 Parses the command line options into the arguments struct. More...
 
void set_params (const std::string &ant, unsigned fc, double g, unsigned sr, unsigned bw)
 Set new SDR parameters and initialize resynchronisation. More...
 
auto main (int argc, char **argv) -> int
 Main entry point for the program. More...
 

Variables

void(* argp_program_version_hook )(FILE *, struct argp_state *) = print_version
 
const char * argp_program_bug_address = "5G-MAG Reference Tools <reference-tools@5g-mag.com>"
 
static char doc [] = "5G-MAG-RT MBMS Modem Process"
 
static struct argp_option options []
 
static struct argp argp
 
static Config cfg
 Global configuration object. More...
 
static unsigned sample_rate = 7680000
 Sample rate of the SDR. More...
 
static unsigned search_sample_rate = 7680000
 Sample rate of the SDR. More...
 
static unsigned frequency = 667000000
 Center freqeuncy the SDR is tuned to. More...
 
static uint32_t bandwidth = 10000000
 Low pass filter bandwidth for the SDR. More...
 
static double gain = 0.9
 Overall system gain for the SDR. More...
 
static std::string antenna = "LNAW"
 Antenna input to be used. More...
 
static bool use_agc = false
 
static unsigned mbsfn_nof_prb = 0
 
static unsigned cas_nof_prb = 0
 
static bool restart = false
 Restart flag. More...
 

Detailed Description

Contains the program entry point, command line parameter handling, and the main runloop for data processing.

Definition in file main.cpp.

Function Documentation

◆ main()

auto main ( int  argc,
char **  argv 
) -> int

Main entry point for the program.

Parameters
argcCommand line agument count
argvCommand line arguments
Returns
0 on clean exit, -1 on failure

Definition at line 224 of file main.cpp.

224  {
225  struct arguments arguments;
226  /* Default values */
227  arguments.config_file = "/etc/5gmag-rt.conf";
228  arguments.sample_file = nullptr;
229  arguments.write_sample_file = nullptr;
230  argp_parse(&argp, argc, argv, 0, nullptr, &arguments);
231 
232  // Read and parse the configuration file
233  try {
234  cfg.readFile(arguments.config_file);
235  } catch(const FileIOException &fioex) {
236  spdlog::error("I/O error while reading config file at {}. Exiting.", arguments.config_file);
237  exit(1);
238  } catch(const ParseException &pex) {
239  spdlog::error("Config parse error at {}:{} - {}. Exiting.",
240  pex.getFile(), pex.getLine(), pex.getError());
241  exit(1);
242  }
243 
244  // Set up logging
245  std::string ident = "modem";
246  auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID | LOG_PERROR | LOG_CONS );
247 
248  spdlog::set_level(
249  static_cast<spdlog::level::level_enum>(arguments.log_level));
250  spdlog::set_pattern("[%H:%M:%S.%f %z] [%^%l%$] [thr %t] %v");
251 
252  spdlog::set_default_logger(syslog_logger);
253  spdlog::info("5g-mag-rt modem v{}.{}.{} starting up", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
254 
255  // Init and tune the SDR
256  auto rx_channels = 1;
257  cfg.lookupValue("modem.sdr.rx_channels", rx_channels);
258  spdlog::info("Initialising SDR with {} RX channel(s)", rx_channels);
259  SdrReader sdr(cfg, rx_channels);
261  sdr.enumerateDevices();
262  exit(0);
263  }
264 
265  std::string sdr_dev = "driver=lime";
266  cfg.lookupValue("modem.sdr.device_args", sdr_dev);
268  spdlog::error("Failed to initialize I/Q data source.");
269  exit(1);
270  }
271 
272  cfg.lookupValue("modem.sdr.search_sample_rate_hz", sample_rate);
274 
275  unsigned long long center_frequency = frequency;
276  if (!cfg.lookupValue("modem.sdr.center_frequency_hz", center_frequency)) {
277  spdlog::error("Unable to parse center_frequency_hz - values must have a ‘L’ character appended");
278  exit(1);
279  }
280  // We needed unsigned long long for correct parsing,
281  // but unsigned is required
282  if (center_frequency <= UINT_MAX) {
283  frequency = static_cast<unsigned>(center_frequency);
284  } else {
285  spdlog::error("Configured center_frequency_hz is {}, maximal value supported is {}.",
286  center_frequency, UINT_MAX);
287  exit(1);
288  }
289 
290 
291  cfg.lookupValue("modem.sdr.normalized_gain", gain);
292  cfg.lookupValue("modem.sdr.antenna", antenna);
293  cfg.lookupValue("modem.sdr.use_agc", use_agc);
294 
295  if (!sdr.tune(frequency, sample_rate, bandwidth, gain, antenna, use_agc)) {
296  spdlog::error("Failed to set initial center frequency. Exiting.");
297  exit(1);
298  }
299 
300  set_srsran_verbose_level(arguments.log_level <= 1 ? SRSRAN_VERBOSE_DEBUG : SRSRAN_VERBOSE_NONE);
301  srsran_use_standard_symbol_size(true);
302 
303  // Create a thread pool for the frame processors
304  unsigned thread_cnt = 4;
305  cfg.lookupValue("modem.phy.threads", thread_cnt);
306  int phy_prio = 10;
307  cfg.lookupValue("modem.phy.thread_priority_rt", phy_prio);
308  thread_pool pool{ thread_cnt + 1, phy_prio };
309 
310  // Elevate execution to real time scheduling
311  struct sched_param thread_param = {};
312  thread_param.sched_priority = 20;
313  cfg.lookupValue("modem.phy.main_thread_priority_rt", thread_param.sched_priority);
314 
315  spdlog::info("Raising main thread to realtime scheduling priority {}", thread_param.sched_priority);
316 
317  int error = pthread_setschedparam(pthread_self(), SCHED_RR, &thread_param);
318  if (error != 0) {
319  spdlog::error("Cannot set main thread priority to realtime: {}. Thread will run at default priority.", strerror(error));
320  }
321 
322  bool enable_measurement_file = false;
323  cfg.lookupValue("modem.measurement_file.enabled", enable_measurement_file);
324  MeasurementFileWriter measurement_file(cfg);
325 
326  // Create the layer components: Phy, RLC, RRC and GW
327  Phy phy(
328  cfg,
329  std::bind(&SdrReader::get_samples, &sdr, _1, _2, _3), // NOLINT
330  arguments.file_bw ? arguments.file_bw * 5 : 25,
332  rx_channels);
333 
334  phy.init();
335 
336  srsran::pdcp pdcp(nullptr, "PDCP");
337  srsran::rlc rlc("RLC");
338  srsran::timer_handler timers;
339 
340  Rrc rrc(cfg, phy, rlc);
341  Gw gw(cfg, phy);
342  gw.init();
343 
344  rlc.init(&pdcp, &rrc, &timers, 0 /* RB_ID_SRB0 */);
345  pdcp.init(&rlc, &rrc, &gw);
346 
347  auto srs_level = srslog::basic_levels::none;
348  switch (arguments.srs_log_level) {
349  case 0: srs_level = srslog::basic_levels::debug; break;
350  case 1: srs_level = srslog::basic_levels::info; break;
351  case 2: srs_level = srslog::basic_levels::warning; break;
352  case 3: srs_level = srslog::basic_levels::error; break;
353  case 4: srs_level = srslog::basic_levels::none; break;
354  }
355 
356  // Configure srsLTE logging
357  auto& mac_log = srslog::fetch_basic_logger("MAC", false);
358  mac_log.set_level(srs_level);
359  auto& phy_log = srslog::fetch_basic_logger("PHY", false);
360  phy_log.set_level(srs_level);
361  auto& rlc_log = srslog::fetch_basic_logger("RLC", false);
362  rlc_log.set_level(srs_level);
363  auto& asn1_log = srslog::fetch_basic_logger("ASN1", false);
364  asn1_log.set_level(srs_level);
365 
366 
367  state_t state = searching;
368 
369  // Create the RESTful API handler
370  std::string uri = "http://0.0.0.0:3010/modem-api/";
371  cfg.lookupValue("modem.restful_api.uri", uri);
372  spdlog::info("Starting RESTful API handler at {}", uri);
373  RestHandler rest_handler(cfg, uri, state, sdr, phy, set_params);
374 
375  // Initialize one CAS and thered_cnt MBSFN frame processors
376  CasFrameProcessor cas_processor(cfg, phy, rlc, rest_handler, rx_channels);
377  if (!cas_processor.init()) {
378  spdlog::error("Failed to create CAS processor. Exiting.");
379  exit(1);
380  }
381 
382  // We need the cas processor to be accesible within the rest_handler object to gather all the values display in the rt-wui
383  rest_handler.set_cas_processor(&cas_processor);
384 
385  std::vector<MbsfnFrameProcessor*> mbsfn_processors;
386  for (int i = 0; i < thread_cnt; i++) {
387  auto p = new MbsfnFrameProcessor(cfg, rlc, phy, mac_log, rest_handler, rx_channels);
388  if (!p->init()) {
389  spdlog::error("Failed to create MBSFN processor. Exiting.");
390  exit(1);
391  }
392  mbsfn_processors.push_back(p);
393  }
394 
395  rest_handler.start(); // Start the listener, we need to do it after storing the cas into the rest_handler, otherwise we will get segfault.
396  // Start receiving sample data
397  sdr.start();
398 
399  // Variables to store the measure BLER of PDSCH and MCH/MCCH.
400  uint32_t mch_bler_global = 0;
401  uint32_t mcch_bler_global = 0;
402  uint32_t pdsch_bler_global = 0;
403  uint32_t mch_total_global = 0;
404  uint32_t mcch_total_global = 0;
405  uint32_t pdsch_total_global = 0;
406 
407  // Variables to store the times the receiver losses the signal and the lost subframes between resynchronizations.
408  uint32_t sync_losses = 0;
409  uint32_t lost_subframes = 0;
410  uint32_t measurements = 0.0f;
411 
412  uint32_t tti = 0;
413 
414  float measurement_interval_f = 5;
415  cfg.lookupValue("modem.measurement_file.interval_secs", measurement_interval_f);
416  uint32_t measurement_interval = measurement_interval_f * 1000;
417  uint32_t tick = 0;
418 
419  // Initial state: searching a cell
420  state = searching;
421 
422  uint8_t mb_idx = 0;
423  // Start the main processing loop
424  for (;;) { // Only one main loop, any time therè's a change of state we force next iteration with continue. This way there's no need of nested loops within the cases.
425  switch (state) {
426  case processing: { // processing
427  tti = (tti + 1) % 10240; // Clamp the TTI
428  if (phy.is_cas_subframe(tti)) {
429  // Get the samples from the SDR interface, hand them to a CAS processor, and start it
430  // on a thread from the pool.
431  if (!restart && phy.get_next_frame(cas_processor.get_rx_buffer_and_lock(), cas_processor.rx_buffer_size())) {
432  spdlog::debug("sending tti {} to regular processor", tti);
433  pool.push([ObjectPtr = &cas_processor, tti, &rest_handler] {
434  if (ObjectPtr->process(tti)) {
435  // Set constellation diagram data and rx params for CAS in the REST API handler
436  rest_handler.add_cinr_value(ObjectPtr->cinr_db());
437  }
438  });
439 
440 
441  if (phy.nof_mbsfn_prb() != mbsfn_nof_prb)
442  {
443  // Handle the non-LTE bandwidths (6, 7 and 8 MHz). In these cases, CAS stays at the original bandwidth, but the MBSFN
444  // portion of the frames can be wider. We need to...
445 
446  mbsfn_nof_prb = phy.nof_mbsfn_prb();
447 
448  // ...adjust the SDR's sample rate to fit the wider MBSFN bandwidth...
449  unsigned new_srate = srsran_sampling_freq_hz(mbsfn_nof_prb);
450  spdlog::info("Setting sample rate {} Mhz for MBSFN with {} PRB / {} Mhz channel width", new_srate/1000000.0, mbsfn_nof_prb,
451  mbsfn_nof_prb * 0.2);
452  sdr.stop();
453 
454  bandwidth = (mbsfn_nof_prb * 200000) * 1.2;
455  sdr.tune(frequency, new_srate, bandwidth, gain, antenna, use_agc);
456 
457  // ... configure the PHY and CAS processor to decode a narrow CAS and wider MBSFN, and move back to syncing state
458  // after reconfiguring and restarting the SDR.
459  phy.set_cell();
460  cas_processor.set_cell(phy.cell());
461 
462  sdr.start();
463  spdlog::info("Synchronizing subframe after PRB extension");
464  state = syncing;
465  }
466  } else {
467  // Failed to receive data, or sync lost. Go back to searching state.
468  spdlog::warn("Synchronization lost while processing. Going back to searching state.");
469  sync_losses++;
470  state = syncing;
471  }
472  } else {
473  // All other frames in FeMBMS dedicated mode are MBSFN frames.
474  spdlog::debug("sending tti {} to mbsfn proc {}", tti, mb_idx);
475 
476  // Get the samples from the SDR interface, hand them to an MNSFN processor, and start it
477  // on a thread from the pool. Getting the buffer pointer from the pool also locks this processor.
478  if (!restart && phy.get_next_frame(mbsfn_processors[mb_idx]->get_rx_buffer_and_lock(), mbsfn_processors[mb_idx]->rx_buffer_size())) {
479  if (phy.mcch_configured() && phy.is_mbsfn_subframe(tti)) {
480  // If data frm SIB1/SIB13 has been received in CAS, configure the processors accordingly
481  if (!mbsfn_processors[mb_idx]->mbsfn_configured()) {
482  srsran_scs_t scs = SRSRAN_SCS_15KHZ;
483  switch (phy.mbsfn_subcarrier_spacing()) {
484  case Phy::SubcarrierSpacing::df_15kHz: scs = SRSRAN_SCS_15KHZ; break;
485  case Phy::SubcarrierSpacing::df_7kHz5: scs = SRSRAN_SCS_7KHZ5; break;
486  case Phy::SubcarrierSpacing::df_1kHz25: scs = SRSRAN_SCS_1KHZ25; break;
487  }
488  auto cell = phy.cell();
489  cell.nof_prb = cell.mbsfn_prb;
490  mbsfn_processors[mb_idx]->set_cell(cell);
491  mbsfn_processors[mb_idx]->configure_mbsfn(phy.mbsfn_area_id(), scs);
492  }
493  pool.push([ObjectPtr = mbsfn_processors[mb_idx], tti] {
494  ObjectPtr->process(tti);
495  });
496  } else {
497  // Nothing to do yet, we lack the data from SIB1/SIB13
498  // Discard the samples and unlock the processor.
499  mbsfn_processors[mb_idx]->unlock();
500  }
501  } else {
502  // Failed to receive data, or sync lost. Go back to searching state.
503  spdlog::warn("Synchronization lost while processing. Going back to searching state.");
504  sync_losses++;
505  state = syncing;
506  }
507  mb_idx = static_cast<int>((mb_idx + 1) % thread_cnt);
508  }
509  }
510  break;
511 
512  case searching: {
513  if (restart) { // Triggered from the rt-wui
514  sdr.stop();
515  sample_rate = search_sample_rate; // sample rate for searching
517  sdr.start();
518  }
519 
520  // We're at the search sample rate, and there's no point in creating a sample file. rtop the sample writer, if enabled.
521  //sdr.disableSampleFileWriting();
522 
523  // In searching state, clear the receive buffer and try to find a cell at the configured frequency and synchronize with it
524  restart = false;
525  // sdr.clear_buffer();
526  bool cell_found = phy.cell_search();
527  if (cell_found) {
528  // A cell has been found. We now know the required number of PRB = bandwidth of the carrier. Set the approproiate
529  // sample rate...
530  cas_nof_prb = mbsfn_nof_prb = phy.nr_prb();
531 
533  // Samples files are recorded at a fixed sample rate that can be determined from the bandwidth command line argument.
534  // 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
535  // PHY to decode a narrow CAS from a wider channel.
537  phy.set_nof_mbsfn_prb(mbsfn_nof_prb);
538  phy.set_cell();
539  } else {
540  // When decoding from the air, configure the SDR accordingly
541  unsigned new_srate = srsran_sampling_freq_hz(cas_nof_prb);
542  spdlog::info("Setting sample rate {} Mhz for {} PRB / {} Mhz channel width", new_srate/1000000.0, phy.nr_prb(),
543  phy.nr_prb() * 0.2);
544  sdr.stop();
545  sdr.clear_buffer();
546  bandwidth = (cas_nof_prb * 200000) * 1.2;
547  sdr.tune(frequency, new_srate, bandwidth, gain, antenna, use_agc);
548  //sleep(1);
549 
550  sdr.start();
551  }
552  spdlog::debug("Synchronizing subframe");
553  // ... and move to syncing state.
554  state = syncing;
555  } else {
556  //sleep(1);
557  }
558  }
559  break;
560  case syncing: {
561  // In syncing state, we already know the cell we want to camp on, and the SDR is tuned to the required
562  // sample rate for its number of PRB / bandwidth. We now synchronize PSS/SSS and receive the MIB once again
563  // at this sample rate.
564  if (restart) { // The only way of going to search is restarting
565  state = searching; // Jump to searching where the restart is actually handled.
566  continue; // We need to skip the next step and go to the next iteration.
567  }
568 
569  bool sfn_sync = false;
570  sfn_sync = phy.synchronize_subframe();
571 
572  if (sfn_sync) {
573  // We're locked on to the cell, and have succesfully received the MIB at the target sample rate.
574  lost_subframes += (((phy.tti() < tti) * 10240 + phy.tti())-tti) * cas_processor.is_started() ; // cas_processor has started when it has a valid cell set.
575  spdlog::info("Decoded MIB at target sample rate, TTI is {}. Subframe synchronized, sync lost in TTI {}, {} subframes lost, {} total subframe lost, sync losses {}.", phy.tti(), tti, (((phy.tti() < tti) * 10240 + phy.tti())-tti) * cas_processor.is_started(), lost_subframes, sync_losses);
576 
577  // Set the cell parameters in the CAS processor, and set started to true
578  cas_processor.set_cell(phy.cell());
579 
580  for (int i = 0; i < thread_cnt; i++) {
581  mbsfn_processors[i]->unlock();
582  }
583  cas_processor.unlock(); // We need to unlock because we felt here from processing after calling get_rx_buffer_and_lock()
584 
585  // Get the initial TTI / subframe ID (= system frame number * 10 + subframe number)
586  tti = phy.tti();
587  // Reset the RRC
588  rrc.reset();
589 
590  // Ready to receive actual data. Go to processing state.
591  state = processing;
592 
593  // If sample file creation is enabled, start writing out samples now that we're at the target sample rate
594  sdr.enableSampleFileWriting();
595  }
596  }
597  break;
598  default:
599  //we are not supposed to be here.
600  break;
601  }
602 
603  tick++;
604  if (tick%measurement_interval == 0) {
605  // It's time to output rx info to the measurement file and to syslog.
606  // Collect the relevant info and write it out.
607  std::vector<std::string> cols;
608  cols.reserve(11);
609 
610  if (state == processing) {
611  spdlog::info("CINR {:.2f} dB", rest_handler.cinr_db() );
612  cols.push_back(std::to_string((float)rest_handler.cinr_db()));
613 
614  // Wait to finish and lock, we don't want to update total and errors independently. Yes, it's a blocking solution, but, what other way is possible?
615  cas_processor.lock();
616 
617  spdlog::info("PDSCH: MCS {}, BLER {}",
618  rest_handler._pdsch.mcs,
619  ((rest_handler._pdsch.errors > 0 && rest_handler._pdsch.total > 0) ? (rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0) : 0));
620 
621  cols.push_back(std::to_string(rest_handler._pdsch.mcs));
622  cols.push_back(std::to_string(((rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0))));
623 
624  pdsch_bler_global += rest_handler._pdsch.errors;
625  pdsch_total_global += rest_handler._pdsch.total;
626  rest_handler._pdsch.errors = 0;
627  rest_handler._pdsch.total = 0;
628  // We are done with the CAS
629  cas_processor.unlock();
630 
631  // Wait for all the mbsfn processors to finish, to avoid having an update on total or errors while accesing to the values that leads to having a wrong BLER.
632  for (int i = 0; i < thread_cnt; i++) {
633  mbsfn_processors[i]->lock();
634  }
635  spdlog::info("MCCH: MCS {}, BLER {}",
636  rest_handler._mcch.mcs,
637  ((rest_handler._mcch.errors > 0 && rest_handler._mcch.total > 0) ? (rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0) : 0));
638 
639  cols.push_back(std::to_string(rest_handler._mcch.mcs));
640  cols.push_back(std::to_string(((rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0))));
641 
642  cols.push_back(std::to_string(tti)); // Current TTI
643  cols.emplace_back(std::string("")); // Blank to maintain the column, only used when desync.
644  cols.push_back(std::to_string(lost_subframes)); // Total amount of lost subframes
645 
646  auto mch_info = phy.mch_info();
647  int mch_idx = 0;
648  std::for_each(std::begin(mch_info), std::end(mch_info), [&cols, &mch_idx, &rest_handler, &mch_bler_global, &mch_total_global](Phy::mch_info_t const& mch) {
649 
650  spdlog::info("MCH {}: MCS {}, BLER {}",
651  mch_idx,
652  mch.mcs,
653  ((rest_handler._mch[mch_idx].errors > 0 && rest_handler._mch[mch_idx].total > 0) ? (rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0) : 0));
654 
655  cols.push_back(std::to_string(mch_idx));
656  cols.push_back(std::to_string(mch.mcs));
657  cols.push_back(std::to_string((rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0)));
658 
659  int mtch_idx = 0;
660  std::for_each(std::begin(mch.mtchs), std::end(mch.mtchs), [&mtch_idx, &mch_bler_global, &mch_total_global](Phy::mtch_info_t const& mtch) {
661  spdlog::info(" MTCH {}: LCID {}, TMGI 0x{}, {}",
662  mtch_idx,
663  mtch.lcid,
664  mtch.tmgi,
665  mtch.dest);
666  mtch_idx++;
667  });
668 
669  mch_bler_global += rest_handler._mch[mch_idx].errors;
670  mch_total_global += rest_handler._mch[mch_idx].total;
671  rest_handler._mch[mch_idx].errors = 0;
672  rest_handler._mch[mch_idx].total = 0;
673  mch_idx++;
674  });
675 
676  mcch_bler_global += rest_handler._mcch.errors;
677  mcch_total_global += rest_handler._mcch.total;
678  rest_handler._mcch.errors = 0;
679  rest_handler._mcch.total = 0;
680  // We can unlock cas and mbsfn processor at this point, every variable has been saved in the rest_handler.
681  for (int i = 0; i < thread_cnt; i++) {
682  mbsfn_processors[i]->unlock();
683  }
684  } else if (state == syncing) { // In syncing and searching states we place in every row and column NaN, this way is easier to process after, since every time measured theres always a row in the csv.
685  cols.emplace_back(std::string("NOT SYNC - SYNCING..."));
686  cols.emplace_back(std::string("nan"));
687  cols.emplace_back(std::string("nan"));
688  cols.emplace_back(std::string("nan"));
689  cols.emplace_back(std::string("nan"));
690  cols.emplace_back(std::string("nan"));
691  cols.push_back(std::to_string(sync_losses));
692  cols.push_back(std::to_string(lost_subframes));
693  cols.emplace_back(std::string("nan"));
694  cols.emplace_back(std::string("nan"));
695  cols.emplace_back(std::string("nan"));
696 
697  } else {
698  cols.emplace_back(std::string("SEARCHING FOR A CELL..."));
699  cols.emplace_back(std::string("nan"));
700  cols.emplace_back(std::string("nan"));
701  cols.emplace_back(std::string("nan"));
702  cols.emplace_back(std::string("nan"));
703  cols.emplace_back(std::string("nan"));
704  cols.emplace_back(std::string(""));
705  cols.push_back(std::to_string(lost_subframes));
706  cols.emplace_back(std::string("nan"));
707  cols.emplace_back(std::string("nan"));
708  cols.emplace_back(std::string("nan"));
709  }
710 
711  if (enable_measurement_file) {
712  measurement_file.WriteLogValues(cols);
713  }
714  measurements++;
715 
716  spdlog::info("------ Global statistics ------ \n\t\tMCH BLER {}, \n\t\tMCH TOTAL ERRORS {}, \n\t\tMCCH BLER: {}, \n\t\tPDSCH BLER: {}, \n\t\tSYNC LOSSES: {}, \n\t\tSF PROCESSED: {}",
717  ((mch_bler_global > 0 && mch_total_global > 0) ? (mch_bler_global * 1.0) / (mch_total_global * 1.0) : 0),
718  mch_bler_global,
719  ((mcch_bler_global > 0 && mcch_total_global > 0) ? (mcch_bler_global * 1.0) / (mcch_total_global * 1.0) : 0),
720  ((pdsch_bler_global > 0 && pdsch_total_global > 0) ? (pdsch_bler_global * 1.0) / (pdsch_total_global * 1.0) : 0),
721  sync_losses,
722  tick);
723 
724  spdlog::info("Total subframe lost {}, sync losses {}.", lost_subframes, sync_losses);
725 
726  }
727  }
728 
729  // Main loop ended by signal. Free the MBSFN processors, and bail.
730  for (int i = 0; i < thread_cnt; i++) {
731  delete( mbsfn_processors[i] );
732  }
733 exit:
734  return 0;
735 }
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.
Network gateway component.
Definition: Gw.h:36
Frame processor for MBSFN subframes.
Writes measurement data / current reception parameters to a file.
The PHY component.
Definition: Phy.h:42
The RESTful API handler.
Definition: RestHandler.h:47
Simple RRC component between PHY and RLC.
Definition: Rrc.h:34
Interface to the SDR stick.
Definition: SdrReader.h:37
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:345
static Config cfg
Global configuration object.
Definition: main.cpp:172
static std::string antenna
Antenna input to be used.
Definition: main.cpp:179
static unsigned mbsfn_nof_prb
Definition: main.cpp:182
static bool use_agc
Definition: main.cpp:180
static unsigned sample_rate
Sample rate of the SDR.
Definition: main.cpp:174
static bool restart
Restart flag.
Definition: main.cpp:193
static unsigned cas_nof_prb
Definition: main.cpp:183
static uint32_t bandwidth
Low pass filter bandwidth for the SDR.
Definition: main.cpp:177
static unsigned search_sample_rate
Sample rate of the SDR.
Definition: main.cpp:175
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:205
static unsigned frequency
Center freqeuncy the SDR is tuned to.
Definition: main.cpp:176
static struct argp argp
Definition: main.cpp:160
static double gain
Overall system gain for the SDR.
Definition: main.cpp:178
std::vector< mtch_info_t > mtchs
Definition: Phy.h:250
Holds all options passed on the command line.
Definition: main.cpp:103
uint8_t file_bw
bandwidth of the sample file
Definition: main.cpp:109
const char * config_file
file path of the config file.
Definition: main.cpp:104
const char * write_sample_file
file path of the created sample file.
Definition: main.cpp:111
bool repeat_sample_file
Definition: main.cpp:113
unsigned srs_log_level
srsLTE log level
Definition: main.cpp:106
int8_t override_nof_prb
ovride PRB number
Definition: main.cpp:107
unsigned log_level
log level
Definition: main.cpp:105
bool list_sdr_devices
Definition: main.cpp:112
const char * sample_file
file path of the sample file.
Definition: main.cpp:108

◆ parse_opt()

static auto parse_opt ( int  key,
char *  arg,
struct argp_state *  state 
) -> error_t
static

Parses the command line options into the arguments struct.

Definition at line 119 of file main.cpp.

119  {
120  auto arguments = static_cast<struct arguments *>(state->input);
121  switch (key) {
122  case 'c':
123  arguments->config_file = arg;
124  break;
125  case 'l':
126  arguments->log_level = static_cast<unsigned>(strtoul(arg, nullptr, 10));
127  break;
128  case 's':
130  static_cast<unsigned>(strtoul(arg, nullptr, 10));
131  break;
132  case 'f':
133  arguments->sample_file = arg;
134  break;
135  case 'w':
137  break;
138  case 'b':
139  arguments->file_bw = static_cast<uint8_t>(strtoul(arg, nullptr, 10));
140  break;
141  case 'p':
143  static_cast<int8_t>(strtol(arg, nullptr, 10));
144  break;
145  case 'd':
146  arguments->list_sdr_devices = true;
147  break;
148  case 'r':
150  break;
151  case ARGP_KEY_ARG:
152  argp_usage(state);
153  break;
154  default:
155  return ARGP_ERR_UNKNOWN;
156  }
157  return 0;
158 }

◆ print_version()

void print_version ( FILE *  stream,
struct argp_state *  state 
)
static

Print the program version in MAJOR.MINOR.PATCH format.

Definition at line 166 of file main.cpp.

166  {
167  fprintf(stream, "%s.%s.%s\n", std::to_string(VERSION_MAJOR).c_str(),
168  std::to_string(VERSION_MINOR).c_str(),
169  std::to_string(VERSION_PATCH).c_str());
170 }

◆ set_params()

void set_params ( const std::string &  ant,
unsigned  fc,
double  g,
unsigned  sr,
unsigned  bw 
)

Set new SDR parameters and initialize resynchronisation.

This function is used by the RESTful API handler to modify the SDR params.

Parameters
antName of the antenna input (For LimeSDR Mini: LNAW, LNAL)
fcCenter frequency to tune to (in Hz)
gainTotal system gain to set [0..1]
srSample rate (in Hz)
bwLow pass filter bandwidth (in Hz)

Definition at line 205 of file main.cpp.

205  {
206  sample_rate = sr;
207  frequency = fc;
208  bandwidth = bw;
209  antenna = ant;
210  gain = g;
211  spdlog::info("RESTful API requesting new parameters: fc {}, bw {}, rate {}, gain {}, antenna {}",
213 
214  restart = true;
215 }

Variable Documentation

◆ antenna

std::string antenna = "LNAW"
static

Antenna input to be used.

Definition at line 179 of file main.cpp.

◆ argp

struct argp argp
static
Initial value:
= {options, parse_opt, nullptr, doc,
nullptr, nullptr, nullptr}
static struct argp_option options[]
Definition: main.cpp:66
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:119
static char doc[]
Definition: main.cpp:64

Definition at line 119 of file main.cpp.

◆ argp_program_bug_address

const char* argp_program_bug_address = "5G-MAG Reference Tools <reference-tools@5g-mag.com>"

Definition at line 63 of file main.cpp.

◆ argp_program_version_hook

void(* argp_program_version_hook) (FILE *, struct argp_state *) ( FILE *  ,
struct argp_state *   
) = print_version

Definition at line 62 of file main.cpp.

◆ bandwidth

uint32_t bandwidth = 10000000
static

Low pass filter bandwidth for the SDR.

Definition at line 177 of file main.cpp.

◆ cas_nof_prb

unsigned cas_nof_prb = 0
static

Definition at line 183 of file main.cpp.

◆ cfg

Config cfg
static

Global configuration object.

Definition at line 172 of file main.cpp.

◆ doc

char doc[] = "5G-MAG-RT MBMS Modem Process"
static

Definition at line 64 of file main.cpp.

◆ frequency

unsigned frequency = 667000000
static

Center freqeuncy the SDR is tuned to.

Definition at line 176 of file main.cpp.

◆ gain

double gain = 0.9
static

Overall system gain for the SDR.

Definition at line 178 of file main.cpp.

◆ mbsfn_nof_prb

unsigned mbsfn_nof_prb = 0
static

Definition at line 182 of file main.cpp.

◆ options

struct argp_option options[]
static

Definition at line 64 of file main.cpp.

◆ restart

bool restart = false
static

Restart flag.

Setting this to true triggers resynchronization using the params set in the following parameters:

See also
sample_rate
frequency
bandwith
gain
antenna

Definition at line 193 of file main.cpp.

◆ sample_rate

unsigned sample_rate = 7680000
static

Sample rate of the SDR.

Definition at line 174 of file main.cpp.

◆ search_sample_rate

unsigned search_sample_rate = 7680000
static

Sample rate of the SDR.

Definition at line 175 of file main.cpp.

◆ use_agc

bool use_agc = false
static

Definition at line 180 of file main.cpp.