aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhilipp Maier <pmaier@sysmocom.de>2018-08-17 12:04:49 +0200
committerPhilipp Maier <pmaier@sysmocom.de>2018-08-20 18:27:28 +0200
commit4553890d702a235aca35b7d3004ac530e1d6da59 (patch)
tree57e59aa3388621a361178360d0e0fd0fd44bbd2c /src
parent9feddb7edfc95b6864e5f594211bd94d54d4ab46 (diff)
measurement: make sure measurement interval end is detected
the measurement interval end is detected by using the measurement indication that is related to the SACCH block as a trigger to start the computation. If the measurement indication for the SACCH gets lost because the block could not be received then the processing is not executed. This may cause wrong results or when it happens condecutively an overflow of the measurement sample buffer. - Store the frame number of the last received measurement indication - Use the stored frame number to check if an interval was crossed when the next measurement indication is received. If we detect that we missed the interval, catch up by running the computation and start the next interval. Change-Id: I3a86cd8185cc6b94258373fe929f0c2f1cf27cfa Related: OS#2975
Diffstat (limited to 'src')
-rw-r--r--src/common/measurement.c156
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;
}