diff options
author | Harald Welte <laforge@gnumonks.org> | 2018-06-28 17:11:01 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2018-06-29 17:53:20 +0000 |
commit | b7b5c4219c7190847ea5c1b86a4f3de61add4d0f (patch) | |
tree | 928c0159fb7c41d3782cc070671820cc012cddd6 | |
parent | 685ded192907a24e2e97c9ae42fd2746b49bb587 (diff) |
Add min/max/std-dev measurement reporting for TOA256
This patch adds extended processing of the high-resolution TOA256
measurement values. It adds reporting of the following values
for each RSL MEAS REP for uplink measurements:
* minimum TOA256 value during reporting period
* maximum TOA256 value during reporting period
* standard deviation of TOA256 value during reporting period
Change-Id: Iea4a4781481f77c6163d82dcd71a844a5be87bf2
-rw-r--r-- | include/osmo-bts/gsm_data_shared.h | 15 | ||||
-rw-r--r-- | src/common/measurement.c | 63 | ||||
-rw-r--r-- | src/common/rsl.c | 30 | ||||
-rw-r--r-- | tests/meas/meas_test.c | 215 | ||||
-rw-r--r-- | tests/meas/meas_test.ok | 34 |
5 files changed, 348 insertions, 9 deletions
diff --git a/include/osmo-bts/gsm_data_shared.h b/include/osmo-bts/gsm_data_shared.h index f4fb7661..e15abfed 100644 --- a/include/osmo-bts/gsm_data_shared.h +++ b/include/osmo-bts/gsm_data_shared.h @@ -127,6 +127,7 @@ enum gsm_lchan_state { #define MAX_NUM_UL_MEAS 104 #define LC_UL_M_F_L1_VALID (1 << 0) #define LC_UL_M_F_RES_VALID (1 << 1) +#define LC_UL_M_F_OSMO_EXT_VALID (1 << 2) struct bts_ul_meas { /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */ @@ -257,6 +258,15 @@ struct gsm_lchan { uint8_t l1_info[2]; struct gsm_meas_rep_unidir ul_res; int16_t ms_toa256; + /* Osmocom extended measurement results, see LC_UL_M_F_EXTD_VALID */ + struct { + /* minimum value of toa256 during measurement period */ + int16_t toa256_min; + /* maximum value of toa256 during measurement period */ + int16_t toa256_max; + /* standard deviation of toa256 value during measurement period */ + uint16_t toa256_std_dev; + } ext; } meas; struct { struct amr_multirate_conf amr_mr; @@ -317,6 +327,11 @@ struct gsm_lchan { } ecu_state; }; +static inline uint8_t lchan_get_ta(const struct gsm_lchan *lchan) +{ + return lchan->meas.l1_info[1]; +} + extern const struct value_string lchan_ciph_state_names[]; static inline const char *lchan_ciph_state_name(uint8_t state) { return get_value_string(lchan_ciph_state_names, state); diff --git a/src/common/measurement.c b/src/common/measurement.c index ba7494a1..01f1e5dc 100644 --- a/src/common/measurement.c +++ b/src/common/measurement.c @@ -3,6 +3,7 @@ #include <errno.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/core/utils.h> #include <osmo-bts/gsm_data.h> #include <osmo-bts/logging.h> @@ -335,6 +336,64 @@ static uint8_t ber10k_to_rxqual(uint32_t ber10k) return 7; } +/* if we clip the TOA value to 12 bits, i.e. toa256=3200, + * -> the maximum deviation can be 2*3200 = 6400 + * -> the maximum squared deviation can be 6400^2 = 40960000 + * -> the maximum sum of squared deviations can be 104*40960000 = 4259840000 + * and hence fit into uint32_t + * -> once the value is divided by 104, it's again below 40960000 + * leaving 6 MSBs of freedom, i.e. we could extend by 64, resulting in 2621440000 + * -> as a result, the standard deviation could be communicated with up to six bits + * of fractional fixed-point number. + */ + +/* compute Osmocom extended measurements for the given lchan */ +static void lchan_meas_compute_extended(struct gsm_lchan *lchan) +{ + /* we assume that lchan_meas_check_compute() has already computed the mean value + * and we can compute the min/max/variance/stddev from this */ + int i; + + /* each measurement is an int32_t, so the squared difference value must fit in 32bits */ + /* the sum of the squared values (each up to 32bit) can very easily exceed 32 bits */ + u_int64_t sq_diff_sum = 0; + /* initialize min/max values with their counterpart */ + lchan->meas.ext.toa256_min = INT16_MAX; + lchan->meas.ext.toa256_max = INT16_MIN; + + OSMO_ASSERT(lchan->meas.num_ul_meas); + + /* all computations are done on the relative arrival time of the burst, relative to the + * beginning of its slot. This is of course excluding the TA value that the MS has already + * compensated/pre-empted its transmission */ + + /* step 1: compute the sum of the squared difference of each value to mean */ + for (i = 0; i < lchan->meas.num_ul_meas; i++) { + struct bts_ul_meas *m = &lchan->meas.uplink[i]; + int32_t diff = (int32_t)m->ta_offs_256bits - (int32_t)lchan->meas.ms_toa256; + /* diff can now be any value of +65535 to -65535, so we can safely square it, + * but only in unsigned math. As squaring looses the sign, we can simply drop + * it before squaring, too. */ + uint32_t diff_abs = labs(diff); + uint32_t diff_squared = diff_abs * diff_abs; + sq_diff_sum += diff_squared; + + /* also use this loop iteration to compute min/max values */ + if (m->ta_offs_256bits > lchan->meas.ext.toa256_max) + lchan->meas.ext.toa256_max = m->ta_offs_256bits; + if (m->ta_offs_256bits < lchan->meas.ext.toa256_min) + lchan->meas.ext.toa256_min = m->ta_offs_256bits; + } + /* step 2: compute the variance (mean of sum of squared differences) */ + sq_diff_sum = sq_diff_sum / lchan->meas.num_ul_meas; + /* as the individual summed values can each not exceed 2^32, and we're + * dividing by the number of summands, the resulting value can also not exceed 2^32 */ + OSMO_ASSERT(sq_diff_sum <= UINT32_MAX); + /* step 3: compute the standard deviation from the variance */ + lchan->meas.ext.toa256_std_dev = osmo_isqrt32(sq_diff_sum); + lchan->meas.flags |= LC_UL_M_F_OSMO_EXT_VALID; +} + int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) { struct gsm_meas_rep_unidir *mru; @@ -410,8 +469,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) mru->sub.rx_qual, num_meas_sub, lchan->meas.num_ul_meas); lchan->meas.flags |= LC_UL_M_F_RES_VALID; - lchan->meas.num_ul_meas = 0; + lchan_meas_compute_extended(lchan); + + lchan->meas.num_ul_meas = 0; /* send a signal indicating computation is complete */ return 1; diff --git a/src/common/rsl.c b/src/common/rsl.c index 5d30ca7c..8bbf73c1 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -2536,6 +2536,13 @@ static inline bool ms_to_valid(const struct gsm_lchan *lchan) return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0); } +struct osmo_bts_supp_meas_info { + int16_t toa256_mean; + int16_t toa256_min; + int16_t toa256_max; + uint16_t toa256_std_dev; +} __attribute__((packed)); + /* 8.4.8 MEASUREMENT RESult */ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le) { @@ -2573,16 +2580,23 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, con meas_res); lchan->tch.dtx.dl_active = false; if (ie_len >= 3) { - if (bts->supp_meas_toa256) { + if (bts->supp_meas_toa256 && lchan->meas.flags & LC_UL_M_F_OSMO_EXT_VALID) { + struct osmo_bts_supp_meas_info *smi; + smi = (struct osmo_bts_supp_meas_info *) &meas_res[ie_len]; + ie_len += sizeof(struct osmo_bts_supp_meas_info); /* append signed 16bit value containing MS timing offset in 1/256th symbols * in the vendor-specific "Supplementary Measurement Information" part of - * the uplink measurements IE. This is the current offset *relative* to the - * TA which the MS has already applied. So if you want to know the total - * propagation time between MS and BTS, you need to add the actual TA value - * used (from L1_INFO below, in full symbols) plus the ms_toa256 value - * in 1/256 symbol periods. */ - meas_res[ie_len++] = lchan->meas.ms_toa256 >> 8; - meas_res[ie_len++] = lchan->meas.ms_toa256 & 0xff; + * the uplink measurements IE. The lchan->meas.ext members are the current + * offset *relative* to the TA which the MS has already applied. As we want + * to know the total propagation time between MS and BTS, we need to add + * the actual TA value applied by the MS plus the respective toa256 value in + * 1/256 symbol periods. */ + int16_t ta256 = lchan_get_ta(lchan) * 256; + smi->toa256_mean = htons(ta256 + lchan->meas.ms_toa256); + smi->toa256_min = htons(ta256 + lchan->meas.ext.toa256_min); + smi->toa256_max = htons(ta256 + lchan->meas.ext.toa256_max); + smi->toa256_std_dev = htons(lchan->meas.ext.toa256_std_dev); + lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID; } msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res); lchan->meas.flags &= ~LC_UL_M_F_RES_VALID; diff --git a/tests/meas/meas_test.c b/tests/meas/meas_test.c index cbc673fd..e8893450 100644 --- a/tests/meas/meas_test.c +++ b/tests/meas/meas_test.c @@ -3,6 +3,7 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/application.h> +#include <osmocom/gsm/gsm_utils.h> #include <osmo-bts/gsm_data.h> #include <osmo-bts/logging.h> @@ -62,6 +63,214 @@ void test_fn_sample(struct fn_sample *s, unsigned int len, uint8_t pchan, uint8_ OSMO_ASSERT(tsmap_result == tsmap); } +#define ULM(ber, ta, neg_rssi) \ + { .ber10k = (ber), .ta_offs_256bits = (ta), .c_i = 1.0, .is_sub = 0, .inv_rssi = (neg_rssi) } + +struct meas_testcase { + const char *name; + /* input data */ + const struct bts_ul_meas *ulm; + unsigned int ulm_count; + uint32_t final_fn; + /* results */ + struct { + int success; + uint8_t rx_lev_full; + uint8_t rx_qual_full; + int16_t toa256_mean; + int16_t toa256_min; + int16_t toa256_max; + uint16_t toa256_std_dev; + } res; +}; + +static struct bts_ul_meas ulm1[] = { + ULM(0, 0, 90), + ULM(0, 256, 90), + ULM(0, -256, 90), +}; +static const struct meas_testcase mtc1 = { + .name = "TOA256 Min-Max negative/positive", + .ulm = ulm1, + .ulm_count = ARRAY_SIZE(ulm1), + .final_fn = 25, + .res = { + .success = 1, + .rx_lev_full = 110-90, + .rx_qual_full = 0, + .toa256_mean = 0, + .toa256_max = 256, + .toa256_min = -256, + .toa256_std_dev = 209, + }, +}; + + +static struct bts_ul_meas ulm2[] = { + ULM(0, 256, 90), + ULM(0, 258, 90), + ULM(0, 254, 90), + ULM(0, 258, 90), + ULM(0, 254, 90), + ULM(0, 256, 90), +}; +static const struct meas_testcase mtc2 = { + .name = "TOA256 small jitter around 256", + .ulm = ulm2, + .ulm_count = ARRAY_SIZE(ulm2), + .final_fn = 25, + .res = { + .success = 1, + .rx_lev_full = 110-90, + .rx_qual_full = 0, + .toa256_mean = 256, + .toa256_max = 258, + .toa256_min = 254, + .toa256_std_dev = 1, + }, +}; + +static struct bts_ul_meas ulm3[] = { + ULM(0, 0, 90), + ULM(0, 0, 80), + ULM(0, 0, 80), + ULM(0, 0, 100), + ULM(0, 0, 100), +}; +static const struct meas_testcase mtc3 = { + .name = "RxLEv averaging", + .ulm = ulm3, + .ulm_count = ARRAY_SIZE(ulm3), + .final_fn = 25, + .res = { + .success = 1, + .rx_lev_full = 110-90, + .rx_qual_full = 0, + .toa256_mean = 0, + .toa256_max = 0, + .toa256_min = 0, + .toa256_std_dev = 0, + }, +}; + +static struct bts_ul_meas ulm4[] = { +}; +static const struct meas_testcase mtc4 = { + .name = "Empty measurements", + .ulm = ulm4, + .ulm_count = ARRAY_SIZE(ulm4), + .final_fn = 25, + .res = { + .success = 0, + .rx_lev_full = 0, + .rx_qual_full = 0, + .toa256_mean = 0, + .toa256_max = 0, + .toa256_min = 0, + .toa256_std_dev = 0, + }, +}; + +static struct bts_ul_meas ulm5[] = { + /* one 104 multiframe can at max contain 26 blocks (TCH/F), + * each of which can at maximum be 64 bits in advance (TA range) */ + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), + ULM(0, 64*256, 90), +}; +static const struct meas_testcase mtc5 = { + .name = "TOA256 26 blocks with max TOA256", + .ulm = ulm5, + .ulm_count = ARRAY_SIZE(ulm5), + .final_fn = 25, + .res = { + .success = 1, + .rx_lev_full = 110-90, + .rx_qual_full = 0, + .toa256_mean = 64*256, + .toa256_max = 64*256, + .toa256_min = 64*256, + .toa256_std_dev = 0, + }, +}; + + +static void reset_lchan_meas(struct gsm_lchan *lchan) +{ + lchan->state = LCHAN_S_ACTIVE; + memset(&lchan->meas, 0, sizeof(lchan->meas)); +} + +static void test_meas_compute(const struct meas_testcase *mtc) +{ + struct gsm_lchan *lchan = &trx->ts[1].lchan[0]; + unsigned int i; + unsigned int fn = 0; + + printf("\nMeasurement Compute Test %s\n", mtc->name); + + lchan->ts->pchan = GSM_PCHAN_TCH_F; + reset_lchan_meas(lchan); + + /* feed uplink measurements into the code */ + for (i = 0; i < mtc->ulm_count; i++) { + lchan_new_ul_meas(lchan, (struct bts_ul_meas *) &mtc->ulm[i], fn); + fn += 1; + } + + /* compute the results */ + OSMO_ASSERT(lchan_meas_check_compute(lchan, mtc->final_fn) == mtc->res.success); + if (!mtc->res.success) { + OSMO_ASSERT(!(lchan->meas.flags & LC_UL_M_F_RES_VALID)); + } else { + OSMO_ASSERT(lchan->meas.flags & (LC_UL_M_F_RES_VALID|LC_UL_M_F_OSMO_EXT_VALID)); + printf("meas.ext.toa256_min | %6d | %6d\n", + lchan->meas.ext.toa256_min, mtc->res.toa256_min); + printf("meas.ext.toa256_max | %6d | %6d\n", + lchan->meas.ext.toa256_max, mtc->res.toa256_max); + printf("meas.ms_toa256 | %6d | %6d\n", + lchan->meas.ms_toa256, mtc->res.toa256_mean); + printf("meas.ext.toa256_std_dev | %6u | %6u\n", + lchan->meas.ext.toa256_std_dev, mtc->res.toa256_std_dev); + printf("meas.ul_res.full.rx_lev | %6u | %6u\n", + lchan->meas.ul_res.full.rx_lev, mtc->res.rx_lev_full); + printf("meas.ul_res.full.rx_qual | %6u | %6u\n", + lchan->meas.ul_res.full.rx_qual, mtc->res.rx_qual_full); + + if ((lchan->meas.ext.toa256_min != mtc->res.toa256_min) || + (lchan->meas.ext.toa256_max != mtc->res.toa256_max) || + (lchan->meas.ms_toa256 != mtc->res.toa256_mean) || + (lchan->meas.ext.toa256_std_dev != mtc->res.toa256_std_dev) || + (lchan->meas.ul_res.full.rx_lev != mtc->res.rx_lev_full)) { + fprintf(stderr, "%s: Unexpected measurement result!\n", mtc->name); + } + } + +} + int main(int argc, char **argv) { void *tall_bts_ctx; @@ -111,6 +320,12 @@ int main(int argc, char **argv) test_fn_sample(test_fn_tch_h_ts_6_ss0_ss1, ARRAY_SIZE(test_fn_tch_h_ts_6_ss0_ss1), GSM_PCHAN_TCH_H, (1 << 6)); test_fn_sample(test_fn_tch_h_ts_7_ss0_ss1, ARRAY_SIZE(test_fn_tch_h_ts_7_ss0_ss1), GSM_PCHAN_TCH_H, (1 << 7)); + test_meas_compute(&mtc1); + test_meas_compute(&mtc2); + test_meas_compute(&mtc3); + test_meas_compute(&mtc4); + test_meas_compute(&mtc5); + printf("Success\n"); return 0; diff --git a/tests/meas/meas_test.ok b/tests/meas/meas_test.ok index 6dbda543..026899d6 100644 --- a/tests/meas/meas_test.ok +++ b/tests/meas/meas_test.ok @@ -539,4 +539,38 @@ Testing: ts[7]->lchan[0], fn=15066=>015066/11/12/21/10, fn%104=90, rc=1, delta=9 Testing: ts[7]->lchan[1], fn=15079=>015079/11/25/34/23, fn%104=103, rc=1, delta=13 Testing: ts[7]->lchan[0], fn=15170=>015170/11/12/23/14, fn%104=90, rc=1, delta=91 Testing: ts[7]->lchan[1], fn=15183=>015183/11/25/36/27, fn%104=103, rc=1, delta=13 + +Measurement Compute Test TOA256 Min-Max negative/positive +meas.ext.toa256_min | -256 | -256 +meas.ext.toa256_max | 256 | 256 +meas.ms_toa256 | 0 | 0 +meas.ext.toa256_std_dev | 209 | 209 +meas.ul_res.full.rx_lev | 20 | 20 +meas.ul_res.full.rx_qual | 0 | 0 + +Measurement Compute Test TOA256 small jitter around 256 +meas.ext.toa256_min | 254 | 254 +meas.ext.toa256_max | 258 | 258 +meas.ms_toa256 | 256 | 256 +meas.ext.toa256_std_dev | 1 | 1 +meas.ul_res.full.rx_lev | 20 | 20 +meas.ul_res.full.rx_qual | 0 | 0 + +Measurement Compute Test RxLEv averaging +meas.ext.toa256_min | 0 | 0 +meas.ext.toa256_max | 0 | 0 +meas.ms_toa256 | 0 | 0 +meas.ext.toa256_std_dev | 0 | 0 +meas.ul_res.full.rx_lev | 20 | 20 +meas.ul_res.full.rx_qual | 0 | 0 + +Measurement Compute Test Empty measurements + +Measurement Compute Test TOA256 26 blocks with max TOA256 +meas.ext.toa256_min | 16384 | 16384 +meas.ext.toa256_max | 16384 | 16384 +meas.ms_toa256 | 16384 | 16384 +meas.ext.toa256_std_dev | 0 | 0 +meas.ul_res.full.rx_lev | 20 | 20 +meas.ul_res.full.rx_qual | 0 | 0 Success |