diff options
-rw-r--r-- | include/osmo-bts/Makefile.am | 3 | ||||
-rw-r--r-- | include/osmo-bts/measurement.h | 13 | ||||
-rw-r--r-- | src/common/Makefile.am | 3 | ||||
-rw-r--r-- | src/common/logging.c | 4 | ||||
-rw-r--r-- | src/common/measurement.c | 235 | ||||
-rw-r--r-- | src/common/rsl.c | 101 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.c | 35 |
7 files changed, 377 insertions, 17 deletions
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am index 64711f86..4f8753dd 100644 --- a/include/osmo-bts/Makefile.am +++ b/include/osmo-bts/Makefile.am @@ -1 +1,2 @@ -noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h oml.h paging.h rsl.h rtp.h signal.h vty.h +noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \ + oml.h paging.h rsl.h rtp.h signal.h vty.h diff --git a/include/osmo-bts/measurement.h b/include/osmo-bts/measurement.h new file mode 100644 index 00000000..5ad6795e --- /dev/null +++ b/include/osmo-bts/measurement.h @@ -0,0 +1,13 @@ +#ifndef OSMO_BTS_MEAS_H +#define OSMO_BTS_MEAS_H + +int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm); + +int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn); +int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn); +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); + +#endif diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 70b94957..c8530c40 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -3,5 +3,6 @@ AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) noinst_LIBRARIES = libbts.a -libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c rsl.c vty.c paging.c +libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \ + rsl.c vty.c paging.c measurement.c #support.c rtp.c diff --git a/src/common/logging.c b/src/common/logging.c index 362e30cd..a7df2282 100644 --- a/src/common/logging.c +++ b/src/common/logging.c @@ -58,7 +58,7 @@ static const struct log_info_cat bts_log_info_cat[] = { [DMEAS] = { .name = "DMEAS", .description = "Radio Measurement Processing", - .enabled = 0, .loglevel = LOGL_NOTICE, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DPAG] = { .name = "DPAG", @@ -107,7 +107,7 @@ const struct log_info bts_log_info = { .num_cat = ARRAY_SIZE(bts_log_info_cat), }; -#define DEFAULT_MASK "DL1C:DOML:DRSL:DPAG" +#define DEFAULT_MASK "DL1C:DOML:DRSL:DPAG:DMEAS" int bts_log_init(const char *category_mask) { diff --git a/src/common/measurement.c b/src/common/measurement.c new file mode 100644 index 00000000..c20db46f --- /dev/null +++ b/src/common/measurement.c @@ -0,0 +1,235 @@ + +#include <stdint.h> +#include <errno.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmo-bts/gsm_data.h> +#include <osmo-bts/logging.h> +#include <osmo-bts/measurement.h> + +/* TS 05.08, Chapter 8.4.1 */ +/* measurement period ends at fn % 104 == ? */ +static const uint8_t tchf_meas_rep_fn104[] = { + [0] = 103, + [1] = 12, + [2] = 25, + [3] = 38, + [4] = 51, + [5] = 64, + [6] = 77, + [7] = 90, +}; +static const uint8_t tchh0_meas_rep_fn104[] = { + [0] = 103, + [1] = 103, + [2] = 25, + [3] = 25, + [4] = 51, + [5] = 51, + [6] = 77, + [7] = 77, +}; +static const uint8_t tchh1_meas_rep_fn104[] = { + [0] = 12, + [1] = 12, + [2] = 38, + [3] = 38, + [4] = 64, + [5] = 64, + [6] = 90, + [7] = 90, +}; + +/* determine if a measurement period ends at the given frame number */ +static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts, + unsigned int subch, uint32_t fn) +{ + unsigned int fn_mod; + const uint8_t *tbl; + int rc = 0; + + if (ts >= 8) + return -EINVAL; + if (pchan >= _GSM_PCHAN_MAX) + return -EINVAL; + + switch (pchan) { + case GSM_PCHAN_TCH_F: + fn_mod = fn % 104; + if (tchf_meas_rep_fn104[ts] == fn_mod) + rc = 1; + break; + case GSM_PCHAN_TCH_H: + fn_mod = fn % 104; + if (subch == 0) + tbl = tchh0_meas_rep_fn104; + else + tbl = tchh1_meas_rep_fn104; + if (tbl[ts] == fn_mod) + rc = 1; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + fn_mod = fn % 102; + if (fn_mod == 11) + rc = 1; + break; + case GSM_PCHAN_CCCH_SDCCH4: + fn_mod = fn % 102; + if (fn_mod == 36) + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +/* receive a L1 uplink measurement from L1 */ +int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm) +{ + DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n", + gsm_lchan_name(lchan), lchan->meas.num_ul_meas); + + if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) { + LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n", + gsm_lchan_name(lchan)); + return -ENOSPC; + } + + memcpy(&lchan->meas.uplink[lchan->meas.num_ul_meas++], ulm, + sizeof(*ulm)); + + return 0; +} + +/* input: BER in steps of .01%, i.e. percent/100 */ +static uint8_t ber10k_to_rxqual(uint32_t ber10k) +{ + /* 05.08 / 8.2.4 */ + if (ber10k < 20) + return 0; + if (ber10k < 40) + return 1; + if (ber10k < 80) + return 2; + if (ber10k < 160) + return 3; + if (ber10k < 320) + return 4; + if (ber10k < 640) + return 5; + if (ber10k < 1280) + return 6; + return 7; +} + +int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn) +{ + uint32_t ber_full_sum = 0; + uint32_t irssi_full_sum = 0; + uint32_t ber_sub_sum = 0; + uint32_t irssi_sub_sum = 0; + int32_t taqb_sum = 0; + unsigned int num_meas_sub = 0; + int i; + + /* if measurement period is not complete, abort */ + if (!is_meas_complete(lchan->ts->pchan, lchan->ts->nr, + lchan->nr, fn)) + return 0; + + /* if there are no measurements, skip computation */ + if (lchan->meas.num_ul_meas == 0) + return 0; + + /* compute the actual measurements */ + + /* step 1: add up */ + for (i = 0; i < lchan->meas.num_ul_meas; i++) { + struct bts_ul_meas *m = &lchan->meas.uplink[i]; + + ber_full_sum += m->ber10k; + irssi_full_sum += m->inv_rssi; + taqb_sum += m->ta_offs_qbits; + + if (m->is_sub) { + num_meas_sub++; + ber_sub_sum += m->ber10k; + irssi_sub_sum += m->inv_rssi; + } + } + + /* step 2: divide */ + ber_full_sum = ber_full_sum / lchan->meas.num_ul_meas; + irssi_full_sum = irssi_full_sum / lchan->meas.num_ul_meas; + taqb_sum = taqb_sum / lchan->meas.num_ul_meas; + ber_sub_sum = ber_sub_sum / num_meas_sub; + irssi_sub_sum = irssi_sub_sum / num_meas_sub; + + DEBUGP(DMEAS, "%s Computed TA(% 4uqb) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), " + "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)", gsm_lchan_name(lchan), + taqb_sum, ber_full_sum/100, + ber_full_sum%100, irssi_full_sum, ber_sub_sum/100, ber_sub_sum%100, + irssi_sub_sum); + + /* store results */ + lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1); + lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1); + lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum); + lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum); + + lchan->meas.flags |= LC_UL_M_F_RES_VALID; + lchan->meas.num_ul_meas = 0; + + /* send a signal indicating computation is complete */ + + return 1; +} + +/* build the 3 byte RSL uplinke measurement IE content */ +int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf) +{ + buf[0] = (lchan->meas.res.rxlev_full & 0x3f); /* FIXME: DTXu support */ + buf[1] = (lchan->meas.res.rxlev_sub & 0x3f); + buf[2] = ((lchan->meas.res.rxqual_full & 7) << 3) | + (lchan->meas.res.rxqual_sub & 7); + + return 3; +} + +int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ts->lchan); i++) { + struct gsm_lchan *lchan = &ts->lchan[i]; + + if (lchan->state != LCHAN_S_ACTIVE) + continue; + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + lchan_meas_check_compute(lchan, fn); + break; + default: + break; + } + } + return 0; +} + +/* needs to be called once every TDMA frame ! */ +int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + ts_meas_check_compute(ts, fn); + } + return 0; +} diff --git a/src/common/rsl.c b/src/common/rsl.c index e6ced269..c9885fde 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -39,6 +39,7 @@ #include <osmo-bts/oml.h> #include <osmo-bts/signal.h> #include <osmo-bts/bts_model.h> +#include <osmo-bts/measurement.h> static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause); @@ -251,7 +252,7 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s)\n", get_value_string(osmo_sitype_strs, osmo_si)); } else if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + uint16_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); if (len > sizeof(sysinfo_buf_t)) len = sizeof(sysinfo_buf_t); bts->si_valid |= (1 << osmo_si); @@ -359,7 +360,7 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT); } if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + uint16_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); /* We have to pre-fix with the two-byte LAPDM UI header */ if (len > sizeof(sysinfo_buf_t)-2) len = sizeof(sysinfo_buf_t)-2; @@ -437,6 +438,9 @@ int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime) rsl_dch_push_hdr(msg, RSL_MT_CHAN_ACTIV_ACK, chan_nr); msg->trx = lchan->ts->trx; + /* since activation was successful, do some lchan initialization */ + lchan->meas.res_nr = 0; + return abis_rsl_sendmsg(msg); } @@ -605,7 +609,7 @@ static int rsl_rx_chan_activ(struct msgb *msg) LOGP(DRSL, LOGL_INFO, " chan_nr=0x%02x type=0x%02x mode=0x%02x\n", dch->chan_nr, type, mode); /* actually activate the channel in the BTS */ - return bts_model_rsl_chan_act(msg->lchan, &tp); + return bts_model_rsl_chan_act(msg->lchan, &tp); } /* 8.4.14 RF CHANnel RELease is received */ @@ -651,7 +655,7 @@ static int rsl_rx_sacch_inf_mod(struct msgb *msg) return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); } if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - uint8_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); + uint16_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); /* We have to pre-fix with the two-byte LAPDM UI header */ if (len > sizeof(sysinfo_buf_t)-2) len = sizeof(sysinfo_buf_t)-2; @@ -851,18 +855,99 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg) return lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch); } +static inline int rsl_link_id_is_sacch(uint8_t link_id) +{ + if (link_id >> 6 == 1) + return 1; + else + return 0; +} + +static int rslms_is_meas_rep(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rh = msgb_l2(msg); + struct abis_rsl_rll_hdr *rllh; + struct gsm48_hdr *gh; + + if ((rh->msg_discr & 0xfe) != ABIS_RSL_MDISC_RLL) { + DEBUGP(DRSL, "msg_disc 0x%x != RLL\n", rh->msg_discr); + return 0; + } + + if (rh->msg_type != RSL_MT_UNIT_DATA_IND) { + DEBUGP(DRSL, "msg_type 0x%x != UNIT_DATA_IND\n", rh->msg_type); + return 0; + } +#if 0 + rllh = msgb_l2(msg); + if (rsl_link_id_is_sacch(rllh->link_id) == 0) + return 0; + gh = msgb_l3(msg); + if (gh->proto_discr != GSM48_PDISC_RR) + return 0; + + if (gh->msg_type != GSM48_MT_RR_MEAS_REP) + return 0; +#endif + return 1; +} + +/* 8.4.8 MEASUREMENT RESult */ +static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len) +{ + struct msgb *msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + + LOGP(DRSL, LOGL_NOTICE, "(%s) Tx MEAS RES\n", gsm_lchan_name(lchan)); + + msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++); + if (lchan->meas.flags & LC_UL_M_F_RES_VALID) { + uint8_t meas_res[16]; + int ie_len = lchan_build_rsl_ul_meas(lchan, meas_res); + if (ie_len >= 3) { + msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res); + lchan->meas.flags &= ~LC_UL_M_F_RES_VALID; + } + } + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->meas.bts_tx_pwr); + if (lchan->meas.flags & LC_UL_M_F_L1_VALID) { + msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, 2, lchan->meas.l1_info); + lchan->meas.flags &= ~LC_UL_M_F_L1_VALID; + } + msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3); + //msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, FIXME); + + rsl_dch_push_hdr(msg, RSL_MT_MEAS_RES, chan_nr); + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + /* 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); - 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)); - msg->trx = lchan->ts->trx; - return abis_rsl_sendmsg(msg); + /* 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", + gsm_lchan_name(lchan), rsl_msg_name(rh->msg_type)); + + 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_rsl_sendmsg(msg); + } } static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg) diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c index c01919fe..fd09ed9b 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -39,6 +39,7 @@ #include <osmo-bts/bts.h> #include <osmo-bts/gsm_data.h> #include <osmo-bts/paging.h> +#include <osmo-bts/measurement.h> #include <sysmocom/femtobts/femtobts.h> #include <sysmocom/femtobts/gsml1prim.h> @@ -326,6 +327,10 @@ static int handle_mph_time_ind(struct femtol1_hdl *fl1, /* Update our data structures with the current GSM time */ gsm_fn2gsmtime(&fl1->gsm_time, time_ind->u32Fn); + /* check if the measurement period of some lchan has ended + * and pre-compute the respective measurement */ + trx_meas_check_compute(fl1->priv, time_ind->u32Fn -1); + return 0; } @@ -357,13 +362,31 @@ static void dump_meas_res(GsmL1_MeasParam_t *m) m->fBer, m->i16BurstTiming); } +static int process_meas_res(struct gsm_lchan *lchan, GsmL1_MeasParam_t *m) +{ + struct bts_ul_meas ulm; + + ulm.ta_offs_qbits = m->i16BurstTiming; + ulm.ber10k = (unsigned int) (m->fBer * 100); + ulm.inv_rssi = (uint8_t) (m->fRssi * -1); + + return lchan_new_ul_meas(lchan, &ulm); +} + static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind) { struct osmo_phsap_prim pp; struct gsm_lchan *lchan; struct lapdm_entity *le; struct msgb *msg; - uint8_t lapdm_sapi = 0; + + lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2); + if (!lchan) { + LOGP(DL1C, LOGL_ERROR, "unable to resolve lchan by hLayer2\n"); + return -ENODEV; + } + + process_meas_res(lchan, &data_ind->measParam); if (data_ind->measParam.fLinkQuality < MIN_QUAL_NORM) return 0; @@ -371,10 +394,12 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i DEBUGP(DL1C, "Rx PH-DATA.ind (hLayer2 = 0x%08x)", data_ind->hLayer2); dump_meas_res(&data_ind->measParam); - lchan = l1if_hLayer2_to_lchan(fl1->priv, data_ind->hLayer2); - if (!lchan) { - LOGP(DL1C, LOGL_ERROR, "unable to resolve lchan by hLayer2\n"); - return -ENODEV; + /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */ + if (data_ind->sapi == GsmL1_Sapi_Sacch && + data_ind->msgUnitParam.u8Size >= 2) { + lchan->meas.l1_info[0] = data_ind->msgUnitParam.u8Buffer[0]; + lchan->meas.l1_info[1] = data_ind->msgUnitParam.u8Buffer[1]; + lchan->meas.flags |= LC_UL_M_F_L1_VALID; } le = le_by_l1_sapi(&lchan->lapdm_ch, data_ind->sapi); |