/* Cesar project {{{ * * Copyright (C) 2009 Spidcom * * <<>> * * }}} */ /** * \file ce/rx/src/rx.c * \brief Channel Estimation in Receive mode (implementation). * \ingroup ce_rx */ #include "common/std.h" #include "ce/rx/bitloading/fsm/fsm.h" #include "ce/rx/inc/measure.h" #include "ce/rx/cp/inc/cp.h" #include "ce/rx/inc/rx.h" #include "ce/rx/rx.h" #include "ce/rx/tonemask.h" #include "mac/common/timings.h" #include "mac/common/defs.h" #include "mac/common/store.h" #include "lib/stats.h" #include "ce/rx/bitloading/pber.h" #include "ce/rx/bitloading/inc/pber.h" #include "ce/rx/bitloading/inc/nsr.h" #include "ce/rx/bitloading/inc/ber.h" #include "ce/rx/bitloading/inc/common.h" #include "ce/rx/bitloading/inc/ber_margin_update.h" #include /** * Static context of the CE in RX. */ static ce_rx_t ce_rx; /** * Table with the list of functions to call for each flag. */ static ce_rx_process_work_t ce_rx_process_work[CE_RX_WORK_FLAG_SIZE] = { ce_rx_process_work_quit, ce_rx_process_work_measure, }; ce_rx_t * ce_rx_init (mac_store_t *mac_store, sar_t *sar, pbproc_t *pbproc, mac_config_t *mac_config) { /* Check parameters. */ dbg_assert (mac_store); dbg_assert (mac_config); ce_rx_trace_init (&ce_rx); ce_rx_stats_init (&ce_rx); /* Store a pointer to the MAC store. */ ce_rx.mac_store = mac_store; /* Store a pointer to the MAC config. */ ce_rx.mac_config = mac_config; /* We are going to run, disable quit. */ ce_rx.stop_flag = false; /* Initialize an empty release list. */ tonemap_release_list_init (&ce_rx.tonemap_release_list); /* Initialize sub modules. */ /* Initialize measure. */ ce_rx_measure_init (&ce_rx, sar, pbproc); /* Initialize communication with the CP. */ ce_rx_cp_init (&ce_rx); /* Initialize extra tonemask to mask faulty carriers * during tonemap generation. */ ce_rx_init_tonemask (&ce_rx); /* Initialize BER configuration.*/ ce_rx_bl_ber_conf_init(); /* Initialize BMU configuration.*/ ce_rx_bl_bmu_conf_init (&ce_rx.tonemask); /* Register our configuration variables. * FIXME: this is a dirty hack. */ lib_stats_set_stat_value_notype ("CE_RX_BL_NSR_MARGIN", &ce_rx_bl_nsr_margin_, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("CE_RX_BL_BER_MARGIN_1_2", &ce_rx_bl_ber_margin_[PHY_FEC_RATE_1_2], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("CE_RX_BL_BER_MARGIN_16_21", &ce_rx_bl_ber_margin_[PHY_FEC_RATE_16_21], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); /* Get BER config*/ ce_rx_bl_ber_conf_t *ber_conf = &ce_rx_bl_ber_conf; lib_stats_set_stat_value_notype ( "CE_RX_BL_MIN_TIME_BETWEEN_CE_RESTART_BER_MS", &ce_rx_bl_min_time_between_ce_restart_ms [CE_RX_BL_DATE_CRITERIA_BER], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BER_LOWER_BOUND", &ber_conf->lower_bound, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); /* Get PBER config. */ ce_rx_bl_pber_conf_t *pber_conf = &ce_rx_bl_pber_conf; lib_stats_set_stat_value_notype ( "CE_RX_BL_PBER_MEAN_INIT", &pber_conf->mean_init, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_PBER_SLIDING_MEAN_COEF", &pber_conf->mean_coef, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_PB_FALSE_FACTOR", &ce_rx_bl_pber_conf.pb_false_factor, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_PB_TOTAL_FACTOR", &ce_rx_bl_pber_conf.pb_total_factor, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_MIN_PB_PER_FRAME", &ce_rx_bl_pber_conf.min_pb_per_frame, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_MIN_FRAME_WITH_HIGH_PB_ERR_RATE", &ce_rx_bl_pber_conf.min_frame_with_high_pb_err_rate, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_MIN_TIME_BETWEEN_CE_RESTART_PBER_MS", &ce_rx_bl_min_time_between_ce_restart_ms [CE_RX_BL_DATE_CRITERIA_PBER], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); /* Get BMU config. */ ce_rx_bl_bmu_conf_t *bmu_conf = &ce_rx_bl_bmu_conf; lib_stats_set_stat_value_notype ( "CE_RX_BL_MIN_TIME_BETWEEN_CE_RESTART_BER_MARGIN_UPDATE", &ce_rx_bl_min_time_between_ce_restart_ms [CE_RX_BL_DATE_BER_MARGIN_UPDATE], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BER_MARGIN_UPDATE_PBER_LIMIT_MIN", &bmu_conf->pber_limit[CE_RX_BL_BMU_MIN], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BER_MARGIN_UPDATE_PBER_LIMIT_MAX", &bmu_conf->pber_limit[CE_RX_BL_BMU_MAX], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BER_MARGIN_UPDATE_MIN_NB_PB", &bmu_conf->pb_count_min, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BMU_POLY_SUP_A", &bmu_conf->poly[CE_RX_BL_BMU_POLY_PLUS][0], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BMU_POLY_SUP_B", &bmu_conf->poly[CE_RX_BL_BMU_POLY_PLUS][1], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BMU_POLY_INF_A", &bmu_conf->poly[CE_RX_BL_BMU_POLY_MINUS][0], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "CE_RX_BL_BMU_POLY_INF_B", &bmu_conf->poly[CE_RX_BL_BMU_POLY_MINUS][1], LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); /* ECos. */ /* No work to do. */ cyg_flag_init (&ce_rx.work_flag); /* Timer/alarm. */ ce_rx.real_time_clock_handle = cyg_real_time_clock (); cyg_clock_to_counter (ce_rx.real_time_clock_handle, &ce_rx.real_time_counter); cyg_alarm_create (ce_rx.real_time_counter, CALLBACK (ce_rx_timer_prevent_tone_map_expiration), (cyg_addrword_t) &ce_rx, &ce_rx.alarm_handler, &ce_rx.alarm); cyg_resolution_t res = cyg_clock_get_resolution (cyg_real_time_clock ()); ce_rx.tck_per_rtc = MAC_MS_TO_TCK (1000LL) * res.dividend / res.divisor / 1000000000LL; cyg_tick_count_t period = MAC_MS_TO_TCK (1000) / ce_rx.tck_per_rtc; cyg_alarm_initialize (ce_rx.alarm_handler, period + cyg_current_time (), period); /* Create the ECos thread. */ cyg_thread_create (CE_RX_THREAD_PRIORITY, CALLBACK (ce_rx_thread), (cyg_addrword_t) &ce_rx, CE_RX_THREAD_NAME, ce_rx.thread_stack, CE_RX_THREAD_STACK_SIZE, &ce_rx.thread_handler, &ce_rx.thread); cyg_thread_resume (ce_rx.thread_handler); TRACE_SHORT (CE_RX_TRACE_, &ce_rx.trace, INIT); return &ce_rx; } void ce_rx_thread (cyg_addrword_t data) { /* Check parameters. */ ce_rx_t *ce_rx = (ce_rx_t *) data; dbg_assert (ce_rx); /* Prepare a mask for the work we can done. */ cyg_flag_value_t work_flag_value, work_flag_mask; uint i; for (work_flag_mask = 0, i = 0; i < CE_RX_WORK_FLAG_SIZE; i++) { work_flag_mask |= (1 << i); } /* While we are not asked to terminate. */ while (!ce_rx->stop_flag) { /* Something to do? */ work_flag_value = cyg_flag_wait (&ce_rx->work_flag, work_flag_mask, CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR); for (i = 0; i < CE_RX_WORK_FLAG_SIZE; i++) { /* This flag (work, quit, etc) enabled? */ if (work_flag_value & (1 << i)) { /* Clear flag. */ work_flag_value &= ~(1 << i); /* Do work. */ ce_rx_process_work[i] (ce_rx); } } } /* If we reach this part, we have been asked to terminate this thread. */ cyg_flag_destroy (&ce_rx->work_flag); } void ce_rx_uninit (ce_rx_t *ce_rx) { /* Ask for stop. */ ce_rx_work_add (ce_rx, CE_RX_WORK_FLAG_QUIT); } void ce_rx_init_tonemask (ce_rx_t *ce_rx) { /* Check parameters. */ dbg_assert (ce_rx); /* Initialize tonemask from MAC configuration. */ ce_rx_tonemask_init (&ce_rx->tonemask, &ce_rx->mac_config->tonemask_info); } void ce_rx_work_add (ce_rx_t *ce_rx, ce_rx_work_flag_t work) { /* Check parameters. */ dbg_assert (ce_rx); dbg_assert (work < CE_RX_WORK_FLAG_SIZE); /* Set work. */ cyg_flag_value_t stop = 1 << work; cyg_flag_setbits (&ce_rx->work_flag, stop); } void ce_rx_process_work_quit (ce_rx_t *ce_rx) { /* Check parameters. */ dbg_assert (ce_rx); /* Stop measure. */ ce_rx_measure_uninit (ce_rx); /* Stop communication with the CP. */ ce_rx_cp_uninit (ce_rx); /* Stop CE RX thread. */ ce_rx->stop_flag = true; CE_RX_TRACE (INIT); } void ce_rx_process_work_measure (ce_rx_t *ce_rx) { /* Check parameters. */ dbg_assert (ce_rx); /* Get measure. */ mbox_node_t *node; while ((node = mbox_get (&ce_rx->measure_mbox))) { dbg_assert (node); ce_rx_measure_mbox_t * measure = PARENT_OF (ce_rx_measure_mbox_t, mbox_node, node); CE_RX_TRACE_VERBOSE (MEASURE_HANDLING, measure->rx_params.tei, measure->rx_params.tmi_av, measure->chan_data_count, measure->total_pb_count, measure->rx_params.sound, measure->rx_params.sound_complete); /* Ensure STA is created. */ mac_store_sta_add (ce_rx->mac_store, measure->rx_params.tei); /* Get the STA. */ sta_t *sta = mac_store_sta_get (ce_rx->mac_store, measure->rx_params.tei); /* STA must exists. */ dbg_assert (sta); /* Transform the given measure into FSM event. */ ce_rx_bl_fsm_event_type_t event = ce_rx_bl_fsm_measure_to_event ( sta, (ce_rx_bitloading_fsm_event_param_t) measure); /* Give event to bit loading. */ ce_rx_bl_fsm_handle_event (ce_rx, sta, event, (ce_rx_bitloading_fsm_event_param_t) measure); /* Release station. */ blk_release (sta); /* Delete. */ slab_release (measure); } } void ce_rx_timer_prevent_tone_map_expiration (cyg_handle_t alarm_handler, cyg_addrword_t data) { /* Check required parameter. */ dbg_assert (data); ce_rx_t *ce_rx = (ce_rx_t *) data; uint tei; /* Go through each STA. */ for (tei = MAC_TEI_STA_MIN; tei <= MAC_TEI_STA_MAX; tei++) { /* Get STA. */ sta_t *sta; sta = mac_store_sta_get (ce_rx->mac_store, tei); /* If STA exists. */ if (sta) { dbg_assert (sta->rx_tonemaps); /* Clean tone maps that need to be released. */ tonemaps_disabled_clean (sta->rx_tonemaps); /* Decrement expiration timer. */ if (sta->rx_tonemaps->refresh_counter_s && !--sta->rx_tonemaps->refresh_counter_s) { /* Send refresh MME. */ ce_rx_cp_send_mme_refresh_tmi_list (ce_rx, sta); } /* Clean. */ blk_release (sta); } } /* Clean release list. */ tonemap_release_list_clean (&ce_rx->tonemap_release_list); } blk_t * ce_rx_get_nsr (ce_rx_t *ce_rx, cp_tei_t tei, uint int_index, uint int_version, u16 *ber) { /* Check parameters. */ dbg_assert (ce_rx); dbg_assert (MAC_TEI_IS_STA (tei)); dbg_assert (int_index < TONEMAP_INTERVAL_NB); dbg_assert (ber); dbg_assert (int_version != 0xFF); /* Get STA from its TEI. */ sta_t *sta = mac_store_sta_get (ce_rx->mac_store, tei); if (!sta) return NULL; blk_t *ret = NULL; /* Check if interval version is valid. */ if (sta->rx_tonemaps->intervals->version == int_version) { /* FIXME: add support for intervals. */ ret = ce_rx_bl_get_nsr (&sta->ce_rx_bt); /* FIXME: set BER for the moment being. */ if (ret) *ber = 0; } /* Clean. */ blk_release (sta); return ret; } void ce_rx_stats_init (ce_rx_t *ce_rx) { dbg_assert (ce_rx); /* Clear all statistics. */ memset (&ce_rx->stats, 0, sizeof (ce_rx->stats)); #if CONFIG_STATS /* Register statistics of the whole CE RX. */ lib_stats_set_stat_value_notype ("ce_rx_resend_tm", &ce_rx->stats.resend_tm, LIB_STATS_ACCESS_READ_ONLY, LIB_STATS_DEBUG); #endif }