From 6818881d72cdde04eca4b1923a805190acde493c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 29 Jun 2011 10:36:07 +0200 Subject: implement baseic uplink measurement processing + reporting * gather measurements from each PH-DATA.ind * check every TDMA frame about meas period expiration * compute averages after period expired * put MS DL MEAS REP into RSL MEAS RES messages, include UL meas bugs: * L3 INFO content seems to have some offset * is_sub is not set anywhere * measurement periods might have up/downlink offset --- src/common/measurement.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 src/common/measurement.c (limited to 'src/common/measurement.c') diff --git a/src/common/measurement.c b/src/common/measurement.c new file mode 100644 index 00000000..c20db46f --- /dev/null +++ b/src/common/measurement.c @@ -0,0 +1,235 @@ + +#include +#include + +#include + +#include +#include +#include + +/* TS 05.08, Chapter 8.4.1 */ +/* measurement period ends at fn % 104 == ? */ +static const uint8_t tchf_meas_rep_fn104[] = { + [0] = 103, + [1] = 12, + [2] = 25, + [3] = 38, + [4] = 51, + [5] = 64, + [6] = 77, + [7] = 90, +}; +static const uint8_t tchh0_meas_rep_fn104[] = { + [0] = 103, + [1] = 103, + [2] = 25, + [3] = 25, + [4] = 51, + [5] = 51, + [6] = 77, + [7] = 77, +}; +static const uint8_t tchh1_meas_rep_fn104[] = { + [0] = 12, + [1] = 12, + [2] = 38, + [3] = 38, + [4] = 64, + [5] = 64, + [6] = 90, + [7] = 90, +}; + +/* determine if a measurement period ends at the given frame number */ +static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts, + unsigned int subch, uint32_t fn) +{ + unsigned int fn_mod; + const uint8_t *tbl; + int rc = 0; + + if (ts >= 8) + return -EINVAL; + if (pchan >= _GSM_PCHAN_MAX) + return -EINVAL; + + switch (pchan) { + case GSM_PCHAN_TCH_F: + fn_mod = fn % 104; + if (tchf_meas_rep_fn104[ts] == fn_mod) + rc = 1; + break; + case GSM_PCHAN_TCH_H: + fn_mod = fn % 104; + if (subch == 0) + tbl = tchh0_meas_rep_fn104; + else + tbl = tchh1_meas_rep_fn104; + if (tbl[ts] == fn_mod) + rc = 1; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + fn_mod = fn % 102; + if (fn_mod == 11) + rc = 1; + break; + case GSM_PCHAN_CCCH_SDCCH4: + fn_mod = fn % 102; + if (fn_mod == 36) + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +/* receive a L1 uplink measurement from L1 */ +int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm) +{ + DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n", + gsm_lchan_name(lchan), lchan->meas.num_ul_meas); + + if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) { + LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n", + gsm_lchan_name(lchan)); + return -ENOSPC; + } + + memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm, + sizeof(*ulm)); + + return 0; +} + +/* input: BER in steps of .01%, i.e. percent/100 */ +static uint8_t ber10k_to_rxqual(uint32_t ber10k) +{ + /* 05.08 / 8.2.4 */ + if (ber10k < 20) + return 0; + if (ber10k < 40) + return 1; + if (ber10k < 80) + return 2; + if (ber10k < 160) + return 3; + if (ber10k < 320) + return 4; + if (ber10k < 640) + return 5; + if (ber10k < 1280) + return 6; + return 7; +} + +int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) +{ + uint32_t ber_full_sum = 0; + uint32_t irssi_full_sum = 0; + uint32_t ber_sub_sum = 0; + uint32_t irssi_sub_sum = 0; + int32_t taqb_sum = 0; + unsigned int num_meas_sub = 0; + int i; + + /* if measurement period is not complete, abort */ + if (!is_meas_complete(lchan->ts->pchan, lchan->ts->nr, + lchan->nr, fn)) + return 0; + + /* if there are no measurements, skip computation */ + if (lchan->meas.num_ul_meas == 0) + return 0; + + /* compute the actual measurements */ + + /* step 1: add up */ + for (i = 0; i < lchan->meas.num_ul_meas; i++) { + struct bts_ul_meas *m = &lchan->meas.uplink[i]; + + ber_full_sum += m->ber10k; + irssi_full_sum += m->inv_rssi; + taqb_sum += m->ta_offs_qbits; + + if (m->is_sub) { + num_meas_sub++; + ber_sub_sum += m->ber10k; + irssi_sub_sum += m->inv_rssi; + } + } + + /* step 2: divide */ + ber_full_sum = ber_full_sum / lchan->meas.num_ul_meas; + irssi_full_sum = irssi_full_sum / lchan->meas.num_ul_meas; + taqb_sum = taqb_sum / lchan->meas.num_ul_meas; + ber_sub_sum = ber_sub_sum / num_meas_sub; + irssi_sub_sum = irssi_sub_sum / num_meas_sub; + + DEBUGP(DMEAS, "%s Computed TA(% 4uqb) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), " + "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)", gsm_lchan_name(lchan), + taqb_sum, ber_full_sum/100, + ber_full_sum%100, irssi_full_sum, ber_sub_sum/100, ber_sub_sum%100, + irssi_sub_sum); + + /* store results */ + lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1); + lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1); + lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum); + lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum); + + lchan->meas.flags |= LC_UL_M_F_RES_VALID; + lchan->meas.num_ul_meas = 0; + + /* send a signal indicating computation is complete */ + + return 1; +} + +/* build the 3 byte RSL uplinke measurement IE content */ +int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf) +{ + buf[0] = (lchan->meas.res.rxlev_full & 0x3f); /* FIXME: DTXu support */ + buf[1] = (lchan->meas.res.rxlev_sub & 0x3f); + buf[2] = ((lchan->meas.res.rxqual_full & 7) << 3) | + (lchan->meas.res.rxqual_sub & 7); + + return 3; +} + +int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ts->lchan); i++) { + struct gsm_lchan *lchan = &ts->lchan[i]; + + if (lchan->state != LCHAN_S_ACTIVE) + continue; + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + lchan_meas_check_compute(lchan, fn); + break; + default: + break; + } + } + return 0; +} + +/* needs to be called once every TDMA frame ! */ +int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + ts_meas_check_compute(ts, fn); + } + return 0; +} -- cgit v1.2.3