aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYves Godin <yves.godin@nutaq.com>2016-09-19 10:38:07 -0400
committerYves Godin <yves.godin@nutaq.com>2016-09-19 10:38:07 -0400
commitab358ab75f3a7d93476ac8fbb0d8d4ca73c3e338 (patch)
tree7d6e5b03a07dd679f76bcbb1d74586671d96b52e
parent13fdfd932be0e57ee0d229936094f4d32b03a886 (diff)
parent4d73bd8164b7a201e602b221219d924fc91c2d5d (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.h38
-rw-r--r--src/common/measurement.c730
-rw-r--r--src/common/rsl.c483
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);