Change in osmo-bts[master]: common/scheduler.c: track TDMA frame loss per logical channels

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Thu Oct 25 17:51:33 UTC 2018


Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/10309 )

Change subject: common/scheduler.c: track TDMA frame loss per logical channels
......................................................................

common/scheduler.c: track TDMA frame loss per logical channels

This change modifies the logic of TDMA frame loss tracking. To
be more precise, the tracking logic was moved from per timeslot
level to per logical channel level, what makes OsmoBTS more
accurate in its measurements.

But before getting into details, it's important to clarify some
things about the Uplink burst processing in transceiver (OsmoTRX).
If an Uplink burst is detected, OsmoTRX demodulates it and sends
to OsmoBTS. If nothing is detected on a particular timeslot,
OsmoTRX will do nothing. In other words, it will not
notify OsmoBTS about this.

Meanwhile, there are usually a few logical channels mapped to a
single TDMA timeslot. Let's use SDCCH8 channel configuration as
an example (simplified layout):

  /* SDCCH/8 (ss=0), subscriber A (active) */
  { TRXC_SDCCH8_0,    bid=0 },
  { TRXC_SDCCH8_0,    bid=1 },
  { TRXC_SDCCH8_0,    bid=2 },
  { TRXC_SDCCH8_0,    bid=3 }, // <-- last_fn=X

  /* SDCCH/8 (ss=1), subscriber B (inactive) */
  { TRXC_SDCCH8_1,    bid=0 },
  { TRXC_SDCCH8_1,    bid=1 },
  { TRXC_SDCCH8_1,    bid=2 },
  { TRXC_SDCCH8_1,    bid=3 },

  /* SDCCH/8 (ss=2), subscriber C (active) */
  { TRXC_SDCCH8_2,    bid=0 }, // <-- current_fn=X+5
  { TRXC_SDCCH8_2,    bid=1 },
  { TRXC_SDCCH8_2,    bid=2 },
  { TRXC_SDCCH8_2,    bid=3 },

SDCCH8 has 8 sub-slots, so up to 8 subscribers can use a single
timeslot. Let's imagine there are three subscribers: A, B, and C.
Both A and C are active subscribers, i.e. they are continuously
transmitting UL bursts, while B is not using ss=1 anymore.

The original way of TDMA frame loss tracking was the following:

  - when an UL burst is received, store it's frame number in
    the timeslot state structure (last_fn);

  - when the next UL burst is received on same timeslot, compute
    how many frames elapsed since the last_fn;

  - if elapsed = (current_fn - last_fn) is lower than 10, then
    iterate from (last_fn + 1) until the current_fn and send
    dummy zero-filled bursts to the higher layers;

  - otherwise (elapsed > 10), process the current burst,
    and do nothing :/

According to our example, subscriber A is sending 4 bursts, then
nobody is sending anything, and then subscriber C is sending
4 bursts. So, there is a 4 frames long gap between the both
transmissions, which is being substituted by dummy bursts. But,
as the logical channel on ss=1 is not active, they are dropped.

This is not that scary, but the current algorithm produces lots
of false-positives, and moreover is not able to track real frame
drops in longer periods (i.e. >10). So, tracking the frame loss
per individual logical channels makes much more sense.

Let's finally drop this hackish 'while (42) { ... }', and track
the amount of lost / received TDMA frames (bursts) individually
per logical channels. Let's also use the multiframe period as
the loss detection period, instead of hardcoded 10. And finally,
let's print more informative debug messages.

Also, it makes sense to use the amount of lost / received bursts
during the calculation of the measurement reports, instead of
sending dummy bursts, but let's do this separately.

Change-Id: I70d05b67a35ddcbdd1b6394dbd7198404a440e76
Related: OS#3428
---
M include/osmo-bts/scheduler.h
M src/common/scheduler.c
2 files changed, 142 insertions(+), 65 deletions(-)

Approvals:
  Harald Welte: Looks good to me, but someone else must approve
  Pau Espin Pedrol: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/include/osmo-bts/scheduler.h b/include/osmo-bts/scheduler.h
index 32d6e91..f9d9962 100644
--- a/include/osmo-bts/scheduler.h
+++ b/include/osmo-bts/scheduler.h
@@ -80,6 +80,9 @@
 
 	/* loss detection */
 	uint8_t			lost_frames;	/* how many L2 frames were lost */
+	uint32_t		last_tdma_fn;	/* last processed TDMA frame number */
+	uint32_t		proc_tdma_fs;	/* how many TDMA frames were processed */
+	uint32_t		lost_tdma_fs;	/* how many TDMA frames were lost */
 
 	/* mode */
 	uint8_t			rsl_cmode, tch_mode; /* mode for TCH channels */
@@ -124,7 +127,6 @@
 
 struct l1sched_ts {
 	uint8_t 		mf_index;	/* selected multiframe index */
-	uint32_t 		mf_last_fn;	/* last received frame number */
 	uint8_t			mf_period;	/* period of multiframe */
 	const struct trx_sched_frame *mf_frames; /* pointer to frame layout */
 
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index 65ece7f..f705ddf 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -222,7 +222,6 @@
 		struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 		l1ts->mf_index = 0;
-		l1ts->mf_last_fn = 0;
 		INIT_LLIST_HEAD(&l1ts->dl_prims);
 		for (i = 0; i < ARRAY_SIZE(l1ts->chan_state); i++) {
 			struct l1sched_chan_state *chan_state;
@@ -853,8 +852,115 @@
 	return bits;
 }
 
+#define TDMA_FN_SUM(a, b) \
+	((a + GSM_HYPERFRAME + b) % GSM_HYPERFRAME)
+
+#define TDMA_FN_SUB(a, b) \
+	((a + GSM_HYPERFRAME - b) % GSM_HYPERFRAME)
+
+static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
+	struct l1sched_chan_state *l1cs, uint8_t tn, uint32_t fn)
+{
+	const struct trx_sched_frame *frame_head;
+	const struct trx_sched_frame *frame;
+	struct l1sched_ts *l1ts;
+	uint32_t elapsed_fs;
+	uint8_t offset, i;
+	uint32_t fn_i;
+
+	/**
+	 * When a channel is just activated, the MS needs some time
+	 * to synchronize and start burst transmission,
+	 * so let's wait until the first UL burst...
+	 */
+	if (l1cs->proc_tdma_fs == 0)
+		return 0;
+
+	/* Get current TDMA frame info */
+	l1ts = l1sched_trx_get_ts(l1t, tn);
+	offset = fn % l1ts->mf_period;
+	frame_head = l1ts->mf_frames + offset;
+
+	/* Not applicable for some logical channels */
+	switch (frame_head->ul_chan) {
+	case TRXC_IDLE:
+	case TRXC_RACH:
+	case TRXC_PDTCH:
+	case TRXC_PTCCH:
+		return 0;
+	default:
+		/* No applicable if we are waiting for handover RACH */
+		if (l1cs->ho_rach_detect)
+			return 0;
+	}
+
+	/* How many frames elapsed since the last one? */
+	elapsed_fs = TDMA_FN_SUB(fn, l1cs->last_tdma_fn);
+	if (elapsed_fs > l1ts->mf_period) { /* Too many! */
+		LOGL1S(DL1P, LOGL_ERROR, l1t, tn, frame_head->ul_chan, fn,
+			"Too many (>%u) contiguous TDMA frames=%u elapsed "
+			"since the last processed fn=%u\n", l1ts->mf_period,
+			elapsed_fs, l1cs->last_tdma_fn);
+		/* FIXME: how should this affect the measurements? */
+		return -EINVAL;
+	}
+
+	/**
+	 * There are several TDMA frames between the last processed
+	 * frame and currently received one. Let's walk through this
+	 * path and count potentially lost frames, i.e. for which
+	 * we didn't receive the corresponsing UL bursts.
+	 *
+	 * Start counting from the last_fn + 1.
+	 */
+	for (i = 1; i < elapsed_fs; i++) {
+		fn_i = TDMA_FN_SUM(l1cs->last_tdma_fn, i);
+		offset = fn_i % l1ts->mf_period;
+		frame = l1ts->mf_frames + offset;
+
+		if (frame->ul_chan == frame_head->ul_chan)
+			l1cs->lost_tdma_fs++;
+	}
+
+	if (l1cs->lost_tdma_fs > 0) {
+		LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, frame_head->ul_chan, fn,
+			"At least %u TDMA frames were lost since the last "
+			"processed fn=%u\n", l1cs->lost_tdma_fs, l1cs->last_tdma_fn);
+
+		/**
+		 * HACK: substitute lost bursts by zero-filled ones
+		 *
+		 * Instead of doing this, it makes sense to use the
+		 * amount of lost frames in measurement calculations.
+		 */
+		static sbit_t zero_burst[GSM_BURST_LEN] = { 0 };
+		trx_sched_ul_func *func;
+
+		for (i = 1; i < elapsed_fs; i++) {
+			fn_i = TDMA_FN_SUM(l1cs->last_tdma_fn, i);
+			offset = fn_i % l1ts->mf_period;
+			frame = l1ts->mf_frames + offset;
+			func = trx_chan_desc[frame->ul_chan].ul_fn;
+
+			if (frame->ul_chan != frame_head->ul_chan)
+				continue;
+
+			LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, frame->ul_chan, fn,
+				"Substituting lost TDMA frame=%u by all-zero "
+				"dummy burst\n", fn_i);
+
+			func(l1t, tn, fn_i, frame->ul_chan, frame->ul_bid,
+				zero_burst, GSM_BURST_LEN, -128, 0);
+
+			l1cs->lost_tdma_fs--;
+		}
+	}
+
+	return 0;
+}
+
 /* process uplink burst */
-int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t current_fn,
+int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	sbit_t *bits, uint16_t nbits, int8_t rssi, int16_t toa256)
 {
 	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
@@ -863,82 +969,51 @@
 	uint8_t offset, period, bid;
 	trx_sched_ul_func *func;
 	enum trx_chan_type chan;
-	uint32_t fn, elapsed;
 
 	if (!l1ts->mf_index)
 		return -EINVAL;
 
-	/* calculate how many frames have been elapsed */
-	elapsed = (current_fn + GSM_HYPERFRAME - l1ts->mf_last_fn) % GSM_HYPERFRAME;
+	/* get frame from multiframe */
+	period = l1ts->mf_period;
+	offset = fn % period;
+	frame = l1ts->mf_frames + offset;
 
-	/* start counting from last fn + 1, but only if not too many fn have
-	 * been elapsed */
-	if (elapsed < 10) {
-		fn = (l1ts->mf_last_fn + 1) % GSM_HYPERFRAME;
-	} else {
-		LOGPFN(DL1P, LOGL_NOTICE, current_fn,
-		       "Too many contiguous elapsed fn, dropping %u\n", elapsed);
-		fn = current_fn;
-	}
+	chan = frame->ul_chan;
+	bid = frame->ul_bid;
+	l1cs = &l1ts->chan_state[chan];
+	func = trx_chan_desc[chan].ul_fn;
 
-	while (42) {
-		/* get frame from multiframe */
-		period = l1ts->mf_period;
-		offset = fn % period;
-		frame = l1ts->mf_frames + offset;
+	/* check if channel is active */
+	if (!trx_chan_desc[chan].auto_active && !l1cs->active)
+		return -EINVAL;
 
-		chan = frame->ul_chan;
-		bid = frame->ul_bid;
-		func = trx_chan_desc[chan].ul_fn;
+	/* omit bursts which have no handler, like IDLE bursts */
+	if (!func)
+		return -EINVAL;
 
-		l1cs = &l1ts->chan_state[chan];
+	/* calculate how many TDMA frames were potentially lost */
+	trx_sched_calc_frame_loss(l1t, l1cs, tn, fn);
 
-		/* check if channel is active */
-		if (!trx_chan_desc[chan].auto_active && !l1cs->active)
-			goto next_frame;
+	/* update TDMA frame counters */
+	l1cs->last_tdma_fn = fn;
+	l1cs->proc_tdma_fs++;
 
-		/* omit bursts which have no handler, like IDLE bursts */
-		if (!func)
-			goto next_frame;
+	/* decrypt */
+	if (bits && l1cs->ul_encr_algo) {
+		ubit_t ks[114];
+		int i;
 
-		/* put burst to function */
-		if (fn == current_fn) {
-			/* decrypt */
-			if (bits && l1cs->ul_encr_algo) {
-				ubit_t ks[114];
-				int i;
-
-				osmo_a5(l1cs->ul_encr_algo,
-					l1cs->ul_encr_key,
-					fn, NULL, ks);
-				for (i = 0; i < 57; i++) {
-					if (ks[i])
-						bits[i + 3] = - bits[i + 3];
-					if (ks[i + 57])
-						bits[i + 88] = - bits[i + 88];
-				}
-			}
-
-			func(l1t, tn, fn, chan, bid, bits, nbits, rssi, toa256);
-		} else if (chan != TRXC_RACH && !l1cs->ho_rach_detect) {
-			sbit_t spare[GSM_BURST_LEN];
-			memset(spare, 0, GSM_BURST_LEN);
-			/* We missed a couple of frame numbers (system overload?) and are now
-			 * substituting some zero-filled bursts for those bursts we missed */
-			LOGPFN(DL1P, LOGL_ERROR, fn, "Substituting all-zero burst (current_fn=%u, "
-				"elapsed=%u\n", current_fn, elapsed);
-			func(l1t, tn, fn, chan, bid, spare, GSM_BURST_LEN, -128, 0);
+		osmo_a5(l1cs->ul_encr_algo, l1cs->ul_encr_key, fn, NULL, ks);
+		for (i = 0; i < 57; i++) {
+			if (ks[i])
+				bits[i + 3] = - bits[i + 3];
+			if (ks[i + 57])
+				bits[i + 88] = - bits[i + 88];
 		}
-
-next_frame:
-		/* reached current fn */
-		if (fn == current_fn)
-			break;
-
-		fn = (fn + 1) % GSM_HYPERFRAME;
 	}
 
-	l1ts->mf_last_fn = fn;
+	/* put burst to function */
+	func(l1t, tn, fn, chan, bid, bits, nbits, rssi, toa256);
 
 	return 0;
 }

-- 
To view, visit https://gerrit.osmocom.org/10309
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I70d05b67a35ddcbdd1b6394dbd7198404a440e76
Gerrit-Change-Number: 10309
Gerrit-PatchSet: 5
Gerrit-Owner: Vadim Yanitskiy <axilirator at gmail.com>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Pau Espin Pedrol <pespin at sysmocom.de>
Gerrit-Reviewer: Vadim Yanitskiy <axilirator at gmail.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20181025/614e5c75/attachment.htm>


More information about the gerrit-log mailing list