From e225f231860789dae22226cb4df04fe1b65a371c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 9 Dec 2013 09:12:57 +0100 Subject: [WIP] HO: Add power measurement of neighbor cells to TCH channels The task will measure power between TX and RX of successive TCH frames. Change-Id: I0c5a1714249799ff0f2031ca0585ec051c61cc6b --- src/target/firmware/include/layer1/prim.h | 3 +- src/target/firmware/include/layer1/sync.h | 19 ++-- src/target/firmware/layer1/l23_api.c | 28 +++-- src/target/firmware/layer1/mframe_sched.c | 35 ++++-- src/target/firmware/layer1/prim_pm.c | 177 ++++++++++++++++++++++++++---- 5 files changed, 212 insertions(+), 50 deletions(-) diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h index 30c51ae6..bff51deb 100644 --- a/src/target/firmware/include/layer1/prim.h +++ b/src/target/firmware/include/layer1/prim.h @@ -29,6 +29,7 @@ extern const struct tdma_sched_item nb_sched_set_ul[]; extern const struct tdma_sched_item tch_sched_set[]; extern const struct tdma_sched_item tch_a_sched_set[]; extern const struct tdma_sched_item tch_d_sched_set[]; -extern const struct tdma_sched_item neigh_pm_sched_set[]; +extern const struct tdma_sched_item neigh_pm_idle_sched_set[]; +extern const struct tdma_sched_item neigh_pm_tch_sched_set[]; #endif /* _L1_PRIM_H */ diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index 7ce87566..af743195 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -129,6 +129,7 @@ struct l1s_state { GSM_DCHAN_UNKNOWN, } type; + uint8_t chan_nr; uint8_t scn; uint8_t tsc; uint8_t tn; @@ -149,14 +150,18 @@ struct l1s_state { }; } dedicated; - /* neighbour cell power measurement process */ + /* neighbor cell power measurement process */ struct { - uint8_t n, second; - uint8_t pos; - uint8_t running; - uint16_t band_arfcn[64]; - uint8_t tn[64]; - uint8_t level[64]; + uint32_t start_fn; /* frame number of measumrement start */ + uint8_t valid; /* we have a complete set of measurements */ + uint8_t rounds; /* current rounds of complete measurements */ + uint8_t pos; /* current neighbor to measure */ + uint8_t running; /* DSP task running */ + uint8_t n; /* number of neighbors to measure */ + uint16_t band_arfcn[64]; /* list of ARFCNs */ + uint8_t tn[64]; /* list of TS offset for each measurement */ + uint16_t level_sum[64]; /* sum while processing rounds */ + uint8_t level[64]; /* latest results */ } neigh_pm; }; diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index c7849aeb..e487864e 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -69,8 +69,8 @@ void l1_queue_for_l2(struct msgb *msg) enum mf_type { MFNONE, MF51, - MF26ODD, - MF26EVEN + MF26EVEN, + MF26ODD }; static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr, uint8_t neigh_mode) { @@ -245,11 +245,12 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) mframe_disable(MF_TASK_NEIGH_PM51_C0T0); /* configure dedicated channel state */ - l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); - l1s.dedicated.tsc = est_req->tsc; + l1s.dedicated.chan_nr = ul->chan_nr; + l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); + l1s.dedicated.tsc = est_req->tsc; old_tn = l1s.dedicated.tn; - l1s.dedicated.tn = ul->chan_nr & 0x7; - l1s.dedicated.h = est_req->h; + l1s.dedicated.tn = ul->chan_nr & 0x7; + l1s.dedicated.h = est_req->h; if (est_req->h) { int i; @@ -276,6 +277,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) } /* figure out which MF tasks to enable */ + l1s.neigh_pm.n = 0; l1a_mftask_set(chan_nr2mf_task_mask(ul->chan_nr, NEIGH_MODE_PM)); /* shift TPU according to chnage in TN */ @@ -551,20 +553,30 @@ static void l1ctl_rx_neigh_pm_req(struct msgb *msg) /* reset list in order to prevent race condition */ l1s.neigh_pm.n = 0; /* atomic */ - l1s.neigh_pm.second = 0; /* now reset pointer and fill list */ l1s.neigh_pm.pos = 0; + l1s.neigh_pm.valid = 0; + l1s.neigh_pm.rounds = 0; l1s.neigh_pm.running = 0; + if (pm_req->n > 64) + pm_req->n = 64; for (i = 0; i < pm_req->n; i++) { l1s.neigh_pm.band_arfcn[i] = ntohs(pm_req->band_arfcn[i]); l1s.neigh_pm.tn[i] = pm_req->tn[i]; + l1s.neigh_pm.level[i] = 0; } printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req->n); l1s.neigh_pm.n = pm_req->n; /* atomic */ - /* on C0 enable PM on frame 51 */ + /* + * IDLE: on C0 enable PM on frame 51 + * DEDICATED: add neighbor cell task + */ if (l1s.dedicated.type == GSM_DCHAN_NONE) mframe_enable(MF_TASK_NEIGH_PM51_C0T0); + else + l1a_mftask_set(chan_nr2mf_task_mask(l1s.dedicated.chan_nr, + NEIGH_MODE_PM)); } /* receive a L1CTL_TRAFFIC_REQ from L23 */ diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 089ca8ed..b242f990 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -50,7 +50,8 @@ struct mframe_sched_item { #define NB_QUAD_FH_DL NB_QUAD_DL #define NB_QUAD_UL nb_sched_set_ul #define NB_QUAD_FH_UL NB_QUAD_UL -#define NEIGH_PM neigh_pm_sched_set +#define NEIGH_PM_IDLE neigh_pm_idle_sched_set +#define NEIGH_PM_TCH neigh_pm_tch_sched_set /* BCCH Normal */ static const struct mframe_sched_item mf_bcch_norm[] = { @@ -210,17 +211,17 @@ static const struct mframe_sched_item mf_sdcch4_cbch[] = { /* Measurement for MF 51 C0 */ static const struct mframe_sched_item mf_neigh_pm51_c0t0[] = { - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 0 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 10 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 20 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 30 }, - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 40 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 10 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 20 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 30 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 40 }, { .sched_set = NULL } }; /* Measurement for MF 51 */ static const struct mframe_sched_item mf_neigh_pm51[] = { - { .sched_set = NEIGH_PM , .modulo = 51, .frame_nr = 50 }, + { .sched_set = NEIGH_PM_IDLE, .modulo = 51, .frame_nr = 50 }, { .sched_set = NULL } }; @@ -301,13 +302,27 @@ static const struct mframe_sched_item mf_tch_h_1[] = { { .sched_set = NULL } }; -/* Measurement for MF 26 */ +/* + * Measurement for MF 26 + * Note: PM will be read 2 frames, later, so we can only measure every second + * frame. + */ static const struct mframe_sched_item mf_neigh_pm26_even[] = { - { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 25 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 10 }, { .sched_set = NULL } }; static const struct mframe_sched_item mf_neigh_pm26_odd[] = { - { .sched_set = NEIGH_PM , .modulo = 26, .frame_nr = 12 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 0 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 2 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 4 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 6 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 8 }, + { .sched_set = NEIGH_PM_TCH, .modulo = 13, .frame_nr = 10 }, { .sched_set = NULL } }; diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c index 5c8c4914..a4b8e638 100644 --- a/src/target/firmware/layer1/prim_pm.c +++ b/src/target/firmware/layer1/prim_pm.c @@ -156,11 +156,39 @@ void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) } /* - * perform measurements of neighbour cells + * perform measurements of neighbour cells on idle frame */ +/* send measurement results */ +static void neigh_pm_ind(void) +{ + struct msgb *msg; + struct l1ctl_neigh_pm_ind *mi; + int i; + uint8_t half_rounds = l1s.neigh_pm.rounds >> 1; + + /* return result */ + msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); + for (i = 0; i < l1s.neigh_pm.n; i++) { + if (msgb_tailroom(msg) < (int) sizeof(*mi)) { + l1_queue_for_l2(msg); + msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); + } + mi = (struct l1ctl_neigh_pm_ind *) + msgb_put(msg, sizeof(*mi)); + mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]); + mi->tn = l1s.neigh_pm.tn[i]; + l1s.neigh_pm.level[i] + = (l1s.neigh_pm.level_sum[i] + half_rounds) + / l1s.neigh_pm.rounds; + mi->pm[0] = l1s.neigh_pm.level[i]; + l1s.neigh_pm.level_sum[i] = 0; + } + l1_queue_for_l2(msg); +} + /* scheduler callback to issue a power measurement task to the DSP */ -static int l1s_neigh_pm_cmd(uint8_t num_meas, +static int l1s_neigh_pm_idle_cmd(uint8_t num_meas, __unused uint8_t p2, __unused uint16_t p3) { uint8_t last_gain = rffe_get_gain(); @@ -188,7 +216,7 @@ static int l1s_neigh_pm_cmd(uint8_t num_meas, } /* scheduler callback to read power measurement resposnse from the DSP */ -static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2, +static int l1s_neigh_pm_idle_resp(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) { uint16_t dbm; @@ -202,29 +230,129 @@ static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2, dbm = (uint16_t) ((dsp_api.db_r->a_pm[0] & 0xffff) >> 3); level = dbm2rxlev(agc_inp_dbm8_by_pm(dbm)/8); - l1s.neigh_pm.level[l1s.neigh_pm.pos] = level; + l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] += level; if (++l1s.neigh_pm.pos >= l1s.neigh_pm.n) { - struct msgb *msg; - struct l1ctl_neigh_pm_ind *mi; - int i; + l1s.neigh_pm.pos = 0; + l1s.neigh_pm.rounds++; + neigh_pm_ind(); + l1s.neigh_pm.rounds = 0; + l1s.neigh_pm.valid = 1; + } + +out: + l1s.neigh_pm.running = 0; + + return 0; +} + +const struct tdma_sched_item neigh_pm_idle_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_pm_idle_cmd, -1, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_pm_idle_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* + * Perform measurements of neighbour cells on TCH + * + * Only when number of neighbor cells is > 0, perform measurement. + * + * For each measurement, l1s.neigh_pm.running is set. In case of an update + * of neighbor cell list, this state is cleared, so a pending measurement would + * be ignored. + * + * The measuement starts at position 0 (first neighbor cell). After each + * measurement result, the position is incremented until the number of neighbor + * cells are reached. + * + * All measurement results are added to level_sum array. It will be used to + * calculate an average from multiple measurements. + * + * All measurements start at round 0. When all neighbors have been measured, + * the number of rounds is increased and the measurement start over. At start + * of round 0, the start_fn is recorded. It will be used to calculate the + * elapsed time from the beginning. + * + * After reach round, the number of elapsed frames is checked. If at least 104 + * frames have been elapsed, this would be the last round. The average of + * measurements are calculated from the level_sum array. Then the result is + * indicated to layer, the measurement states are reset, the measurments are + * maked as valid, and the measurement process starts over. + */ + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_neigh_pm_tch_cmd(uint8_t num_meas, + __unused uint8_t p2, __unused uint16_t p3) +{ + uint8_t last_gain; + + if (l1s.neigh_pm.n == 0) + return 0; + + /* set start_fn for the first round */ + if (l1s.neigh_pm.pos == 0 && l1s.neigh_pm.rounds == 0) + l1s.neigh_pm.start_fn = l1s.next_time.fn; + + /* save current gain */ + last_gain = rffe_get_gain(); + + dsp_api.db_w->d_task_md = num_meas; /* number of measurements */ +// dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + + /* Tell the RF frontend to set the gain appropriately (keep last) */ + rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL); + + + /* + * Program TPU + * Use TS 5 (two TS after TX) + */ + l1s_rx_win_ctrl((l1s.neigh_pm.n) ? + l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] : 0, + L1_RXWIN_PW, 5); + + /* restore last gain */ + rffe_set_gain(last_gain); + + l1s.neigh_pm.running = 1; + + return 0; +} +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_neigh_pm_tch_resp(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + uint16_t dbm; + uint8_t level; + + dsp_api.r_page_used = 1; + + if (l1s.neigh_pm.n == 0 || !l1s.neigh_pm.running) + goto out; + + dbm = (uint16_t) ((dsp_api.db_r->a_pm[0] & 0xffff) >> 3); + level = dbm2rxlev(agc_inp_dbm8_by_pm(dbm)/8); + + l1s.neigh_pm.level_sum[l1s.neigh_pm.pos] += level; + + + if (++l1s.neigh_pm.pos >= l1s.neigh_pm.n) { + uint32_t elapsed = (l1s.next_time.fn + 2715648 + - l1s.neigh_pm.start_fn) % 2715648; l1s.neigh_pm.pos = 0; - /* return result */ - msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); - for (i = 0; i < l1s.neigh_pm.n; i++) { - if (msgb_tailroom(msg) < (int) sizeof(*mi)) { - l1_queue_for_l2(msg); - msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND); - } - mi = (struct l1ctl_neigh_pm_ind *) - msgb_put(msg, sizeof(*mi)); - mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]); - mi->tn = l1s.neigh_pm.tn[i]; - mi->pm[0] = l1s.neigh_pm.level[i]; - mi->pm[1] = 0; + l1s.neigh_pm.rounds++; + /* + * We want at least 104 frames before indicating the + * measurement(s). Add two, since the measurement was + * started two frames ago. + */ + if (elapsed >= 104 + 2) { + neigh_pm_ind(); + l1s.neigh_pm.rounds = 0; + l1s.neigh_pm.valid = 1; } - l1_queue_for_l2(msg); } out: @@ -233,10 +361,11 @@ out: return 0; } -const struct tdma_sched_item neigh_pm_sched_set[] = { - SCHED_ITEM_DT(l1s_neigh_pm_cmd, 0, 1, 0), SCHED_END_FRAME(), +/* NOTE: Prio 1 is below TCH's TX+RX prio 0 */ +const struct tdma_sched_item neigh_pm_tch_sched_set[] = { + SCHED_ITEM_DT(l1s_neigh_pm_tch_cmd, 1, 1, 0), SCHED_END_FRAME(), SCHED_END_FRAME(), - SCHED_ITEM(l1s_neigh_pm_resp, -4, 1, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_neigh_pm_tch_resp, -4, 1, 0), SCHED_END_FRAME(), SCHED_END_SET() }; -- cgit v1.2.3