/* Cesar project {{{ * * Copyright (C) 2009 Spidcom * * <<>> * * }}} */ /** * \file ce/rx/bitloading/src/bitloading.c * \brief Bit loading related functions. * \ingroup ce_rx_bl */ #include "common/std.h" #include "lib/blk_table.h" #include "lib/fixed.h" #include "ce_rx_bl_fsm_defs.h" #include "ce/rx/bitloading/inc/ber.h" #include "ce/rx/bitloading/inc/bitloading.h" #include "ce/rx/bitloading/ber_margin_update.h" #include "ce/rx/ce_rx_param.h" #include "mac/common/tonemap.h" #include "mac/common/defs.h" #define LIB_HEAPSORT_USER_TYPE ce_rx_bl_ber_impact_t #define LIB_HEAPSORT_USER_COMP_LESSER ce_rx_bl_ber_impact_compare #define LIB_HEAPSORT_USER_SWAP ce_rx_bl_ber_impact_swap #define LIB_HEAPSORT_USER_PREFIX ce_rx_bl_ber_impact #include "lib/heapsort.h" /** * Number of LSB to remove of the BER to compute the BER weighted sum. * It corresponds to the sum of BER weighted by the number of bits of a tone: * - BER is a Q53U53, * - bits of a tone is a Q0U4, * - tone by symbol is a Q0U11. * * To compute the BER weighted sum, we need to reduce to the BER to a Q49U49 * (by removing enough LSB). * The BER weighted sum will be a Q49U64. */ #define CE_RX_BL_BER_WEIGHTED_SUM_SHIFT (4) blk_t * ce_rx_bl_get_nsr (ce_rx_bitloading_t *bl) { dbg_assert (bl); switch (bl->fsm) { /* We can only get noise after bit loading initial. */ case CE_RX_BL_FSM_STATE_TRACKING: if (bl->noise_nrj_blk_count) return bl->noise_nrj; else return NULL; break; default: return NULL; break; } } void ce_rx_bl_sort_optimization (ce_rx_bl_ber_impact_t *opti_table, uint size) { lib_heapsort (opti_table, size); } u64 ce_rx_bl_update_tone_map_under_ber_consign (u64 ber_pt, tonemask_info_t *tonemask, ce_rx_bitloading_t *bl, tonemap_t *tm, ce_rx_bl_ber_impact_t *opti_table, uint *tone_en) { /* Check parameters. */ dbg_assert (tonemask); dbg_assert (tm); dbg_assert (opti_table); dbg_assert (tone_en); u8 modulation; u64 ber_weighted_sum_q49 = 0; u64 ber_upper; uint i = 0, carrier_index = 0; blk_t *cur_blk = bl->noise_nrj; u32 *nsr_data = (u32 *) cur_blk->data; tm->bits_per_symbol = 0; *tone_en = 0; /* Go through the tone map. */ TONEMAP_ACCESS_BEGIN (tm, tonemask->tonemask) { /* Index of a tone in a word of tones. */ uint tm_tone_index_; /* The 8 tones of the word. */ u32 tm_word_tone_ = 0; for (tm_tone_index_ = 0; tm_tone_index_ < 8; tm_tone_index_++) { /* If tone enable. */ if (!(tk_cur_ & 0x1)) { /* Compute BER. */ dbg_assert_ptr (nsr_data); ce_rx_bl_ber_vs_nsr (ce_rx_bl_ber_poly_coef, nsr_data[carrier_index % (BLK_SIZE / 4)], ber_pt, &modulation, &opti_table[i].ber_lower, &ber_upper); /* Update optimization table. */ if (modulation == CE_MOD_COUNT - 1) /* This tone can not be improved, set an impossible BER * difference. */ opti_table[i].ber_diff = -1; else opti_table[i].ber_diff = (ber_upper - opti_table[i].ber_lower) >> 32; opti_table[i].carrier_index = carrier_index; /* Update statistics for this tone map. */ if (modulation) (*tone_en)++; ber_weighted_sum_q49 += fixed_round_64 (opti_table[i].ber_lower, CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) * CE_BIT_PER_MOD[modulation]; tm->bits_per_symbol += CE_BIT_PER_MOD[modulation]; i++; } else modulation = 0; /* Write tone. */ tm_word_tone_ |= modulation << (4 * tm_tone_index_); /* Next tone mask bit. */ tk_cur_ >>= 1; carrier_index++; } *tm_ = tm_word_tone_; /* Next group of tone (grouped by word). */ tm_++; } TONEMAP_ACCESS_MIDDLE; /* Get next block of NSR. */ /* Dirty? You mean? FIXME */ if (!(carrier_index % (BLK_SIZE / 4))) { cur_blk = cur_blk->next; if (cur_blk && cur_blk != INVALID_PTR) nsr_data = (u32 *) cur_blk->data; else dbg_invalid_ptr (nsr_data); } TONEMAP_ACCESS_END; /* At this point, opti_table should contain carrier_nb elements. */ dbg_assert (i == tonemask->carrier_nb); return ber_weighted_sum_q49; } u16 ce_rx_bl_update_tone_map_at_ber_consign (u64 ber_pt, tonemask_info_t *tonemask, ce_rx_bitloading_t *bl, tonemap_t *tm, ce_rx_bl_ber_impact_t *opti, u64 *ber_weighted_sum_q49, uint *tone_en, bool *opti_failed) { /* Check parameters. */ dbg_assert (ber_pt < CE_RX_BL_BER_DEFAULT_OVER); dbg_assert (tonemask); dbg_assert (tm); dbg_assert (bl); dbg_assert (opti); dbg_assert (tone_en); dbg_assert (opti_failed); dbg_assert (ber_weighted_sum_q49); uint pos = 0; u32 *tone_word; s8 mod = -1; u64 ber_pt_q49 = fixed_round_64 (ber_pt, CE_RX_BL_BER_WEIGHTED_SUM_SHIFT); *opti_failed = false; /* Optimize tone map until we reach the BER consign or we are out of * table. */ while (ber_pt_q49 * tm->bits_per_symbol >= *ber_weighted_sum_q49 && pos < tonemask->carrier_nb) { /* Get the modulation of the lowest BER impact. */ mod = tonemap_get_tone (tm, opti[pos].carrier_index, &tone_word); /* If the tone we would like to increase is already at its maximum, * all the remaining tone that can be increase are already at the * maximum too. So basically, we should stop the algorithm here. */ if (mod == CE_MOD_COUNT - 1) { *opti_failed = true; return pos; } /* Remove from number of bits of the tone map the current value. */ tm->bits_per_symbol -= CE_BIT_PER_MOD[mod]; /* Remove from the summed BER of the tone map the current value. */ *ber_weighted_sum_q49 -= fixed_round_64 (opti[pos].ber_lower, CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) * CE_BIT_PER_MOD[mod]; /* Increase modulation of this tone index. */ tonemap_write_tone_to_word (tone_word, opti[pos].carrier_index, ++mod); /* Update number of bits of the tone map. */ tm->bits_per_symbol += CE_BIT_PER_MOD[mod]; /* Update summed BER of the tone map. */ *ber_weighted_sum_q49 += fixed_round_64 (opti[pos].ber_lower + ((u64) opti[pos].ber_diff << 32), CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) * CE_BIT_PER_MOD[mod]; /* Update tone enabled count if needed. */ if (mod == 1) (*tone_en)++; /* Next one. */ pos++; } /* Sanity check: we must have done something! */ dbg_assert (mod != -1); /* Remove last tone if we are over target. */ if (*ber_weighted_sum_q49 > ber_pt_q49 * tm->bits_per_symbol) { /* Go back to last position. */ pos--; dbg_assert (pos < tonemask->carrier_nb); /* Remove last one. */ tm->bits_per_symbol -= CE_BIT_PER_MOD[mod]; *ber_weighted_sum_q49 -= fixed_round_64 (opti[pos].ber_lower + ((u64) opti[pos].ber_diff << 32), CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) * CE_BIT_PER_MOD[mod]; /* Write new value. */ tonemap_write_tone_to_word (tone_word, opti[pos].carrier_index, --mod); tm->bits_per_symbol += CE_BIT_PER_MOD[mod]; *ber_weighted_sum_q49 += fixed_round_64 (opti[pos].ber_lower, CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) * CE_BIT_PER_MOD[mod]; if (mod == 0) (*tone_en)--; } return pos; } tonemap_t * ce_rx_bl_compute_tone_map_iterative (const u64 bpt_initial[PHY_FEC_RATE_NB], uint iteration_max, tonemask_info_t *tonemask, tonemask_info_t *reducedtm, ce_rx_bitloading_t *bl) { /* Check parameters. */ dbg_assert (bpt_initial); dbg_assert (iteration_max); dbg_assert (bl); u64 ber_prev[PHY_FEC_RATE_NB], ber_reached[PHY_FEC_RATE_NB], ber_cur; uint it_count; /* Dynamic table size is not possible (because of compiler bug on stack). * For the moment being we use a define (even if table is too big). */ dbg_assert (tonemask && tonemask->carrier_nb <= PHY_CARRIER_NB); dbg_assert (reducedtm && reducedtm->carrier_nb <= PHY_CARRIER_NB); ce_rx_bl_ber_impact_t opti[PHY_FEC_RATE_NB][PHY_CARRIER_NB]; u16 opti_cursor[PHY_FEC_RATE_NB]; uint cpt; u32 sort[PHY_FEC_RATE_NB]; tonemap_t *tm[PHY_FEC_RATE_NB]; uint tone_en; s64 tmp_ber; bool opti_failed[PHY_FEC_RATE_NB] = { false, false }; phy_fecrate_t fec_rate; /* For each FEC rate. */ for (fec_rate = PHY_FEC_RATE_1_2; fec_rate < PHY_FEC_RATE_NB; fec_rate++) { /* Initialize. */ it_count = 0; /* Allocate a tone map for this FEC rate. */ tm[fec_rate] = tonemap_alloc (); /* Get initial BER consign from initial bits per tone. */ ber_cur = ce_rx_bl_ber_pt_bpt (fec_rate, tonemask->carrier_nb, bpt_initial[fec_rate]); /* Apply BER target static margin. */ tmp_ber = ber_cur - ce_rx_bl_ber_margin_[fec_rate]; dbg_assert (tmp_ber >= 0); dbg_assert (tmp_ber < CE_RX_BL_BER_DEFAULT_OVER); /* Apply BER margin update. */ ber_cur = ce_rx_bl_bmu_apply_margin (&bl->bmu, tmp_ber, fec_rate); do { /* Make the tone map reach the BER consign, no optimization. */ u64 ber_weighted_sum_q49 = ce_rx_bl_update_tone_map_under_ber_consign (ber_cur, reducedtm, bl, tm[fec_rate], opti[fec_rate], &tone_en); /* Sort optimization table. */ ce_rx_bl_sort_optimization (opti[fec_rate], reducedtm->carrier_nb); /* Optimize tone map to reach BER consign. */ opti_cursor[fec_rate] = ce_rx_bl_update_tone_map_at_ber_consign (ber_cur, reducedtm, bl, tm[fec_rate], opti[fec_rate], &ber_weighted_sum_q49, &tone_en, &opti_failed[fec_rate]); /* Store previous BER consign. */ ber_prev[fec_rate] = ber_cur; /* We may have zero bit per symbol (when NSR is high for example). * */ if (tm[fec_rate]->bits_per_symbol != 0) { /* Store BER target reached. */ ber_reached[fec_rate] = (ber_weighted_sum_q49 << CE_RX_BL_BER_WEIGHTED_SUM_SHIFT) / tm[fec_rate]->bits_per_symbol; /* Get new BER consign. */ ber_cur = ce_rx_bl_ber_pt_bpt (fec_rate, reducedtm->carrier_nb, (tm[fec_rate]->bits_per_symbol * CE_RX_BL_BPT_QUANT_FACTOR) / tone_en); } else { ber_reached[fec_rate] = 0; ber_cur = 0; } /* Apply BER target static margin. */ tmp_ber = ber_cur - ce_rx_bl_ber_margin_[fec_rate]; dbg_assert (tmp_ber >= 0); dbg_assert (tmp_ber < CE_RX_BL_BER_DEFAULT_OVER); /* Apply BER margin update. */ ber_cur = ce_rx_bl_bmu_apply_margin (&bl->bmu, tmp_ber, fec_rate); } /* Repeat until no modification of the BER consign or until maximum * number of iterations is reached. */ while (++it_count < iteration_max && ber_prev[fec_rate] != ber_cur); /* Store generated tone map. */ sort[fec_rate] = tm[fec_rate]->bits_per_symbol; } /* Choose best tone map depending on FEC rate. */ /* 1/2 * 42 -> 21. * 16/21 * 42 -> 32. */ phy_fecrate_t good, bad; if ((sort[PHY_FEC_RATE_1_2] * 21) > sort[PHY_FEC_RATE_16_21] * 32) { good = PHY_FEC_RATE_1_2; bad = PHY_FEC_RATE_16_21; } else { good = PHY_FEC_RATE_16_21; bad = PHY_FEC_RATE_1_2; } /* Remove worst tone map. */ tonemap_free (tm[bad]); /* Remove the "good one" because not usable. */ if (tm[good]->bits_per_symbol == 0) { tonemap_free (tm[good]); return NULL; } /* Configure generated tone map. */ tm[good]->fecrate = good; /* Update pre-computed parameters. */ tonemap_update (tm[good], TONEMAP_P_PBERROR_DEFAULT_UF32); /* Store BER consign of this tone map. */ tm[good]->ber_target = ber_prev[good]; /* Store BER reached by this tone map. */ tm[good]->ber_target_reached = ber_reached[good]; /* Configure tone map for usage. */ tm[good]->cpf = true; /* Copy the optimization table corresponding to the best tone map. */ u16 *carrier; if (bl->opti_table == NULL) bl->opti_table = blk_table_init (sizeof (u16), tonemask->carrier_nb); for (cpt = 0; cpt < tonemask->carrier_nb; cpt++) { carrier = (u16 *) blk_table_get (bl->opti_table, cpt); *carrier = opti[good][cpt].carrier_index; } /* Store the cursor of the optimization table for this tone map. */ bl->opti_table_cursor = opti_cursor[good]; /* Store optimization result and update corresponding stats. */ bl->optimization_failed = opti_failed[good]; if (bl->optimization_failed) bl->stats.optimization_failure++; /* Return best tone map. */ return tm[good]; }