diff options
author | Yves Godin <yves.godin@nutaq.com> | 2016-09-19 10:38:07 -0400 |
---|---|---|
committer | Yves Godin <yves.godin@nutaq.com> | 2016-09-19 10:38:07 -0400 |
commit | ab358ab75f3a7d93476ac8fbb0d8d4ca73c3e338 (patch) | |
tree | 7d6e5b03a07dd679f76bcbb1d74586671d96b52e | |
parent | 13fdfd932be0e57ee0d229936094f4d32b03a886 (diff) | |
parent | 4d73bd8164b7a201e602b221219d924fc91c2d5d (diff) |
Merge branch 'nrw/litecell15-feature-meas-preproc-19092016' into 'nrw/litecell15-next'
LC15: Initial commit for measurement preprocessing implemnetation
This MR should have: *
See merge request !54
-rw-r--r-- | include/osmo-bts/measurement.h | 38 | ||||
-rw-r--r-- | src/common/measurement.c | 730 | ||||
-rw-r--r-- | src/common/rsl.c | 483 |
3 files changed, 1240 insertions, 11 deletions
diff --git a/include/osmo-bts/measurement.h b/include/osmo-bts/measurement.h index 2037ff6b..7c9086b6 100644 --- a/include/osmo-bts/measurement.h +++ b/include/osmo-bts/measurement.h @@ -1,8 +1,46 @@ #ifndef OSMO_BTS_MEAS_H #define OSMO_BTS_MEAS_H +#define HO_RQD_CAUSE_MAX 17 + +#define HO_RQD_CAUSE_L_RXLEV_UL_H (1 << 0) +#define HO_RQD_CAUSE_L_RXLEV_DL_H (1 << 1) +#define HO_RQD_CAUSE_L_RXQUAL_UL_H (1 << 2) +#define HO_RQD_CAUSE_L_RXQUAL_DL_H (1 << 3) +#define HO_RQD_CAUSE_RXLEV_UL_IH (1 << 4) +#define HO_RQD_CAUSE_RXLEV_DL_IH (1 << 5) +#define HO_RQD_CAUSE_MAX_MS_RANGE (1 << 6) +#define HO_RQD_CAUSE_POWER_BUDGET (1 << 7) +#define HO_RQD_CAUSE_ENQUIRY (1 << 8) /* currently unsupported */ +#define HO_RQD_CAUSE_ENQUIRY_FAILED (1 << 9) /* currently unsupported */ +#define HO_RQD_CAUSE_NORMAL3G (1 << 10) /* currently unsupported */ +#define HO_RQD_CAUSE_EMERGENCY3G (1 << 11) /* currently unsupported */ +#define HO_RQD_CAUSE_SRV_PREFERRED3G (1 << 12) /* currently unsupported */ +#define HO_RQD_CAUSE_O_M_SHUTDOWN (1 << 13) +#define HO_RQD_CAUSE_QUALITY_PROMOTION (1 << 14) +#define HO_RQD_CAUSE_LOAD_PROMOTION (1 << 15) +#define HO_RQD_CAUSE_LOAD_DEMOTION (1 << 16) + +#define MEAS_PREPROC_AVG_PARAMID_RXLEV (1 << 0) +#define MEAS_PREPROC_AVG_PARAMID_RXQUAL (1 << 1) +#define MEAS_PREPROC_AVG_PARAMID_DIST (1 << 2) + +#define MEAS_PREPROC_AVG_METHOD_UNWEIGHTED (1 << 8) +#define MEAS_PREPROC_AVG_METHOD_WEIGHTED (1 << 9) +#define MEAS_PREPROC_AVG_METHOD_MEDIAN (1 << 10) + + int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm); int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn); +/* build the 3 byte RSL uplinke measurement IE content */ +int lchan_build_rsl_ul_meas(struct gsm_lchan *, uint8_t *buf); + +void meas_preproc_rep_init(struct gsm_lchan *lchan); +int meas_update_ho_causes(struct gsm_lchan *lchan); +int meas_preproc_avg(struct gsm_lchan *lchan); +int meas_parse_ms_rep(struct msgb *msg, struct gsm_lchan *lchan); + + #endif diff --git a/src/common/measurement.c b/src/common/measurement.c index be1d4f64..73d8c694 100644 --- a/src/common/measurement.c +++ b/src/common/measurement.c @@ -41,6 +41,8 @@ static const uint8_t tchh1_meas_rep_fn104[] = { [7] = 90, }; +static uint8_t ber10k_to_rxqual(uint32_t ber10k); + /* 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) @@ -91,8 +93,14 @@ static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts, /* 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); + struct gsm_meas_rep_unidir *mru; + 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 (lchan->state != LCHAN_S_ACTIVE) { LOGP(DMEAS, LOGL_NOTICE, "%s measurement during state: %s\n", @@ -105,9 +113,59 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm) return -ENOSPC; } - memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm, + memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas], ulm, sizeof(*ulm)); + /* set measurement valid flag */ + if ((lchan->meas.num_ul_meas == MAX_NUM_MEAS_PREPROC - 1) && !(lchan->meas.flags & LC_UL_M_F_RES_VALID )) + lchan->meas.flags |= LC_UL_M_F_RES_VALID; + + /* rollover measurement index*/ + lchan->meas.num_ul_meas = (lchan->meas.num_ul_meas + 1) % MAX_NUM_UL_MEAS; + + /* make sure we have enough measurement samples in buffer before calculation */ + if (!lchan->meas.flags & LC_UL_M_F_RES_VALID) + return -EINPROGRESS; + + /* compute the actual measurements */ + /* step 1: add up */ + for (i = 0; i < MAX_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 /= MAX_NUM_UL_MEAS; + irssi_full_sum /= MAX_NUM_UL_MEAS; + taqb_sum /= MAX_NUM_UL_MEAS; + + if (num_meas_sub) { + ber_sub_sum = ber_sub_sum / num_meas_sub; + irssi_sub_sum = irssi_sub_sum / num_meas_sub; + } +/* + DEBUGP(DMEAS, "%s Computed TA(% 4dqb) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), " + "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n", 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); +*/ + /* step 3: store results */ + mru = &lchan->meas.ul_res; + mru->full.rx_lev = dbm2rxlev((int)irssi_full_sum * -1); + mru->sub.rx_lev = dbm2rxlev((int)irssi_sub_sum * -1); + mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum); + mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum); + return 0; } @@ -236,3 +294,669 @@ int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn) } return 0; } + +void meas_preproc_rep_init(struct gsm_lchan *lchan) { + int i; + + /* clear cached measurement reports */ + LOGP(DRSL, LOGL_NOTICE, "%s Clear measurement report\n", gsm_lchan_name(lchan)); + + lchan->meas_preproc.meas_idx = 0; + lchan->meas_preproc.meas_ave_idx = 0; + lchan->meas.flags &= ~LC_AVE_F_RES_VALID; + lchan->meas.flags &= ~LC_AVE_F_CACHE_VALID; + memset(lchan->meas_preproc.ul_ave_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.ul_ave_res, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.dl_ave_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.dl_ave_res, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.ul_qual_ave_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.ul_qual_ave_res, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.dl_qual_ave_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.dl_qual_ave_res, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.ms_bts_ave_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.ms_bts_ave_res, 0, MAX_NUM_MEAS_PREPROC); + + for (i = 0; i < ARRAY_SIZE(lchan->meas_preproc.cell); i++){ + lchan->meas_preproc.cell[i].bcch_freq = 0; + lchan->meas_preproc.cell[i].bsic = 0; + lchan->meas_preproc.cell[i].rxlev = 0; + memset(lchan->meas_preproc.cell[i].rxlev_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.cell[i].rxlev_ave_res, 0, MAX_NUM_MEAS_PREPROC); + } + + /* Cancel any pending timer */ + osmo_timer_del(&lchan->meas_preproc.preproc_ho_timer); +} + + +/* Calculate HO cause decision */ +static int meas_ho_cause_decision(uint8_t *res_vec, int cur_idx, int p, int n, uint8_t res_thres, int sign) { + int i, j, k; + uint8_t vote = 0; + + LOGP(DMEAS, LOGL_DEBUG, "Cached res_vec P=%d out of N=%d, thres=%x, buf=%s \n", p, n, res_thres, osmo_hexdump(res_vec, n)); + + j = cur_idx - n + 1; + if (j < 0) + j = n + j; + + LOGP(DMEAS, LOGL_DEBUG, "cur_idx=%d, start_idx=%d\n", cur_idx, j); + + for(i = j; i < j + n; i++) { + k = i % n; + /*if there is a measurement result higher threshold, we increase HO detection counter*/ + if ((int)res_vec[k] * sign > (int)res_thres * sign) { + vote++; + LOGP(DMEAS, LOGL_DEBUG, "HO decision detected res=%x, thres=%x, vote=%d \n", res_vec[k], res_thres, vote); + } + } + /*if HO detection counter bigger that p_out_of_n, we will return valid HO cause generation */ + if (vote < p) + return 0; + + return vote; +} + +/* Update HO causes */ +int meas_update_ho_causes(struct gsm_lchan *lchan) +{ + struct ipac_preproc_cfg preproc_cfg = lchan->ts->trx->trx_preproc_cfg; + int num_cell = lchan->meas_preproc.num_cell; + int rc, i, tx_rep_pending; + uint8_t better_rxlev = 0; + int power_budget; + + /*make sure average result is valid */ + if (!( lchan->meas.flags & LC_AVE_F_RES_VALID )) + return -EINPROGRESS; + + /* make sure average result is valid */ + if (!( lchan->meas.flags & LC_AVE_F_CACHE_VALID )) + return -EINPROGRESS; + + /* print log for testing purpose */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXLEV) { + LOGP(DMEAS, LOGL_DEBUG, "%s MEAS_PREPROC_AVG SERVICING CELL rxlev_ul_avg=%3ddBm, rxlev_dl_avg=%3ddBm\n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.ul_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx])); + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXQUAL) { + LOGP(DMEAS, LOGL_DEBUG, "%s MEAS_PREPROC_AVG SERVICING CELL rxqual_ul_avg=%d, rxqual_dl_avg=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ul_qual_ave_res[lchan->meas_preproc.meas_ave_idx], + lchan->meas_preproc.dl_qual_ave_res[lchan->meas_preproc.meas_ave_idx]); + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_DIST) { + LOGP(DMEAS, LOGL_DEBUG, "%s MEAS_PREPROC_AVG SERVICING CELL ms_ta_avg=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ms_bts_ave_vec[lchan->meas_preproc.meas_ave_idx]); + + } + + for (i = 0; i < num_cell; i++) { + LOGP(DMEAS, LOGL_DEBUG, "%s MEAS_PREPROC_AVG NCELL(%d) rxlev_avg=%3ddBm\n", + gsm_lchan_name(lchan), + i, + rxlev2dbm(lchan->meas_preproc.cell[i].rxlev_ave_res[lchan->meas_preproc.meas_ave_idx])); + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXLEV) { + LOGP(DMEAS, LOGL_DEBUG, "%s Cached ul_ave_res %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ul_ave_res, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt), + lchan->meas_preproc.meas_ave_idx); + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached dl_ave_res %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.dl_ave_res, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt), + lchan->meas_preproc.meas_ave_idx); + + for (i = 0; i < num_cell; i++) { + LOGP(DMEAS, LOGL_DEBUG, "%s Cached ncell(%d) rxlev_avg_res %s, idx=%d\n", + gsm_lchan_name(lchan), + i, + osmo_hexdump(lchan->meas_preproc.cell[i].rxlev_ave_res, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt), + lchan->meas_preproc.meas_ave_idx); + } + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXQUAL) { + LOGP(DMEAS, LOGL_DEBUG, "%s Cached ul_qual_ave_res %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ul_qual_ave_res, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt), + lchan->meas_preproc.meas_ave_idx); + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached dl_qual_ave_res %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.dl_qual_ave_res, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt), + lchan->meas_preproc.meas_ave_idx); + } + + /* FIXME : we do nothing if there is no neighbor CELL reported */ + if(!num_cell) + return 0; + + /* find the best neighbor cell if possible */ + for (i = 0; i < num_cell; i++) { + if ( lchan->meas_preproc.cell[i].rxlev_ave_res[lchan->meas_preproc.meas_ave_idx] > better_rxlev ) { + better_rxlev = lchan->meas_preproc.cell[i].rxlev_ave_res[lchan->meas_preproc.meas_ave_idx]; + lchan->meas_preproc.better_ncell = i; + } + } + + /* make sure the average RXLEV of NCELL is above default cell RXLEV_MIN to start to evaluate HO */ + if( better_rxlev < preproc_cfg.ncell_dflts.rxlev_min_def) { + lchan->meas_preproc.cur_ho_causes = 0; + return 0; + } + + /* update HO cause based on averaged RXLEV */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXLEV) { + /* GSM 05.08 section A.3.2.2a */ + /* check for RXLEV UL*/ + rc = meas_ho_cause_decision(lchan->meas_preproc.ul_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p5, + preproc_cfg.ho_comp.n5, + preproc_cfg.ho_thresh.l_rxlev_ul_h, + -1); + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_L_RXLEV_UL_H; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_L_RXLEV_UL_H detected rxlev=%3ddBm, l_rxlev_ul_h=%3ddBm \n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.ul_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(preproc_cfg.ho_thresh.l_rxlev_ul_h)); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_L_RXLEV_UL_H; + + /* check for RXLEV DL */ + rc = meas_ho_cause_decision(lchan->meas_preproc.dl_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p5, + preproc_cfg.ho_comp.n5, + preproc_cfg.ho_thresh.l_rxlev_dl_h, + -1); + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_L_RXLEV_DL_H; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_L_RXLEV_DL_H detected rxlev=%3ddBm, l_rxlev_ul_h=%3ddBm \n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(preproc_cfg.ho_thresh.l_rxlev_dl_h)); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_L_RXLEV_DL_H; + + /* GSM 05.08 section A.3.2.2c */ + /* check for RXQUAL UL */ + rc = meas_ho_cause_decision(lchan->meas_preproc.ul_qual_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p7, + preproc_cfg.ho_comp.n7, + preproc_cfg.ho_thresh.l_rxqual_ul_h, + 1); + if (rc) { + /* check for RXLEV UL */ + rc = meas_ho_cause_decision(lchan->meas_preproc.ul_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p7, + preproc_cfg.ho_comp.n7, + preproc_cfg.ho_thresh.rxlev_ul_ih, + 1); + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_RXLEV_UL_IH; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_RXLEV_UL_IH detected rxlev=%3ddBm, l_rxlev_ul_h=%3ddBm \n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.ul_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(preproc_cfg.ho_thresh.rxlev_ul_ih)); + } + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_RXLEV_UL_IH; + + /* check for RXQUAL DL */ + rc = meas_ho_cause_decision(lchan->meas_preproc.dl_qual_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p7, + preproc_cfg.ho_comp.n7, + preproc_cfg.ho_thresh.l_rxqual_dl_h, + 1); + if (rc) { + /* check for RXLEV DL */ + rc = meas_ho_cause_decision(lchan->meas_preproc.dl_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p7, + preproc_cfg.ho_comp.n7, + preproc_cfg.ho_thresh.rxlev_dl_ih, + 1); + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_RXLEV_DL_IH; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_RXLEV_DL_IH detected rxlev=%3ddBm, l_rxlev_ul_h=%3ddBm \n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(preproc_cfg.ho_thresh.rxlev_dl_ih)); + } + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_RXLEV_DL_IH; + + /* GSM 05.08 section A.3.2.2e */ + power_budget = better_rxlev - lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx]; + + if ((power_budget > 0) && (power_budget > preproc_cfg.ncell_dflts.ho_margin_def)) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_POWER_BUDGET; + + LOGP(DMEAS, LOGL_NOTICE, "%s NCELL(%d) HO_RQD_CAUSE_POWER_BUDGET detected rxlev=%3ddBm, ncell_rxlev=%3ddBm \n", + gsm_lchan_name(lchan), + lchan->meas_preproc.better_ncell, + rxlev2dbm(lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx]), + rxlev2dbm(lchan->meas_preproc.cell[lchan->meas_preproc.better_ncell].rxlev_ave_res[lchan->meas_preproc.meas_ave_idx])); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_POWER_BUDGET; + + } + + /* update HO cause based on averaged RXQUAL */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXQUAL) { + /* GSM 05.08 section A.3.2.2b */ + rc = meas_ho_cause_decision(lchan->meas_preproc.ul_qual_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p6, + preproc_cfg.ho_comp.n6, + preproc_cfg.ho_thresh.l_rxqual_ul_h, + 1); + + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_L_RXQUAL_UL_H; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_L_RXQUAL_UL_H detected rxqual=%d, l_rxqual_ul_h=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ul_qual_ave_res[lchan->meas_preproc.meas_ave_idx], + preproc_cfg.ho_thresh.l_rxqual_ul_h); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_L_RXQUAL_UL_H; + + rc = meas_ho_cause_decision(lchan->meas_preproc.dl_qual_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p6, + preproc_cfg.ho_comp.n6, + preproc_cfg.ho_thresh.l_rxqual_dl_h, + 1); + + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_L_RXQUAL_DL_H; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_L_RXQUAL_DL_H detected rxqual=%d, l_rxqual_dl_h=%d \n", + gsm_lchan_name(lchan), + lchan->meas_preproc.dl_qual_ave_res[lchan->meas_preproc.meas_ave_idx], + preproc_cfg.ho_thresh.l_rxqual_dl_h); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_L_RXQUAL_DL_H; + + } + + /* update HO cause based on averaged MS-BTS distance */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_DIST) { + /* GSM 05.08 section 3.2.2d */ + /*validate threshold for the maximum distance between MS and current BTS for HO process to start */ + rc = meas_ho_cause_decision(lchan->meas_preproc.ms_bts_ave_res, + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ho_comp.p8, + preproc_cfg.ho_comp.n8, + preproc_cfg.ho_thresh.ms_range_max, + 1); + + if (rc) { + lchan->meas_preproc.cur_ho_causes |= HO_RQD_CAUSE_MAX_MS_RANGE; + + LOGP(DMEAS, LOGL_NOTICE, "%s HO_RQD_CAUSE_MAX_MS_RANGE detected ms_ta=%d, ms_range_max=%d \n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ms_bts_ave_res[lchan->meas_preproc.meas_ave_idx], + preproc_cfg.ho_thresh.ms_range_max); + + } else + lchan->meas_preproc.cur_ho_causes &= ~HO_RQD_CAUSE_MAX_MS_RANGE; + } + + LOGP(DMEAS, LOGL_DEBUG, "%s IPAC HO_CAUSES: recorded=0x%x, current=0x%x\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.rec_ho_causes, + lchan->meas_preproc.cur_ho_causes); + + /* nothing to do when no current HO cause */ + if (!lchan->meas_preproc.cur_ho_causes) + return 0; + + /* only add new HO cause to recorded HO cause list */ + tx_rep_pending = 0; + for(i = 0; i < HO_RQD_CAUSE_MAX; i++) { + /* evaluate valid HO cause only */ + if (lchan->meas_preproc.cur_ho_causes & (1 << i)) { + /* check if HO cause has not already in recorded list */ + if ((lchan->meas_preproc.cur_ho_causes & (1 << i)) == (lchan->meas_preproc.rec_ho_causes & (1 << i))) + continue; + + /* not in the recorded list, update recorded list now */ + lchan->meas_preproc.rec_ho_causes |= (1 << i); + tx_rep_pending++; + LOGP(DMEAS, LOGL_DEBUG, "%s IPAC HO_CAUSE pending %d: recorded=0x%x, current=0x%x\n", + gsm_lchan_name(lchan), + tx_rep_pending, + lchan->meas_preproc.rec_ho_causes, + lchan->meas_preproc.cur_ho_causes); + + } + } + + if (tx_rep_pending) + return tx_rep_pending; + + return 0; +} + +/* Parse MS measurement report */ +int meas_parse_ms_rep(struct msgb *msg, struct gsm_lchan *lchan) +{ + int i; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t *l3_info = gh->data; + struct bitvec *bv; + + lchan->meas_preproc.ms_pwr = ms_pwr_dbm(msg->trx->bts->band, lchan->meas.l1_info[0] >> 3); + lchan->meas_preproc.ms_ta = lchan->meas.l1_info[1] >> 1; + + /* UL measurement */ + LOGP(DMEAS, LOGL_DEBUG, "%s L3 INFO hexdump: %s\n", + gsm_lchan_name(lchan), + osmo_hexdump(msgb_l3(msg), + msgb_l3len(msg))); + + /* check if measurement is valid */ + if ( (l3_info[1] & 0x40) ) { + return -1; + } + + /* set DL MES_REP valid flag */ + lchan->meas.flags |= LC_DL_M_F_RES_VALID; + + /* BA_USED flag */ + if ( (l3_info[0] & 0x80) ) + lchan->meas.flags |= LC_NCELL_M_F_BA1; + + /* UL DTX flag */ + if ( (l3_info[0] & 0x40) ) + lchan->meas.flags |= LC_UL_M_F_DTX; + + LOGP(DMEAS, LOGL_DEBUG, "%s L3 INFO MEAS FLAGS 0x%02x\n", + gsm_lchan_name(lchan), + lchan->meas.flags); + + bv = talloc_zero(NULL, struct bitvec); + + bv->data_len = msgb_l3len(msg); + bv->data = l3_info; + bv->cur_bit = 2; + + lchan->meas_preproc.dl_res.full.rx_lev = bitvec_get_uint(bv, 6) & 0x3f; + bv->cur_bit += 2; + + lchan->meas_preproc.dl_res.sub.rx_lev = bitvec_get_uint(bv, 6) & 0x3f; + bv->cur_bit += 1; + + lchan->meas_preproc.dl_res.full.rx_qual = bitvec_get_uint(bv, 3) & 0x7; + lchan->meas_preproc.dl_res.sub.rx_qual = bitvec_get_uint(bv, 3) & 0x7; + + /* neighbor cells */ + lchan->meas_preproc.num_cell = bitvec_get_uint(bv, 3) & 0x07; + if ( lchan->meas_preproc.num_cell == 0 ) { + for (i = 0; i < ARRAY_SIZE(lchan->meas_preproc.cell); i++){ + lchan->meas_preproc.cell[i].bcch_freq = 0; + lchan->meas_preproc.cell[i].bsic = 0; + lchan->meas_preproc.cell[i].rxlev = 0; + memset(lchan->meas_preproc.cell[i].rxlev_vec, 0, MAX_NUM_MEAS_PREPROC); + memset(lchan->meas_preproc.cell[i].rxlev_ave_res, 0, MAX_NUM_MEAS_PREPROC); + } + } + + /* NCELL info */ + for (i = 0 ; i < lchan->meas_preproc.num_cell; i++) { + lchan->meas_preproc.cell[i].rxlev = bitvec_get_uint(bv, 6) & 0x3f; + lchan->meas_preproc.cell[i].bcch_freq = bitvec_get_uint(bv, 5) & 0x1f; + lchan->meas_preproc.cell[i].bsic = bitvec_get_uint(bv, 6) & 0x3f; + + LOGP(DMEAS, LOGL_DEBUG, "%s NCELL(%d) RXL-NCELL=%3ddBm BCCH-FREQ-NCELL=%d BSIC-NCELL=%d\n", + gsm_lchan_name(lchan), + i, + rxlev2dbm(lchan->meas_preproc.cell[i].rxlev), + lchan->meas_preproc.cell[i].bcch_freq, + lchan->meas_preproc.cell[i].bsic); + } + /* copy UL measurement results */ + lchan->meas_preproc.ul_res = lchan->meas.ul_res; + + LOGP(DMEAS, LOGL_DEBUG, "%s RXL-FULL-UL=%3ddBm RXL-SUB-UL=%3ddBm\n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.ul_res.full.rx_lev), + rxlev2dbm(lchan->meas_preproc.ul_res.sub.rx_lev)); + + LOGP(DMEAS, LOGL_DEBUG, "%s RXQ-FULL-UL=%d RXQ-SUB-UL=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ul_res.full.rx_qual, + lchan->meas_preproc.ul_res.sub.rx_qual); + + LOGP(DMEAS, LOGL_DEBUG, "%s MS L1 TX PWR=%3ddBm MS L1 TA=%u\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.ms_pwr, + lchan->meas_preproc.ms_ta); + + LOGP(DMEAS, LOGL_DEBUG, "%s RXL-FULL-DL=%3ddBm RXL-SUB-DL=%3ddBm\n", + gsm_lchan_name(lchan), + rxlev2dbm(lchan->meas_preproc.dl_res.full.rx_lev), + rxlev2dbm(lchan->meas_preproc.dl_res.sub.rx_lev)); + + LOGP(DMEAS, LOGL_DEBUG, "%s RXQ-FULL-DL=%d RXQ-SUB-DL=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.dl_res.full.rx_qual, + lchan->meas_preproc.dl_res.sub.rx_qual); + + /* free bit vector buffer */ + talloc_free(bv); + + return 0; +} + +static int meas_preproc_unweighted_avg(int start_idx, int avg_len, uint8_t *in_buf, int buf_size) { + int i, j; + int avg_res = 0; + + for (i = start_idx; i < start_idx + avg_len; i++) { + j = i % buf_size; + avg_res += in_buf[j]; + } + return (avg_res/avg_len); +} + +int meas_preproc_avg(struct gsm_lchan *lchan) +{ + struct ipac_preproc_cfg preproc_cfg = lchan->ts->trx->trx_preproc_cfg; + int i, start_idx; + int ul_avg = 0; + int dl_avg = 0; + int ul_qual_avg = 0; + int dl_qual_avg = 0; + int ms_bts_avg = 0; + int ncell_avg[6] = {0, 0, 0, 0, 0, 0}; + uint8_t avg_method = preproc_cfg.ave_cfg.ave_method; + uint8_t num_cell = lchan->meas_preproc.num_cell; + + /* Only unweighted average method is supported for the moment*/ + if (!(preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_METHOD_UNWEIGHTED)) { + LOGP(DMEAS, LOGL_ERROR, "%s Unsupported preprocessing averaging method 0x%02x\n", + gsm_lchan_name(lchan), + avg_method); + return -2; + } + + /*STEP1: cache measurement results*/ + LOGP(DMEAS, LOGL_DEBUG, "%s Caching input MS MEAS_REP (%d/%d)\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.meas_idx, + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + + /* cache RXLEV */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXLEV) { + LOGP(DMEAS, LOGL_DEBUG, "%s RSL_IPAC_EIE_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x \n", + gsm_lchan_name(lchan), + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].param_id, + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqave, + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].ave_method, + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + + lchan->meas_preproc.ul_ave_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.ul_res.full.rx_lev; + lchan->meas_preproc.dl_ave_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.dl_res.full.rx_lev; + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached input ul_ave_vec %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ul_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt), + lchan->meas_preproc.meas_idx); + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached input dl_ave_vec %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ul_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt), + lchan->meas_preproc.meas_idx); + } + + /* cache RXQUAL */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXQUAL) { + LOGP(DMEAS, LOGL_DEBUG, "%s RSL_IPAC_EIE_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x \n", + gsm_lchan_name(lchan), + preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].param_id, + preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqave, + preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].ave_method, + preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt); + + lchan->meas_preproc.ul_qual_ave_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.ul_res.full.rx_qual; + lchan->meas_preproc.dl_qual_ave_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.dl_res.full.rx_qual; + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached input ul_qual_ave_vec %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ul_qual_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt), + lchan->meas_preproc.meas_idx); + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached input dl_qual_ave_vec %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.dl_qual_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt), + lchan->meas_preproc.meas_idx); + } + + /* cacch MS-BTS distance */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_DIST) { + LOGP(DMEAS, LOGL_DEBUG, "%s RSL_IPAC_EIE_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x \n", + gsm_lchan_name(lchan), + preproc_cfg.ms_ave_cfg[IPAC_MS_BTS_DIS_AVE].param_id, + preproc_cfg.ms_ave_cfg[IPAC_MS_BTS_DIS_AVE].h_reqave, + preproc_cfg.ms_ave_cfg[IPAC_MS_BTS_DIS_AVE].ave_method, + preproc_cfg.ms_ave_cfg[IPAC_MS_BTS_DIS_AVE].h_reqt); + + lchan->meas_preproc.ms_bts_ave_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.ms_ta; + + LOGP(DMEAS, LOGL_DEBUG, "%s Cached input ms_bts_ave_vec %s, idx=%d\n", + gsm_lchan_name(lchan), + osmo_hexdump(lchan->meas_preproc.ms_bts_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt), + lchan->meas_preproc.meas_idx); + } + + /* neighbor cells RXLEV averaging */ + for (i = 0; i < num_cell; i++) { + lchan->meas_preproc.cell[i].rxlev_vec[lchan->meas_preproc.meas_idx] = lchan->meas_preproc.cell[i].rxlev; + } + + /* set measurement valid flag */ + if ((lchan->meas_preproc.meas_idx == preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt - 1) && !(lchan->meas.flags & LC_AVE_F_RES_VALID )) + lchan->meas.flags |= LC_AVE_F_RES_VALID; + + /* rollover measurement index of input vectors*/ + lchan->meas_preproc.meas_idx = (lchan->meas_preproc.meas_idx + 1) % preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt; + + /*make sure we cache enough measurement results before averaging*/ + if (!(lchan->meas.flags & LC_AVE_F_RES_VALID)) + return -EINPROGRESS; + + /* STEP 2: Averaging process */ + LOGP(DMEAS, LOGL_DEBUG, "%s Caching MS MEAS_AVE_REP (%d/%d)\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.meas_ave_idx, + preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + + /* calculate start averaging index in order to average last h_reqave measurements */ + start_idx = lchan->meas_preproc.meas_idx - preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqave + 1; + if (start_idx < 0) + start_idx += preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt; + + LOGP(DMEAS, LOGL_DEBUG, "%s cur_idx=%d, start_idx=%d\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.meas_idx, + start_idx); + + /* servicing cell averaging */ + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXLEV) { + /* averaging RXLEV */ + ul_avg = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqave, lchan->meas_preproc.ul_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + dl_avg = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqave, lchan->meas_preproc.dl_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + + /* return averaged RXLEV results */ + lchan->meas_preproc.ul_ave_res[lchan->meas_preproc.meas_ave_idx] = ul_avg; + lchan->meas_preproc.dl_ave_res[lchan->meas_preproc.meas_ave_idx] = dl_avg; + + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_RXQUAL) { + /* averaging RXQUAL */ + ul_qual_avg = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqave, lchan->meas_preproc.ul_qual_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt); + dl_qual_avg = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqave, lchan->meas_preproc.dl_qual_ave_vec, preproc_cfg.ms_ave_cfg[IPAC_RXQUAL_AVE].h_reqt); + + /* return averaged RXQUAL results */ + lchan->meas_preproc.ul_qual_ave_res[lchan->meas_preproc.meas_ave_idx] = ul_qual_avg; + lchan->meas_preproc.dl_qual_ave_res[lchan->meas_preproc.meas_ave_idx] = dl_qual_avg; + } + + if (preproc_cfg.meas_mode_flags & MEAS_PREPROC_AVG_PARAMID_DIST) { + /* averaging MS-BTS distance */ + ms_bts_avg = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_MS_BTS_DIS_AVE].h_reqave, lchan->meas_preproc.ms_bts_ave_vec, MAX_NUM_MEAS_PREPROC); + + /* return averaged MS-BTS distance results */ + lchan->meas_preproc.ms_bts_ave_res[lchan->meas_preproc.meas_ave_idx] = ms_bts_avg; + } + + /* neighbor cell RXLEV averaging */ + for (i = 0; i < num_cell; i++) { + /* averaging neighbor cell RXLEV */ + ncell_avg[i] = meas_preproc_unweighted_avg(start_idx, preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqave, lchan->meas_preproc.cell[i].rxlev_vec, MAX_NUM_MEAS_PREPROC); + + /* return neighbor cell averaged RXLEV result */ + lchan->meas_preproc.cell[i].rxlev_ave_res[lchan->meas_preproc.meas_ave_idx] = ncell_avg[i]; + } + + /* set average valid flag */ + if ((lchan->meas_preproc.meas_ave_idx == preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt - 1) && !(lchan->meas.flags & LC_AVE_F_CACHE_VALID )) + lchan->meas.flags |= LC_AVE_F_CACHE_VALID; + + /* roll-over measurement average results pointer */ + lchan->meas_preproc.meas_ave_idx = (lchan->meas_preproc.meas_ave_idx + 1) % (preproc_cfg.ms_ave_cfg[IPAC_RXLEV_AVE].h_reqt); + + /*make sure we cache enough average measurement results before exiting the routine*/ + if (!(lchan->meas.flags & LC_AVE_F_CACHE_VALID)) + return -EINPROGRESS; + + return 0; +} diff --git a/src/common/rsl.c b/src/common/rsl.c index 12579cb6..8eae98d4 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -534,6 +534,9 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) LOGP(DRSL, LOGL_NOTICE, "%s Tx RF CHAN REL ACK\n", gsm_lchan_name(lchan)); + /* clear cached measurement reports */ + meas_preproc_rep_init(lchan); + /* * Free the LAPDm resources now that the BTS * has released all the resources. @@ -560,6 +563,9 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan) LOGP(DRSL, LOGL_NOTICE, "%s Tx CHAN ACT ACK\n", gsm_lchan_name(lchan)); + /* Reset cached measurement reports */ + meas_preproc_rep_init(lchan); + msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); if (!msg) return -ENOMEM; @@ -643,6 +649,9 @@ int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause) "%s Sending Connection Failure: cause = 0x%02x\n", gsm_lchan_name(lchan), cause); + /* clear cached measurement reports */ + meas_preproc_rep_init(lchan); + msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); if (!msg) return -ENOMEM; @@ -2121,6 +2130,281 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc) pdch_act? "ACT" : "DEACT", rc); } +/* Parse received MEASurement PREPROCessing DeFauLTs */ +static int rsl_rx_ipac_meas_preproc_dflts(struct gsm_bts_trx *trx, struct msgb *msg) +{ + int rc; + uint8_t len; + uint8_t val; + struct tlv_parsed tp; + struct tlv_parsed eie_tp; + struct ipac_preproc_cfg *preproc_cfg = &trx->trx_preproc_cfg; + uint16_t preproc_eie_l3h; + uint16_t offset = 0; + int i; + + LOGP(DRSL, LOGL_INFO, "%s RSL received MEAS_PREPROC_DFT\n", gsm_trx_name(trx)); + + rc = rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rc < 0) + { + LOGP(DRSL, LOGL_ERROR, "%s Preprocessing Measurement Default was not parsed successfully (%d).\n", gsm_trx_name(trx), rc); + return rc; + } + + if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) { + val = *TLVP_VAL(&tp, RSL_IE_MS_POWER); + offset += 2; + LOGP(DRSL, LOGL_NOTICE, "%s RSL_IE_MS_POWER presented (0x%02x)\n", gsm_trx_name(trx), val); + + } + + if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM)) { + len = TLVP_LEN(&tp, RSL_IE_MS_POWER_PARAM); + offset += 2; + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IE_MS_POWER_PARAM hexdump: %s\n", gsm_trx_name(trx), osmo_hexdump(msgb_l3(msg) + offset, len)); + + /* parse MS measurement average parameters EIE */ + for(i = 0; i < 2; i++) { + rc = rsl_ipac_eie_tlv_parse(&eie_tp, msgb_l3(msg) + offset, 4); + if (rc < 0) + { + LOGP(DRSL, LOGL_NOTICE, "%s MS power Parameters EIE was not parsed successfully (%d).\n", gsm_trx_name(trx), rc); + return rc; + } + + offset += 4; + + /* parse MS measurement average parameters */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_MEAS_AVG_CFG)) { + rc = TLVP_LEN(&eie_tp, RSL_IPAC_EIE_MEAS_AVG_CFG); + if (rc > 2) { + LOGP(DRSL, LOGL_ERROR, "%s Unsupported RSL_IPAC_EIE_MEAS_AVG_CFG len > 0x%02x\n", gsm_trx_name(trx), rc); + return rsl_tx_error_report(msg->trx, RSL_ERR_IE_LENGTH); + } + /* parse MS measurement averaging configuration parameters */ + struct ipac_preproc_ave_cfg *ave_cfg = (struct ipac_preproc_ave_cfg*) TLVP_VAL(&eie_tp, RSL_IPAC_EIE_MEAS_AVG_CFG); + + /* set parameter ID flags*/ + switch (ave_cfg->param_id) { + case IPAC_RXLEV_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_PARAMID_RXLEV; + break; + + case IPAC_RXQUAL_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_PARAMID_RXQUAL; + break; + + case IPAC_MS_BTS_DIS_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_PARAMID_DIST; + break; + + default: + break; + } + + /* set averaging method flags*/ + switch (ave_cfg->ave_method) { + case IPAC_UNWEIGHTED_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_METHOD_UNWEIGHTED; + break; + + case IPAC_WEIGHTED_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_METHOD_WEIGHTED; + break; + + case IPAC_MEDIAN_AVE: + preproc_cfg->meas_mode_flags |= MEAS_PREPROC_AVG_METHOD_MEDIAN; + break; + + default: + break; + } + + /* parse HReqAve parameter */ + if (ave_cfg->h_reqave < 1 || ave_cfg->h_reqave > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqave=%d is out of specification (%d).\n", gsm_trx_name(trx), ave_cfg->h_reqave, MAX_NUM_MEAS_PREPROC); + ave_cfg->h_reqave = 4; + } + + /* parse HReqT parameter */ + if (ave_cfg->h_reqt < 1 || ave_cfg->h_reqt > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqt=%d is out of specification (%d).\n", gsm_trx_name(trx), ave_cfg->h_reqt, MAX_NUM_MEAS_PREPROC); + ave_cfg->h_reqt = 4; + } + + /* validate HReqAve * HReqT <= MAX_NUM_MEAS_PREPROC */ + if (ave_cfg->h_reqave * ave_cfg->h_reqt > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqt(%d)*h_reqave(%d)=%d is out of specification (must be < %d).\n", + gsm_trx_name(trx), + ave_cfg->h_reqt, + ave_cfg->h_reqave, + ave_cfg->h_reqt*ave_cfg->h_reqave, + MAX_NUM_MEAS_PREPROC); + + ave_cfg->h_reqt = ave_cfg->h_reqave = 4; + } + + memcpy(&preproc_cfg->ms_ave_cfg[ave_cfg->param_id], ave_cfg, sizeof(struct ipac_preproc_ave_cfg)); + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_MS_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x, flags=0x%X\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.ms_ave_cfg[ave_cfg->param_id].param_id, + trx->trx_preproc_cfg.ms_ave_cfg[ave_cfg->param_id].h_reqave, + trx->trx_preproc_cfg.ms_ave_cfg[ave_cfg->param_id].ave_method, + trx->trx_preproc_cfg.ms_ave_cfg[ave_cfg->param_id].h_reqt, + preproc_cfg->meas_mode_flags); + + } + } + + /* parse MS power control threshold EIE from remaining bytes in MS measurement average parameters EIE */ + rc = rsl_ipac_eie_tlv_parse(&eie_tp, msgb_l3(msg) + offset, len - offset + 4); + if (rc < 0) + { + LOGP(DRSL, LOGL_ERROR, "%s MS power control threshold EIE was not parsed successfully (%d).\n", gsm_trx_name(trx), rc); + return rc; + } + + /* parse MS power control threshold EIE */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_MS_PWR_CTL)) { + LOGP(DRSL, LOGL_NOTICE, "%s MS power control threshold EIE presented. (unused for now)\n", gsm_trx_name(trx)); + } + + /* parse MS power control comparators EIE */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_PC_THRESH_COMP)) { + LOGP(DRSL, LOGL_NOTICE, "%s MS power control comparators EIE presented. (unused for now)\n", gsm_trx_name(trx)); + } + + } + + /* parse measurement average parameters EIE */ + if (TLVP_PRESENT(&tp, RSL_IE_PREPROC_PARAM)) { + len = TLVP_LEN(&tp, RSL_IE_PREPROC_PARAM); + LOGP(DRSL, LOGL_NOTICE, "%s RSL_IE_PREPROC_PARAM presented, len=0x%02x\n", gsm_trx_name(trx), len); + + /* determine basic MEAS_RES or PREPROC_MEAS_RES to be sent to BSC */ + trx->trx_preproc_cfg.meas_rep_mode = (*TLVP_VAL(&tp, RSL_IE_PREPROC_PARAM)) & 0x01; + + if (!trx->trx_preproc_cfg.meas_rep_mode ){ + LOGP(DRSL, LOGL_NOTICE, "%s Basic MEAS_RES will be used (0x%02x)\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.meas_rep_mode); + return 0; + } + + LOGP(DRSL, LOGL_NOTICE, "%s PREPROC_MEAS_RES will be used (0x%02x)\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.meas_rep_mode); + + /* calculate start of preprocessing EIE */ + preproc_eie_l3h = msgb_l3len(msg) - (len--) + 1; + + LOGP(DRSL, LOGL_DEBUG, "%s RSL_EIE_PREPROC_PARAM dump: %s\n", gsm_trx_name(trx), osmo_hexdump(msgb_l3(msg) + preproc_eie_l3h, len)); + + rc = rsl_ipac_eie_tlv_parse(&eie_tp, msgb_l3(msg) + preproc_eie_l3h, len); + if (rc < 0) + { + LOGP(DRSL, LOGL_ERROR, "%s Pre-processing Parameters EIE was not parsed successfully (%d).\n", gsm_trx_name(trx), rc); + return rc; + } + + /* parse HO measurement average parameters */ + if (TLVP_PRESENT(&tp, RSL_IPAC_EIE_MEAS_AVG_CFG)) { + len = TLVP_LEN(&tp, RSL_IPAC_EIE_MEAS_AVG_CFG); + if (len > 2) { + LOGP(DRSL, LOGL_ERROR, "%s Unsupported RSL_IPAC_EIE_MEAS_AVG_CFG len > 0x%02x\n", gsm_trx_name(trx), len); + return rc; + } + + /* parse HO measurement averaging configuration parameters */ + struct ipac_preproc_ave_cfg *ave_cfg = (struct ipac_preproc_ave_cfg*) TLVP_VAL(&tp, RSL_IPAC_EIE_MEAS_AVG_CFG); + + /* parse HReqAve parameter */ + if (ave_cfg->h_reqave < 1 || ave_cfg->h_reqave > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqave=%d is out of specification (%d).\n", gsm_trx_name(trx), ave_cfg->h_reqave, MAX_NUM_MEAS_PREPROC); + ave_cfg->h_reqave = 4; + } + + /* parse HReqT parameter */ + if (ave_cfg->h_reqt < 1 || ave_cfg->h_reqt > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqt=%d is out of specification (%d).\n", gsm_trx_name(trx), ave_cfg->h_reqt, MAX_NUM_MEAS_PREPROC); + ave_cfg->h_reqt = 4; + } + + /* validate HReqAve * HReqT <= MAX_NUM_MEAS_PREPROC */ + if (ave_cfg->h_reqave * ave_cfg->h_reqt > MAX_NUM_MEAS_PREPROC) { + LOGP(DRSL, LOGL_NOTICE, "%s h_reqt(%d)*h_reqave(%d)=%d is out of specification (must be < %d).\n", + gsm_trx_name(trx), + ave_cfg->h_reqt, + ave_cfg->h_reqave, + ave_cfg->h_reqt*ave_cfg->h_reqave, + MAX_NUM_MEAS_PREPROC); + + ave_cfg->h_reqt = ave_cfg->h_reqave = 4; + } + + memcpy(&preproc_cfg->ave_cfg, ave_cfg, sizeof(struct ipac_preproc_ave_cfg)); + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_HO_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.ave_cfg.param_id, + trx->trx_preproc_cfg.ave_cfg.h_reqave, + trx->trx_preproc_cfg.ave_cfg.ave_method, + trx->trx_preproc_cfg.ave_cfg.h_reqt); + } + + /* parse HO threshold parameters */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_HANDO_THRESH)) { + struct ipac_preproc_ho_thresh *ho_thresh = (struct ipac_preproc_ho_thresh*)TLVP_VAL(&eie_tp, RSL_IPAC_EIE_HANDO_THRESH); + + memcpy(&preproc_cfg->ho_thresh, ho_thresh, sizeof(struct ipac_preproc_ho_thresh)); + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_HO_THRESH l_rxlev_ul_h=0x%02x, l_rxlev_dl_h=0x%02x, rxlev_ul_ih=0x%02x, rxlev_dl_ih=0x%02x, l_rxqual_ul_h=0x%02x, l_rxqual_dl_h=0x%02x, ms_range_max=0x%02x \n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.ho_thresh.l_rxlev_ul_h, + trx->trx_preproc_cfg.ho_thresh.l_rxlev_dl_h, + trx->trx_preproc_cfg.ho_thresh.rxlev_ul_ih, + trx->trx_preproc_cfg.ho_thresh.rxlev_dl_ih, + trx->trx_preproc_cfg.ho_thresh.l_rxqual_ul_h, + trx->trx_preproc_cfg.ho_thresh.l_rxqual_dl_h, + trx->trx_preproc_cfg.ho_thresh.ms_range_max); + } + + /* parse NCELL default parameters */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_NCELL_DEFAULTS)) { + struct ipac_preproc_ncell_dflts *ncell_dflts = (struct ipac_preproc_ncell_dflts*)TLVP_VAL(&eie_tp, RSL_IPAC_EIE_NCELL_DEFAULTS); + + memcpy(&preproc_cfg->ncell_dflts, ncell_dflts, sizeof(struct ipac_preproc_ncell_dflts)); + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_NCELL_DEFAULTS rxlev_min_def=0x%02x, ho_margin_def=0x%02x, ms_txpwr_max_def=0x%02x\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.ncell_dflts.rxlev_min_def, + trx->trx_preproc_cfg.ncell_dflts.ho_margin_def, + trx->trx_preproc_cfg.ncell_dflts.ms_txpwr_max_def); + } + + /* parse HO threshold comparators */ + if (TLVP_PRESENT(&eie_tp, RSL_IPAC_EIE_HO_THRESH_COMP)) { + struct ipac_preproc_ho_comp *ho_comp = (struct ipac_preproc_ho_comp*)TLVP_VAL(&eie_tp, RSL_IPAC_EIE_HO_THRESH_COMP); + + memcpy(&preproc_cfg->ho_comp, ho_comp, sizeof(struct ipac_preproc_ho_comp)); + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_HO_THRESH_COMP p5=0x%02x, n5=0x%02x, p6=0x%02x, n6=0x%02x, p7=0x%02x, n7=0x%02x, p8=0x%02x, n8=0x%02x, ho_interval=0x%02x\n", + gsm_trx_name(trx), + trx->trx_preproc_cfg.ho_comp.p5, + trx->trx_preproc_cfg.ho_comp.n5, + trx->trx_preproc_cfg.ho_comp.p6, + trx->trx_preproc_cfg.ho_comp.n6, + trx->trx_preproc_cfg.ho_comp.p7, + trx->trx_preproc_cfg.ho_comp.n7, + trx->trx_preproc_cfg.ho_comp.p8, + trx->trx_preproc_cfg.ho_comp.n8, + trx->trx_preproc_cfg.ho_comp.ho_interval); + } + + LOGP(DRSL, LOGL_NOTICE, "%s RSL_IE_PREPROC_PARAM parsing is done \n", gsm_trx_name(trx)); + + } + + return rc; +} +/* /* * selecting message */ @@ -2236,11 +2520,122 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len) return abis_bts_rsl_sendmsg(msg); } +/* 8.4.18 PREPROCessed MEASurement RESult */ +static int rsl_tx_preproc_meas_res(struct gsm_lchan *lchan) +{ + struct msgb *msg; + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t preproc_meas_len = 0; + uint8_t ho_cause[IPAC_HO_RQD_CAUSE_MAX]; + uint8_t ho_cause_len = 0; + uint8_t ho_candidate_len = 0; + struct ipac_preproc_ho_candidates ho_candidate[6]; + int i, j; + + memset(ho_cause, 0, sizeof(ho_cause)); + memset(ho_candidate, 0, sizeof(ho_candidate)); + + + /* make sure averaged result is valid */ + if ( !(lchan->meas.flags & LC_AVE_F_RES_VALID) ) + return -EINPROGRESS; + + msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); + if (!msg) + return -ENOMEM; + + /* calculate HO causes list length in number of bytes */ + for (i = 0, j = 0; i < IPAC_HO_RQD_CAUSE_MAX; i++) { + if (lchan->meas_preproc.rec_ho_causes & (1 << i)) { + ho_cause[j++] = i + 1; + ho_cause_len++; + } + } + + /* take into account of EIE and LEN bytes */ + preproc_meas_len += ho_cause_len + 2; + + /* construct neighbor cell list */ + if(lchan->meas_preproc.num_cell) { + /* better cell is always the first candidate */ + ho_candidate[0].bsic = lchan->meas_preproc.cell[lchan->meas_preproc.better_ncell].bsic; + ho_candidate[0].bcch_freq = lchan->meas_preproc.cell[lchan->meas_preproc.better_ncell].bcch_freq; + ho_candidate_len += sizeof(struct ipac_preproc_ho_candidates); + + for (i = 0 ; i < lchan->meas_preproc.num_cell; i++) { + if (i == lchan->meas_preproc.better_ncell) + continue; + + ho_candidate[i].bsic = lchan->meas_preproc.cell[i].bsic; + ho_candidate[i].bcch_freq = lchan->meas_preproc.cell[i].bcch_freq; + ho_candidate_len += sizeof(struct ipac_preproc_ho_candidates); + } + + /* take into account of EIE and LEN bytes */ + preproc_meas_len += ho_candidate_len + 2; + } + + /* push MDISC, MT, CHANNEL NUMBER */ + rsl_dch_push_hdr(msg, RSL_MT_PREPROC_MEAS_RES, chan_nr); + + /* MEAS_PREPROC_RES IE 9.3.34 */ + msgb_tv_put(msg, RSL_IE_PREPROC_MEAS, preproc_meas_len); + + /* HO CAUSE EIE 4.3.12 */ + msgb_tlv_put(msg, RSL_IPAC_EIE_HO_CAUSE, ho_cause_len, (uint8_t *)&ho_cause); + + /* HO CANDIDATES EIE 4.3.13 */ + msgb_tlv_put(msg, RSL_IPAC_EIE_HO_CANDIDATES, ho_candidate_len, (uint8_t *)&ho_candidate); + + msg->trx = lchan->ts->trx; + + return abis_bts_rsl_sendmsg(msg); +} + +/* call back for measurement preprocessing timer */ +static void rsl_meas_preproc_timer_cb(void *_lchan) +{ + struct gsm_lchan *lchan = _lchan; + int rc; + + LOGP(DRSL, LOGL_DEBUG, "%s MEAS_PREPROC TIMER expired within %d seconds\n", + gsm_lchan_name(lchan), + lchan->ts->trx->trx_preproc_cfg.ho_comp.ho_interval); + + //update recorded HO cause list + lchan->meas_preproc.rec_ho_causes = lchan->meas_preproc.cur_ho_causes; + + LOGP(DRSL, LOGL_DEBUG, "%s Sending MEAS_PREPROC_RES to BSC, recorded=0x%x, current=0x%x\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.rec_ho_causes, + lchan->meas_preproc.cur_ho_causes); + + //send MEAS_PREPROC to BSC + rc = rsl_tx_preproc_meas_res(lchan); + if (rc < 0) { + LOGP(DRSL, LOGL_ERROR, "%s Failed to send preprocessed measurement result \n", + gsm_lchan_name(lchan)); + return; + } + + //restart timer if the current HO cause list is not empty, i.e. no MS has not started HO procedure yet + if (lchan->meas_preproc.cur_ho_causes) { + LOGP(DRSL, LOGL_DEBUG, "%s MEAS_PREPROC TIMER restarted, interval=%d seconds\n", + gsm_lchan_name(lchan), + lchan->ts->trx->trx_preproc_cfg.ho_comp.ho_interval); + + //schedule restart the timer + osmo_timer_schedule(&lchan->meas_preproc.preproc_ho_timer, lchan->ts->trx->trx_preproc_cfg.ho_comp.ho_interval, 0); + } +} + /* call-back for LAPDm code, called when it wants to send msgs UP */ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) { struct gsm_lchan *lchan = ctx; struct abis_rsl_common_hdr *rh = msgb_l2(msg); + struct ipac_preproc_cfg preproc_cfg = lchan->ts->trx->trx_preproc_cfg; + int rc; if (lchan->state != LCHAN_S_ACTIVE) { LOGP(DRSL, LOGL_INFO, "%s(%s) is not active . Dropping message.\n", @@ -2253,20 +2648,89 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) /* check if this is a measurement report from SACCH which needs special * processing before forwarding */ - if (rslms_is_meas_rep(msg)) { - int rc; - - LOGP(DRSL, LOGL_INFO, "%s Handing RLL msg %s from LAPDm to MEAS REP\n", + if (!rslms_is_meas_rep(msg)) { + LOGP(DRSL, LOGL_INFO, "%s Fwd RLL msg %s from LAPDm to A-bis\n", gsm_lchan_name(lchan), rsl_msg_name(rh->msg_type)); + + return abis_bts_rsl_sendmsg(msg); + + } + + LOGP(DRSL, LOGL_INFO, "%s Handing RLL msg %s from LAPDm to %s MEAS REP\n", + gsm_lchan_name(lchan), rsl_msg_name(rh->msg_type), preproc_cfg.meas_rep_mode? "PREPROC": ""); + + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IPAC_EIE_MEAS_AVG_CFG param_id=0x%02x, h_reqave=0x%02x, avg_method=0x%02x, h_reqt=0x%02x \n", + gsm_lchan_name(lchan), + preproc_cfg.ave_cfg.param_id, + preproc_cfg.ave_cfg.h_reqave, + preproc_cfg.ave_cfg.ave_method, + preproc_cfg.ave_cfg.h_reqt); + + /* check if it is a basic MEAS_REP */ + if (!preproc_cfg.meas_rep_mode) { + /* send basic MEASurement RESult */ rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg)); msgb_free(msg); return rc; - } else { - LOGP(DRSL, LOGL_INFO, "%s Fwd RLL msg %s from LAPDm to A-bis\n", - gsm_lchan_name(lchan), rsl_msg_name(rh->msg_type)); + } - return abis_bts_rsl_sendmsg(msg); + /* always perform preprocessing measurement on traffic channels */ + if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) { + msgb_free(msg); + return 0; + } + + /* parse MS measurement report */ + rc = meas_parse_ms_rep(msg, lchan); + if (rc < 0) { + msgb_free(msg); + return 0; } + + /* averaging MS measurement results */ + rc = meas_preproc_avg(lchan); + if (rc < 0) { + LOGP(DRSL, LOGL_NOTICE, "%s MS measurement averaging is in progress (%d)\n", gsm_lchan_name(lchan), rc); + msgb_free(msg); + return rc; + } + + /* calculate possible HO Cause */ + rc = meas_update_ho_causes(lchan); + if (!rc) { + msgb_free(msg); + return 0; + } + + /* send preprocessed measurement report to BSC */ + LOGP(DRSL, LOGL_DEBUG, "%s Sending MEAS_PREPROC_RES to BSC, recorded=0x%x, current=0x%x\n", + gsm_lchan_name(lchan), + lchan->meas_preproc.rec_ho_causes, + lchan->meas_preproc.cur_ho_causes); + + rc = rsl_tx_preproc_meas_res(lchan); + if (rc < 0) { + msgb_free(msg); + return rc; + } + + /* check if T_ho timer is not active */ + if (!lchan->meas_preproc.preproc_ho_timer.active) { + /* Cancel any pending timer */ + osmo_timer_del(&lchan->meas_preproc.preproc_ho_timer); + + /* start T-ho timer */ + lchan->meas_preproc.preproc_ho_timer.data = lchan; + lchan->meas_preproc.preproc_ho_timer.cb = rsl_meas_preproc_timer_cb; + osmo_timer_schedule(&lchan->meas_preproc.preproc_ho_timer, preproc_cfg.ho_comp.ho_interval, 0); + + LOGP(DRSL, LOGL_DEBUG, "%s RSL_IE_PREPROC_PARAM TIMER started, interval=%d seconds\n", + gsm_lchan_name(lchan), + preproc_cfg.ho_comp.ho_interval); + } + + msgb_free(msg); + return 0; } static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg) @@ -2410,6 +2874,9 @@ static int rsl_rx_trx(struct gsm_bts_trx *trx, struct msgb *msg) case RSL_MT_SACCH_FILL: ret = rsl_rx_sacch_fill(trx, msg); break; + case RSL_MT_IPAC_MEAS_PREPROC_DFT: + ret = rsl_rx_ipac_meas_preproc_dflts(trx, msg); + break; default: LOGP(DRSL, LOGL_NOTICE, "undefined RSL TRX msg_type 0x%02x\n", th->msg_type); |