summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2013-12-09 11:44:40 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2016-09-25 08:11:42 +0200
commit1466d42251c79a59ee96abb62810c9d226750ee5 (patch)
tree190e4b544eab8ec598e429a514b3f1772c96bd94
parent6683d8df89ae37422e6afd21e8243a378fc5d684 (diff)
[WIP] HO: Add SCH read of neighbor cells to TCH channels
The task will try to sync to neighbor cell during search (idle) frame.
-rw-r--r--src/target/firmware/include/layer1/prim.h1
-rw-r--r--src/target/firmware/include/layer1/sync.h11
-rw-r--r--src/target/firmware/include/layer1/tpu_window.h1
-rw-r--r--src/target/firmware/layer1/l23_api.c2
-rw-r--r--src/target/firmware/layer1/mframe_sched.c3
-rw-r--r--src/target/firmware/layer1/prim_fbsb.c159
-rw-r--r--src/target/firmware/layer1/prim_pm.c4
-rw-r--r--src/target/firmware/layer1/tpu_window.c2
8 files changed, 183 insertions, 0 deletions
diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h
index bff51de..652a914 100644
--- a/src/target/firmware/include/layer1/prim.h
+++ b/src/target/firmware/include/layer1/prim.h
@@ -31,5 +31,6 @@ 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_idle_sched_set[];
extern const struct tdma_sched_item neigh_pm_tch_sched_set[];
+extern const struct tdma_sched_item neigh_sync_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 0b6895f..40cc3aa 100644
--- a/src/target/firmware/include/layer1/sync.h
+++ b/src/target/firmware/include/layer1/sync.h
@@ -159,8 +159,19 @@ struct l1s_state {
uint16_t level_sum[64]; /* sum while processing rounds */
uint8_t level[64]; /* latest results */
} neigh_pm;
+
+ /* neighbor cell SCH sync process */
+ struct {
+ uint8_t flags_bsic[64]; /* flags + bsic */
+ uint8_t count; /* counter for sync process */
+ uint8_t index; /* cell of current sync process (0..63) */
+ uint8_t running; /* DSP task running */
+ } neigh_sb;
};
+#define NEIGH_PM_FLAG_SCANNED 0x80
+#define NEIGH_PM_FLAG_BSIC 0x40
+
extern struct l1s_state l1s;
struct l1s_meas_hdr {
diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h
index 7b146f1..f460ef0 100644
--- a/src/target/firmware/include/layer1/tpu_window.h
+++ b/src/target/firmware/include/layer1/tpu_window.h
@@ -6,6 +6,7 @@ enum l1_rxwin_type {
L1_RXWIN_FB, /* FCCH burst detection */
L1_RXWIN_SB, /* SCH burst detection */
L1_RXWIN_NB, /* Normal burst decoding */
+ L1_RXWIN_SB26, /* SCH burst detection of neighbour cell */
_NUM_L1_RXWIN
};
diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c
index 860aed8..737670d 100644
--- a/src/target/firmware/layer1/l23_api.c
+++ b/src/target/firmware/layer1/l23_api.c
@@ -551,7 +551,9 @@ static void l1ctl_rx_neigh_pm_req(struct msgb *msg)
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;
+ l1s.neigh_sb.flags_bsic[i] = 0;
}
+ l1s.neigh_sb.count = 0;
printf("L1CTL_NEIGH_PM_REQ new list with %u entries\n", pm_req->n);
l1s.neigh_pm.n = pm_req->n; /* atomic */
diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c
index b1df723..78934cf 100644
--- a/src/target/firmware/layer1/mframe_sched.c
+++ b/src/target/firmware/layer1/mframe_sched.c
@@ -52,6 +52,7 @@ struct mframe_sched_item {
#define NB_QUAD_FH_UL NB_QUAD_UL
#define NEIGH_PM_IDLE neigh_pm_idle_sched_set
#define NEIGH_PM_TCH neigh_pm_tch_sched_set
+#define NEIGH_SYNC neigh_sync_sched_set
/* BCCH Normal */
static const struct mframe_sched_item mf_bcch_norm[] = {
@@ -304,6 +305,7 @@ static const struct mframe_sched_item mf_neigh_pm26_even[] = {
{ .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 = NEIGH_SYNC, .modulo = 26, .frame_nr = 24 },
{ .sched_set = NULL }
};
static const struct mframe_sched_item mf_neigh_pm26_odd[] = {
@@ -313,6 +315,7 @@ static const struct mframe_sched_item mf_neigh_pm26_odd[] = {
{ .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 = NEIGH_SYNC, .modulo = 26, .frame_nr = 11 },
{ .sched_set = NULL }
};
diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c
index 9eb56c6..bb0cdbc 100644
--- a/src/target/firmware/layer1/prim_fbsb.c
+++ b/src/target/firmware/layer1/prim_fbsb.c
@@ -567,6 +567,165 @@ void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req)
}
+
+/* SB for Neighbours in dedicated mode
+ *
+ * Only when number of neighbor cells is > 0, perform synchronization.
+ *
+ * For each synchronization, l1s.neigh_pm.running is set. In case of an update
+ * of neighbor cell list, this state is cleared, so a pending sync result would
+ * be ignored.
+ *
+ * After a (new) list of neighbor cells are received, the measurements are not
+ * yet valid. A valid state flag is used to indicate valid measurements. Until
+ * there are no valid measurements, the synchronization is not performed.
+ *
+ * The task is to scan the 6 strongest neighbor cells by trying to synchronize
+ * to it. This is done by selecting the strongest unscanned neighbor cell.
+ * If 6 cells have been scanned or all cells (if less than 6) have been
+ * scanned, the process clears all 'scanned' flags and starts over with the
+ * strongest (now the strongest unscanned) cell.
+ *
+ * Each synchronization attempt is performed during the "search frame" (IDLE
+ * frame). The process attempts to sync 11 times to ensure that it hits the
+ * SCH of the neighbor's BCCH. (The alignment of BCCH shifts after every two
+ * 26-multiframe in a way that the "search frame" is aligned with the SCH, at
+ * least once for 11 successive "search frames".)
+ *
+ * If the synchronization attempt is successful, the BSIC and neighbor cell
+ * offset is stored. These are indicated to layer23 with the measurement
+ * results.
+ *
+ * When performing handover to a neighbor cell, the stored offset is used to
+ * calculate new GSM time and tpu_offset.
+ */
+
+static void select_neigh_cell(void)
+{
+ uint8_t strongest = 0, strongest_unscanned = 0;
+ int strongest_i = 0, strongest_unscanned_i = -1;
+ int num_scanned = 0;
+ int i;
+
+ /* find strongest cell and strongest unscanned cell and count */
+ for (i = 0; i < l1s.neigh_pm.n; i++) {
+ if (l1s.neigh_pm.level[i] > strongest) {
+ strongest = l1s.neigh_pm.level[i];
+ strongest_i = i;
+ }
+ if (!(l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_SCANNED)) {
+ if (l1s.neigh_pm.level[i] > strongest_unscanned) {
+ strongest_unscanned = l1s.neigh_pm.level[i];
+ strongest_unscanned_i = i;
+ }
+ } else
+ num_scanned++;
+ }
+
+ /* no unscanned cell or we have scanned enough */
+ if (strongest_unscanned_i < 0 || num_scanned >= 6) {
+ /* flag all cells unscanned */
+ for (i = 0; i < l1s.neigh_pm.n; i++)
+ l1s.neigh_sb.flags_bsic[i] &= ~NEIGH_PM_FLAG_SCANNED;
+ /* use strongest cell to begin scanning with */
+ l1s.neigh_sb.index = strongest_i;
+ } else {
+ /* use strongest unscanned cell to begin scanning with */
+ l1s.neigh_sb.index = strongest_unscanned_i;
+ }
+}
+
+static int l1s_neigh_sb_cmd(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ int index = l1s.neigh_sb.index;
+ uint8_t last_gain;
+
+ if (l1s.neigh_pm.n == 0)
+ return 0;
+
+ /* if measurements are not yet valid, wait */
+ if (!l1s.neigh_pm.valid)
+ return 0;
+
+ /* check for cell to sync to */
+ if (l1s.neigh_sb.count == 0) {
+ /* there is no cell selected, search for cell */
+ select_neigh_cell();
+ }
+
+ printf("detect SB arfcn %d (#%d) %d dbm\n", l1s.neigh_pm.band_arfcn[index], l1s.neigh_sb.count, rxlev2dbm(l1s.neigh_pm.level[index]));
+
+ last_gain = rffe_get_gain();
+
+ /* Tell the RF frontend to set the gain appropriately */
+ rffe_compute_gain(rxlev2dbm(l1s.neigh_pm.level[index]), CAL_DSP_TGT_BB_LVL);
+
+ /* Program DSP */
+ dsp_api.db_w->d_task_md = TCH_SB_DSP_TASK; /* maybe with I/Q swap? */
+// dsp_api.db_w->d_task_md = dsp_task_iq_swap(TCH_SB_DSP_TASK, l1s.neigh_pm.band_arfcn[index], 0);
+ dsp_api.ndb->d_fb_mode = 0;
+
+ /* Program TPU */
+ l1s_rx_win_ctrl(l1s.neigh_pm.band_arfcn[index], L1_RXWIN_SB26, 5);
+
+ /* restore last gain */
+ rffe_set_gain(last_gain);
+
+ l1s.neigh_sb.running = 1;
+
+ return 0;
+}
+
+static int l1s_neigh_sb_resp(__unused uint8_t p1, __unused uint8_t p2,
+ __unused uint16_t p3)
+{
+ int index = l1s.neigh_sb.index;
+ uint32_t sb;
+
+ if (l1s.neigh_pm.n == 0 || !l1s.neigh_sb.running)
+ goto out;
+
+ /* check if sync was successful */
+ if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) {
+ printf("SB error arfcn %d\n", l1s.neigh_pm.band_arfcn[index]);
+
+ /* next sync */
+ if (++l1s.neigh_sb.count == 11) {
+ l1s.neigh_sb.count = 0;
+ l1s.neigh_sb.flags_bsic[index] |= NEIGH_PM_FLAG_SCANNED;
+ }
+ } else {
+ l1s.neigh_sb.count = 0;
+
+ read_sb_result(last_fb, 1);
+ sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16;
+ l1s.neigh_sb.flags_bsic[index] =
+ l1s_decode_sb(&fbs.mon.time, sb)
+ | NEIGH_PM_FLAG_BSIC | NEIGH_PM_FLAG_SCANNED;
+ printf("SB OK!!!!!! arfcn %d\n", l1s.neigh_pm.band_arfcn[index]);
+
+ /* store time offset */
+ }
+
+out:
+ l1s.neigh_sb.running = 0;
+
+ dsp_api.r_page_used = 1;
+
+ return 0;
+
+}
+
+/* NOTE: Prio 1 is below TCH's RX+TX prio 0 */
+const struct tdma_sched_item neigh_sync_sched_set[] = {
+ SCHED_ITEM_DT(l1s_neigh_sb_cmd, 1, 0, 1), SCHED_END_FRAME(),
+ SCHED_END_FRAME(),
+ SCHED_ITEM(l1s_neigh_sb_resp, -4, 0, 1), SCHED_END_FRAME(),
+ SCHED_END_SET()
+};
+
+
static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void)
{
l1s.completion[L1_COMPL_FB] = &l1a_fb_compl;
diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c
index 3edcb10..71e0493 100644
--- a/src/target/firmware/layer1/prim_pm.c
+++ b/src/target/firmware/layer1/prim_pm.c
@@ -182,6 +182,10 @@ static void neigh_pm_ind(void)
/ l1s.neigh_pm.rounds;
mi->pm[0] = l1s.neigh_pm.level[i];
l1s.neigh_pm.level_sum[i] = 0;
+ if ((l1s.neigh_sb.flags_bsic[i] & NEIGH_PM_FLAG_BSIC))
+ mi->bsic = l1s.neigh_sb.flags_bsic[i] & 0x3f;
+ else
+ mi->bsic = 255;
}
l1_queue_for_l2(msg);
}
diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c
index 23d3244..9ba914b 100644
--- a/src/target/firmware/layer1/tpu_window.c
+++ b/src/target/firmware/layer1/tpu_window.c
@@ -48,6 +48,7 @@
#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q)
#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */
#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798)
+#define L1_SB26_DURATION_Q (L1_TDMA_LENGTH_Q + 984) // 984
#define L1_PW_DURATION_Q 289
#define DSP_SETUP_TIME 66
@@ -57,6 +58,7 @@ static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = {
[L1_RXWIN_FB] = L1_FB_DURATION_Q,
[L1_RXWIN_SB] = L1_SB_DURATION_Q,
[L1_RXWIN_NB] = L1_NB_DURATION_Q,
+ [L1_RXWIN_SB26] = L1_SB26_DURATION_Q,
};
#define L1_TX_NB_DURATION_Q 626