diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/measurement.c | 156 |
1 files changed, 150 insertions, 6 deletions
diff --git a/src/common/measurement.c b/src/common/measurement.c index bd2c0b79..41844b1f 100644 --- a/src/common/measurement.c +++ b/src/common/measurement.c @@ -9,6 +9,7 @@ #include <osmo-bts/logging.h> #include <osmo-bts/measurement.h> #include <osmo-bts/scheduler.h> +#include <osmo-bts/rsl.h> /* Tables as per TS 45.008 Section 8.3 */ static const uint8_t ts45008_83_tch_f[] = { 52, 53, 54, 55, 56, 57, 58, 59 }; @@ -217,6 +218,32 @@ static uint8_t translate_tch_meas_rep_fn104(uint8_t fn_mod) return 0; } +/* Same as above, but the inverse function */ +static uint8_t translate_tch_meas_rep_fn104_inv(uint8_t fn_mod) +{ + switch (fn_mod) { + case 103: + return 25; + case 12: + return 38; + case 25: + return 51; + case 38: + return 64; + case 51: + return 77; + case 64: + return 90; + case 77: + return 103; + case 90: + return 12; + } + + /* Invalid / not of interest */ + return 0; +} + /* determine if a measurement period ends at the given frame number */ static int is_meas_complete(struct gsm_lchan *lchan, uint32_t fn) { @@ -271,7 +298,101 @@ static int is_meas_complete(struct gsm_lchan *lchan, uint32_t fn) return rc; } -/* receive a L1 uplink measurement from L1 */ +/* Check if a measurement period is overdue. This situation may occur when the + * SACCH frame that closes the measurement interval was not received. Then the + * end of the measurement will not be detected. Using this function we can + * detect if we missed a measurement period end and we also find the frame + * number of the lost SACCH frame. (this function is only used internally, + * it is public to call it from unit-tests) */ +bool is_meas_overdue(struct gsm_lchan *lchan, uint32_t *fn_missed_end, uint32_t fn) +{ + uint32_t fn_mod; + uint32_t last_fn_mod; + uint8_t interval_end; + uint8_t modulus; + const uint8_t *tbl; + enum gsm_phys_chan_config pchan = ts_pchan(lchan->ts); + + /* On the very first measurement we will not be able to do this check + * as we do not have a reference yet. So we have to assume that we + * did not miss the interval end yet. */ + if (lchan->meas.last_fn == LCHAN_FN_DUMMY) + return false; + + /* Determine the interval ending and the modulus to calculate with */ + switch (pchan) { + case GSM_PCHAN_TCH_F: + modulus = 104; + interval_end = tchf_meas_rep_fn104[lchan->ts->nr]; + interval_end = translate_tch_meas_rep_fn104_inv(interval_end); + break; + case GSM_PCHAN_TCH_H: + modulus = 104; + last_fn_mod = lchan->meas.last_fn % 104; + if (lchan->nr == 0) + tbl = tchh0_meas_rep_fn104; + else + tbl = tchh1_meas_rep_fn104; + interval_end = tbl[lchan->ts->nr]; + interval_end = translate_tch_meas_rep_fn104_inv(interval_end); + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: + modulus = 102; + last_fn_mod = lchan->meas.last_fn % 102; + interval_end = sdcch8_meas_rep_fn102[lchan->ts->nr]; + break; + case GSM_PCHAN_CCCH_SDCCH4: + case GSM_PCHAN_CCCH_SDCCH4_CBCH: + modulus = 102; + interval_end = sdcch8_meas_rep_fn102[lchan->ts->nr]; + break; + default: + return false; + break; + } + + fn_mod = fn % modulus; + last_fn_mod = lchan->meas.last_fn % modulus; + + if (fn_mod > last_fn_mod) { + /* When the current frame number is larger then the last frame + * number we check if the interval ending falls in between + * the two. If it does we calculate the absolute frame number + * position on which the interval should have ended. */ + if (interval_end > last_fn_mod && interval_end < fn_mod) { + *fn_missed_end = interval_end + fn - fn_mod; + return true; + } + } else { + /* When the current frame number is smaller then the last frame + * number, than the modulus interval has wrapped. We then just + * check the presence of the interval ending in the section + * that starts at the current frame number and ends at the + * interval end. */ + if (interval_end > last_fn_mod) { + if (fn < lchan->meas.last_fn) + *fn_missed_end = interval_end + GSM_MAX_FN - modulus; + else + *fn_missed_end = interval_end + fn - modulus; + return true; + } + /* We also check the section that starts from the beginning of + * the interval and ends at the current frame number. */ + if (interval_end < fn_mod) { + if (fn < lchan->meas.last_fn) + *fn_missed_end = interval_end; + else + *fn_missed_end = interval_end + fn - fn_mod; + return true; + } + } + + return false; +} + +/* receive a L1 uplink measurement from L1 (this function is only used + * internally, it is public to call it from unit-tests) */ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm, uint32_t fn) { if (lchan->state != LCHAN_S_ACTIVE) { @@ -299,6 +420,8 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm, uint32_t memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm, sizeof(*ulm)); + lchan->meas.last_fn = fn; + return 0; } @@ -484,11 +607,31 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) * interval. */ void lchan_meas_process_measurement(struct gsm_lchan *lchan, struct bts_ul_meas *ulm, uint32_t fn) { - lchan_new_ul_meas(lchan, ulm, fn); - - /* Check measurement period end and prepare the UL - * measurment report at Meas period End */ - lchan_meas_check_compute(lchan, fn); + uint32_t fn_missed_end; + bool missed_end; + + /* The measurement processing detects the end of a measurement period + * by checking if the received measurement sample is from a SACCH + * block. If so, then the measurement computation is performed and the + * next cycle starts. However, when the SACCH block is not received + * then the associated measurement indication is also skipped. Because + * of this we must check now if the measurement interval ended between + * the last and the current call of this function */ + missed_end = is_meas_overdue(lchan, &fn_missed_end, fn); + + if (missed_end) { + DEBUGPFN(DMEAS, fn, "%s measurement interval ending missed, catching up...\n", gsm_lchan_name(lchan)); + /* We missed the end of the interval. Do the computation now + * and add the uplink measurement we got as the first sample + * of a new interval */ + lchan_meas_check_compute(lchan, fn_missed_end); + lchan_new_ul_meas(lchan, ulm, fn); + } else { + /* This is the normal case, we first add the measurement sample + * to the current interva and run the check+computation */ + lchan_new_ul_meas(lchan, ulm, fn); + lchan_meas_check_compute(lchan, fn); + } } /* Reset all measurement related struct members to their initial values. This @@ -497,4 +640,5 @@ void lchan_meas_process_measurement(struct gsm_lchan *lchan, struct bts_ul_meas void lchan_meas_reset(struct gsm_lchan *lchan) { memset(&lchan->meas, 0, sizeof(lchan->meas)); + lchan->meas.last_fn = LCHAN_FN_DUMMY; } |