Main entry point for the program.
218 argp_parse(&argp, argc, argv, 0,
nullptr, &
arguments);
223 }
catch(
const FileIOException &fioex) {
226 }
catch(
const ParseException &pex) {
227 spdlog::error(
"Config parse error at {}:{} - {}. Exiting.",
228 pex.getFile(), pex.getLine(), pex.getError());
233 std::string ident =
"rp";
234 auto syslog_logger = spdlog::syslog_logger_mt(
"syslog", ident, LOG_PID | LOG_PERROR);
238 spdlog::set_pattern(
"[%H:%M:%S.%f %z] [%^%l%$] [thr %t] %v");
240 spdlog::set_default_logger(syslog_logger);
241 spdlog::info(
"OBECA rp v{}.{}.{} starting up", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
245 spdlog::info(
"Initialising LimeSDR");
249 spdlog::error(
"Failed to initialize I/Q data source.");
253 cfg.lookupValue(
"sdr.search_sample_rate_hz", sample_rate);
254 search_sample_rate = sample_rate;
255 cfg.lookupValue(
"sdr.center_frequency_hz", frequency);
256 cfg.lookupValue(
"sdr.normalized_gain", gain);
257 cfg.lookupValue(
"sdr.antenna", antenna);
259 if (!lime.tune(frequency, sample_rate, bandwidth, gain, antenna)) {
260 spdlog::error(
"Failed to set initial center frequency. Exiting.");
265 srslte_use_standard_symbol_size(
true);
268 unsigned thread_cnt = 4;
269 cfg.lookupValue(
"phy.threads", thread_cnt);
271 cfg.lookupValue(
"phy.thread_priority_rt", phy_prio);
272 thread_pool pool{ thread_cnt + 1, phy_prio };
275 struct sched_param thread_param = {};
276 thread_param.sched_priority = 20;
277 cfg.lookupValue(
"phy.main_thread_priority_rt", thread_param.sched_priority);
279 spdlog::info(
"Raising main thread to realtime scheduling priority {}", thread_param.sched_priority);
281 int error = pthread_setschedparam(pthread_self(), SCHED_RR, &thread_param);
283 spdlog::error(
"Cannot set main thread priority to realtime: {}. Thread will run at default priority.", strerror(error));
286 bool enable_measurement_file =
false;
287 cfg.lookupValue(
"measurement_file.enabled", enable_measurement_file);
299 srslte::pdcp pdcp(
nullptr,
"PDCP");
300 srslte::rlc rlc(
"RLC");
301 srslte::timer_handler timers;
303 Rrc rrc(cfg, phy, rlc);
307 rlc.init(&pdcp, &rrc, &timers, 0 );
308 pdcp.init(&rlc, &rrc, &gw);
310 auto srs_level = srslte::LOG_LEVEL_WARNING;
312 case 0: srs_level = srslte::LOG_LEVEL_DEBUG;
break;
313 case 1: srs_level = srslte::LOG_LEVEL_INFO;
break;
314 case 2: srs_level = srslte::LOG_LEVEL_WARNING;
break;
315 case 3: srs_level = srslte::LOG_LEVEL_ERROR;
break;
316 case 4: srs_level = srslte::LOG_LEVEL_NONE;
break;
320 srslte::log_ref rlc_log = srslte::logmap::get(
"RLC");
321 rlc_log->set_level(srs_level);
322 rlc_log->set_hex_limit(128);
324 srslte::log_ref mac_log = srslte::logmap::get(
"MAC");
325 mac_log->set_level(srs_level);
326 mac_log->set_hex_limit(128);
328 state_t state = searching;
331 std::string uri =
"http://0.0.0.0:3000/rp-api/";
332 cfg.lookupValue(
"restful_api.uri", uri);
333 spdlog::info(
"Starting RESTful API handler at {}", uri);
338 if (!cas_processor.init()) {
339 spdlog::error(
"Failed to create CAS processor. Exiting.");
343 std::vector<MbsfnFrameProcessor*> mbsfn_processors;
344 for (
int i = 0; i < thread_cnt; i++) {
347 spdlog::error(
"Failed to create MBSFN processor. Exiting.");
350 mbsfn_processors.push_back(p);
358 uint32_t measurement_interval = 5;
359 cfg.lookupValue(
"measurement_file.interval_secs", measurement_interval);
360 measurement_interval *= 1000;
368 if (state == searching) {
371 sample_rate = search_sample_rate;
372 lime.tune(frequency, sample_rate, bandwidth, gain, antenna);
378 bool cell_found = phy.cell_search();
382 cas_nof_prb = mbsfn_nof_prb = phy.nr_prb();
383 unsigned new_srate = srslte_sampling_freq_hz(cas_nof_prb);
384 spdlog::info(
"Setting sample rate {} Mhz for {} PRB / {} Mhz channel width", new_srate/1000000.0, phy.nr_prb(),
388 bandwidth = (cas_nof_prb * 200000) * 1.2;
389 lime.tune(frequency, new_srate, bandwidth, gain, antenna);
391 spdlog::debug(
"Synchronizing subframe");
397 }
else if (state == syncing) {
401 unsigned max_frames = 200;
402 bool sfn_sync =
false;
403 while (!sfn_sync && max_frames-- > 0) {
404 sfn_sync = phy.synchronize_subframe();
407 if (max_frames == 0 && !sfn_sync) {
409 spdlog::warn(
"Synchronization failed. Going back to search state.");
416 spdlog::info(
"Decoded MIB at target sample rate, TTI is {}. Subframe synchronized.", phy.tti());
419 cas_processor.set_cell(phy.cell());
421 for (
int i = 0; i < thread_cnt; i++) {
422 mbsfn_processors[i]->unlock();
435 while (state == processing) {
436 tti = (tti + 1) % 10240;
441 if (!restart && phy.get_next_frame(cas_processor.rx_buffer(), cas_processor.rx_buffer_size())) {
442 spdlog::debug(
"sending tti {} to regular processor", tti);
443 pool.push([ObjectPtr = &cas_processor, tti] {
444 ObjectPtr->process(tti);
448 rest_handler._ce_values = std::move(cas_processor.ce_values());
449 rest_handler._pdsch.SetData(cas_processor.pdsch_data());
450 rest_handler.add_cinr_value(cas_processor.cinr_db());
452 if (phy.nof_mbsfn_prb() != mbsfn_nof_prb)
454 mbsfn_nof_prb = phy.nof_mbsfn_prb();
456 unsigned new_srate = srslte_sampling_freq_hz(mbsfn_nof_prb);
457 spdlog::info(
"Setting sample rate {} Mhz for MBSFN with {} PRB / {} Mhz channel width", new_srate/1000000.0, mbsfn_nof_prb,
458 mbsfn_nof_prb * 0.2);
461 bandwidth = (mbsfn_nof_prb * 200000) * 1.2;
462 lime.tune(frequency, new_srate, bandwidth, gain, antenna);
464 spdlog::debug(
"Synchronizing subframe after PRB extension");
467 cas_processor.set_cell(phy.cell());
473 sample_rate = search_sample_rate;
474 lime.tune(frequency, sample_rate, bandwidth, gain, antenna);
484 spdlog::debug(
"sending tti {} to mbsfn proc {}", tti, mb_idx);
488 if (!restart && phy.get_next_frame(mbsfn_processors[mb_idx]->get_rx_buffer_and_lock(), mbsfn_processors[mb_idx]->rx_buffer_size())) {
489 if (phy.mcch_configured()) {
491 if (!mbsfn_processors[mb_idx]->mbsfn_configured()) {
493 switch (phy.mbsfn_subcarrier_spacing()) {
494 case Phy::SubcarrierSpacing::df_15kHz: scs = SRSLTE_SCS_15KHZ;
break;
495 case Phy::SubcarrierSpacing::df_7kHz5: scs = SRSLTE_SCS_7KHZ5;
break;
496 case Phy::SubcarrierSpacing::df_1kHz25: scs = SRSLTE_SCS_1KHZ25;
break;
498 auto cell = phy.cell();
499 cell.nof_prb = cell.mbsfn_prb;
500 mbsfn_processors[mb_idx]->set_cell(cell);
501 mbsfn_processors[mb_idx]->configure_mbsfn(phy.mbsfn_area_id(), scs);
503 pool.push([ObjectPtr = mbsfn_processors[mb_idx], tti] {
504 ObjectPtr->process(tti);
509 mbsfn_processors[mb_idx]->unlock();
513 spdlog::warn(
"Synchronization lost while processing. Going back to searching state.");
515 sample_rate = search_sample_rate;
516 lime.tune(frequency, sample_rate, bandwidth, gain, antenna);
524 mb_idx =
static_cast<int>((mb_idx + 1) % thread_cnt);
528 if (tick%measurement_interval == 0) {
531 std::vector<std::string> cols;
533 spdlog::info(
"CINR {:.2f} dB", rest_handler.cinr_db() );
534 cols.push_back(std::to_string(rest_handler.cinr_db()));
536 spdlog::info(
"PDSCH: MCS {}, BLER {}, BER {}",
537 rest_handler._pdsch.mcs,
538 ((rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0)),
539 rest_handler._pdsch.ber);
540 cols.push_back(std::to_string(rest_handler._pdsch.mcs));
541 cols.push_back(std::to_string(((rest_handler._pdsch.errors * 1.0) / (rest_handler._pdsch.total * 1.0))));
542 cols.push_back(std::to_string(rest_handler._pdsch.ber));
544 spdlog::info(
"MCCH: MCS {}, BLER {}, BER {}",
545 rest_handler._mcch.mcs,
546 ((rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0)),
547 rest_handler._mcch.ber);
549 cols.push_back(std::to_string(rest_handler._mcch.mcs));
550 cols.push_back(std::to_string(((rest_handler._mcch.errors * 1.0) / (rest_handler._mcch.total * 1.0))));
551 cols.push_back(std::to_string(rest_handler._mcch.ber));
553 auto mch_info = phy.mch_info();
555 std::for_each(std::begin(mch_info), std::end(mch_info), [&cols, &mch_idx, &rest_handler](
Phy::mch_info_t const& mch) {
556 spdlog::info(
"MCH {}: MCS {}, BLER {}, BER {}",
559 (rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0),
560 rest_handler._mch[mch_idx].ber);
561 cols.push_back(std::to_string(mch_idx));
562 cols.push_back(std::to_string(mch.mcs));
563 cols.push_back(std::to_string((rest_handler._mch[mch_idx].errors * 1.0) / (rest_handler._mch[mch_idx].total * 1.0)));
564 cols.push_back(std::to_string(rest_handler._mch[mch_idx].ber));
567 std::for_each(std::begin(mch.mtchs), std::end(mch.mtchs), [&mtch_idx](
Phy::mtch_info_t const& mtch) {
568 spdlog::info(
" MTCH {}: LCID {}, TMGI 0x{}, {}",
577 spdlog::info(
"-----");
578 if (enable_measurement_file) {
579 measurement_file.WriteLogValues(cols);
587 for (
int i = 0; i < thread_cnt; i++) {
588 delete( mbsfn_processors[i] );