diff --git a/include/osmo-bts/gsm_data_shared.h b/include/osmo-bts/gsm_data_shared.h index 41998ad80..e0d70b0bd 100644 --- a/include/osmo-bts/gsm_data_shared.h +++ b/include/osmo-bts/gsm_data_shared.h @@ -479,6 +479,11 @@ enum gsm_bts_features { BTS_FEAT_SPEECH_H_AMR, BTS_FEAT_ETWS_PN, BTS_FEAT_MS_PWR_CTRL_DSP, + /* When the feature is set then the measurement data is included in + * (PRIM_PH_DATA) and struct ph_tch_param (PRIM_TCH). Otherwise the + * measurement data is passed using a separate MPH INFO MEAS IND. + * (See also ticket: OS#2977) */ + BTS_FEAT_MEAS_PAYLOAD_COMB, _NUM_BTS_FEAT }; diff --git a/include/osmo-bts/scheduler_backend.h b/include/osmo-bts/scheduler_backend.h index 51c957cc3..aa2d6e9e9 100644 --- a/include/osmo-bts/scheduler_backend.h +++ b/include/osmo-bts/scheduler_backend.h @@ -54,7 +54,8 @@ int _sched_compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, enum osmo_ph_pres_info_type presence_info); int _sched_compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len); + enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len, + int16_t ta_offs_256bits, uint16_t ber10k, float rssi); ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, uint16_t *nbits); diff --git a/src/common/gsm_data_shared.c b/src/common/gsm_data_shared.c index b31d458b5..c4a60b512 100644 --- a/src/common/gsm_data_shared.c +++ b/src/common/gsm_data_shared.c @@ -109,6 +109,7 @@ const struct value_string gsm_bts_features_descs[] = { { BTS_FEAT_SPEECH_H_AMR, "Halfrate speech AMR" }, { BTS_FEAT_ETWS_PN, "ETWS Primary Notification on PCH" }, { BTS_FEAT_MS_PWR_CTRL_DSP, "DSP/HW based MS Power Control Loop" }, + { BTS_FEAT_MEAS_PAYLOAD_COMB, "Measurement and Payload data combined"}, { 0, NULL } }; diff --git a/src/common/l1sap.c b/src/common/l1sap.c index 889f7f609..21f3a8a87 100644 --- a/src/common/l1sap.c +++ b/src/common/l1sap.c @@ -629,42 +629,93 @@ static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool se } /* measurement information received from bts model */ -static int l1sap_info_meas_ind(struct gsm_bts_trx *trx, - struct osmo_phsap_prim *l1sap, - struct info_meas_ind_param *info_meas_ind) +static void process_l1sap_meas_data(struct gsm_bts_trx *trx, + struct osmo_phsap_prim *l1sap, + enum osmo_ph_prim ind_type) { struct bts_ul_meas ulm; struct gsm_lchan *lchan; + struct info_meas_ind_param *info_meas_ind; + struct ph_data_param *ph_data_ind; + struct ph_tch_param *ph_tch_ind; + uint8_t chan_nr; + uint32_t fn; + uint8_t inv_rssi; + uint8_t is_sub; + int16_t ta_offs_256bits; + uint16_t ber10k; + const char *ind_name; - lchan = get_active_lchan_by_chan_nr(trx, info_meas_ind->chan_nr); - if (!lchan) { - LOGPFN(DL1P, LOGL_ERROR, info_meas_ind->fn, - "No lchan for MPH INFO MEAS IND (chan_nr=%s)\n", rsl_chan_nr_str(info_meas_ind->chan_nr)); - return 0; + switch (ind_type) { + case PRIM_MPH_INFO: + /* (legacy way, see also OS#2977) */ + info_meas_ind = &l1sap->u.info.u.meas_ind; + chan_nr = info_meas_ind->chan_nr; + fn = info_meas_ind->fn; + inv_rssi = info_meas_ind->inv_rssi; + is_sub = info_meas_ind->is_sub; + ta_offs_256bits = info_meas_ind->ta_offs_256bits; + ber10k = info_meas_ind->ber10k; + ind_name = "MPH INFO"; + break; + case PRIM_TCH: + ph_tch_ind = &l1sap->u.tch; + if (ph_tch_ind->rssi == 0) + return; + chan_nr = ph_tch_ind->chan_nr; + fn = ph_tch_ind->fn; + inv_rssi = abs(ph_tch_ind->rssi); + is_sub = ph_tch_ind->is_sub; + ta_offs_256bits = ph_tch_ind->ta_offs_256bits; + ber10k = ph_tch_ind->ber10k; + ind_name = "TCH"; + break; + case PRIM_PH_DATA: + ph_data_ind = &l1sap->u.data; + if (ph_data_ind->rssi == 0) + return; + chan_nr = ph_data_ind->chan_nr; + fn = ph_data_ind->fn; + inv_rssi = abs(ph_data_ind->rssi); + is_sub = ph_data_ind->is_sub; + ta_offs_256bits = ph_data_ind->ta_offs_256bits; + ber10k = ph_data_ind->ber10k; + ind_name = "DATA"; + break; + default: + OSMO_ASSERT(false); } - DEBUGPFN(DL1P, info_meas_ind->fn, - "%s MPH_INFO meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n", - gsm_lchan_name(lchan), info_meas_ind->ta_offs_256bits, - info_meas_ind->ber10k, info_meas_ind->inv_rssi); + lchan = get_active_lchan_by_chan_nr(trx, chan_nr); + if (!lchan) { + LOGPFN(DL1P, LOGL_ERROR, fn, + "No lchan for %s MEAS IND (chan_nr=%s)\n", + ind_name, rsl_chan_nr_str(chan_nr)); + return; + } + + DEBUGPFN(DL1P, fn, + "%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n", + gsm_lchan_name(lchan), ind_name, ta_offs_256bits, ber10k, + inv_rssi); /* in the GPRS case we are not interested in measurement * processing. The PCU will take care of it */ if (lchan->type == GSM_LCHAN_PDTCH) - return 0; + return; memset(&ulm, 0, sizeof(ulm)); - ulm.ta_offs_256bits = info_meas_ind->ta_offs_256bits; - ulm.ber10k = info_meas_ind->ber10k; - ulm.inv_rssi = info_meas_ind->inv_rssi; - ulm.is_sub = info_meas_ind->is_sub; + ulm.ta_offs_256bits = ta_offs_256bits; + ulm.ber10k = ber10k; + ulm.inv_rssi = inv_rssi; + ulm.is_sub = is_sub; /* we assume that symbol period is 1 bit: */ - set_ms_to_data(lchan, info_meas_ind->ta_offs_256bits / 256, true); + set_ms_to_data(lchan, ta_offs_256bits / 256, true); - lchan_meas_process_measurement(lchan, &ulm, info_meas_ind->fn); + lchan_meas_process_measurement(lchan, &ulm, fn); - return 0; + return; } /* any L1 MPH_INFO indication prim received from bts model */ @@ -685,7 +736,12 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx, &info->u.time_ind); break; case PRIM_INFO_MEAS: - rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind); + /* We should never get an INFO_IND with PRIM_INFO_MEAS + * when BTS_FEAT_MEAS_PAYLOAD_COMB is enabled */ + if (gsm_bts_has_feature(trx->bts, BTS_FEAT_MEAS_PAYLOAD_COMB)) + OSMO_ASSERT(false); + + process_l1sap_meas_data(trx, l1sap, PRIM_MPH_INFO); break; default: LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n", @@ -1200,6 +1256,12 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, return -EINVAL; } + /* The ph_data_param contained in the l1sap primitive may contain + * measurement data. If this data is present, forward it for + * processing */ + if (gsm_bts_has_feature(trx->bts, BTS_FEAT_MEAS_PAYLOAD_COMB)) + process_l1sap_meas_data(trx, l1sap, PRIM_PH_DATA); + if (ts_is_pdch(&trx->ts[tn])) { lchan = get_lchan_by_chan_nr(trx, chan_nr); if (!lchan) @@ -1315,6 +1377,12 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, return 0; } + /* The ph_tch_param contained in the l1sap primitive may contain + * measurement data. If this data is present, forward it for + * processing */ + if (gsm_bts_has_feature(trx->bts, BTS_FEAT_MEAS_PAYLOAD_COMB)) + process_l1sap_meas_data(trx, l1sap, PRIM_TCH); + msgb_pull(msg, sizeof(*l1sap)); /* Low level layers always call us when TCH content is expected, even if diff --git a/src/common/scheduler.c b/src/common/scheduler.c index 3713b0633..e8df5373a 100644 --- a/src/common/scheduler.c +++ b/src/common/scheduler.c @@ -753,7 +753,8 @@ int _sched_compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, } int _sched_compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len) + enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len, + int16_t ta_offs_256bits, uint16_t ber10k, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -769,6 +770,10 @@ int _sched_compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, PRIM_OP_INDICATION, msg); l1sap->u.tch.chan_nr = chan_nr; l1sap->u.tch.fn = fn; + l1sap->u.tch.rssi = (int8_t) (rssi); + l1sap->u.tch.ber10k = ber10k; + l1sap->u.tch.ta_offs_256bits = ta_offs_256bits; + msg->l2h = msgb_put(msg, tch_len); if (tch_len) memcpy(msg->l2h, tch, tch_len); diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 38b43b9fb..b28c8947a 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -572,40 +572,6 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn) return l1sap_up(bts->c0, &l1sap); } - -static void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, int16_t toa256, - float ber, float rssi, uint32_t fn) -{ - memset(l1sap, 0, sizeof(*l1sap)); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO, - PRIM_OP_INDICATION, NULL); - l1sap->u.info.type = PRIM_INFO_MEAS; - l1sap->u.info.u.meas_ind.chan_nr = chan_nr; - l1sap->u.info.u.meas_ind.ta_offs_256bits = toa256; - l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000); - l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1); - l1sap->u.info.u.meas_ind.fn = fn; -} - -int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr, - int n_errors, int n_bits_total, float rssi, int16_t toa256) -{ - struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)]; - struct osmo_phsap_prim l1sap; - /* 100% BER is n_bits_total is 0 */ - float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total; - - LOGPFN(DMEAS, LOGL_DEBUG, fn, "RX UL measurement for %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS " - "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa256=%d\n", - gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power_ctrl.current), - rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa256); - - l1if_fill_meas_res(&l1sap, chan_nr, toa256, ber, rssi, fn); - - return l1sap_up(trx, &l1sap); -} - - /* primitive from common part */ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) { diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 4ff1f9726..73709995a 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -115,8 +115,6 @@ struct trx_l1h *trx_l1h_alloc(void *tall_ctx, struct phy_instance *pinst); int l1if_provision_transceiver_trx(struct trx_l1h *l1h); int l1if_provision_transceiver(struct gsm_bts *bts); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); -int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr, - int n_errors, int n_bits_total, float rssi, int16_t toa256); static inline struct l1sched_trx *trx_l1sched_hdl(struct gsm_bts_trx *trx) { diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 6671243f8..ab0472e7d 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -116,6 +116,7 @@ int bts_model_init(struct gsm_bts *bts) gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_F_AMR); gsm_bts_set_feature(bts, BTS_FEAT_SPEECH_H_AMR); gsm_bts_set_feature(bts, BTS_FEAT_CBCH); + gsm_bts_set_feature(bts, BTS_FEAT_MEAS_PAYLOAD_COMB); bts_model_vty_init(bts); diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c index 20d502f0e..5abb73b01 100644 --- a/src/osmo-bts-trx/scheduler_trx.c +++ b/src/osmo-bts-trx/scheduler_trx.c @@ -193,12 +193,10 @@ got_msg: /* TODO: Should we pass old TOA here? Otherwise we risk * unnecessary decreasing TA */ - /* Send uplink measurement information to L2 */ - l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, - 456, 456, -110, 0); - /* FIXME: use actual values for BER etc */ + /* Note: RSSI is set to 0 to indicate to the higher + * layers that this is a faked ph_data_ind */ _sched_compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0, - -110, 0, 0, 10000, + 0, 0, 0, 10000, PRES_INFO_INVALID); } } @@ -345,6 +343,9 @@ static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, uint8_t rsl_cmode = chan_state->rsl_cmode; uint8_t tch_mode = chan_state->tch_mode; struct osmo_phsap_prim *l1sap; + int32_t *toa256_sum = &chan_state->toa256_sum; + uint8_t *toa_num = &chan_state->toa_num; + int16_t toa256; /* handle loss detection of received TCH frames */ if (rsl_cmode == RSL_CMOD_SPD_SPEECH @@ -390,8 +391,17 @@ inval_mode1: LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "TCH mode invalid, please fix!\n"); len = 0; } - if (len) - _sched_compose_tch_ind(l1t, tn, fn, chan, tch_data, len); + + if (len) { + if (*toa_num == 0) + toa256 = 0; + else + toa256 = *toa256_sum / *toa_num; + + /* Note: RSSI is set to 0 to indicate to the higher + * layers that this is a faked tch_ind */ + _sched_compose_tch_ind(l1t, tn, fn, chan, tch_data, len, toa256, 10000, 0); + } } /* get frame and unlink from queue */ @@ -982,12 +992,6 @@ int rx_data_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, } else l2_len = GSM_MACBLOCK_LEN; - /* Send uplink measurement information to L2 */ - l1if_process_meas_res(l1t->trx, bi->tn, *first_fn, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, - *rssi_sum / *rssi_num, - *toa256_sum / *toa_num); lqual_cb = *ci_cb_num ? (*ci_cb_sum / *ci_cb_num) : 0; ber10k = compute_ber10k(n_bits_total, n_errors); return _sched_compose_ph_data_ind(l1t, bi->tn, *first_fn, @@ -1097,14 +1101,6 @@ int rx_pdtch_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, &n_errors, &n_bits_total); } - - /* Send uplink measurement information to L2 */ - l1if_process_meas_res(l1t->trx, bi->tn, *first_fn, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, - *rssi_sum / *rssi_num, - *toa256_sum / *toa_num); - if (rc <= 0) { LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn, "Received bad PDTCH (%u/%u)\n", @@ -1141,6 +1137,7 @@ int rx_tchf_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, struct gsm_lchan *lchan = get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | bi->tn); unsigned int fn_begin; + uint16_t ber10k; /* handle rach, if handover rach detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1240,17 +1237,13 @@ int rx_tchf_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, if (rc != GSM_MACBLOCK_LEN && lchan->ecu_state) osmo_ecu_frame_in(lchan->ecu_state, bfi_flag, tch_data, rc); + ber10k = compute_ber10k(n_bits_total, n_errors); if (bfi_flag) goto bfi; /* FACCH */ if (rc == GSM_MACBLOCK_LEN) { - uint16_t ber10k = compute_ber10k(n_bits_total, n_errors); fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_F); - l1if_process_meas_res(l1t->trx, bi->tn, fn_begin, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, - bi->rssi, bi->toa256); _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan, tch_data + amr, GSM_MACBLOCK_LEN, /* FIXME: AVG RSSI and ToA256 */ @@ -1311,12 +1304,8 @@ bfi: /* TCH or BFI */ compose_l1sap: fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_F); - l1if_process_meas_res(l1t->trx, bi->tn, fn_begin, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, - bi->rssi, bi->toa256); return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan, - tch_data, rc); + tch_data, rc, bi->toa256, ber10k, bi->rssi); } /*! \brief a single TCH/H burst was received by the PHY, process it */ @@ -1343,6 +1332,7 @@ int rx_tchh_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, */ int fn_is_odd = (((bi->fn + 26 - 10) % 26) >> 2) & 1; unsigned int fn_begin; + uint16_t ber10k; /* handle RACH, if handover RACH detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1385,6 +1375,8 @@ int rx_tchh_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, } *mask = 0x0; + ber10k = compute_ber10k(n_bits_total, n_errors); + /* skip second of two TCH frames of FACCH was received */ if (chan_state->ul_ongoing_facch) { chan_state->ul_ongoing_facch = 0; @@ -1464,10 +1456,6 @@ int rx_tchh_fn(struct l1sched_trx *l1t, enum trx_chan_type chan, fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H0); else fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H1); - l1if_process_meas_res(l1t->trx, bi->tn, fn_begin, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, bi->rssi, - bi->toa256); _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan, tch_data + amr, GSM_MACBLOCK_LEN, /* FIXME: AVG both RSSI and ToA */ @@ -1533,12 +1521,8 @@ compose_l1sap: fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H0); else fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H1); - l1if_process_meas_res(l1t->trx, bi->tn, fn_begin, - trx_chan_desc[chan].chan_nr | bi->tn, - n_errors, n_bits_total, bi->rssi, - bi->toa256); return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan, - tch_data, rc); + tch_data, rc, bi->toa256, ber10k, bi->rssi); } /* schedule all frames of all TRX for given FN */