summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2020-03-04 21:22:11 +0700
committerlaforge <laforge@osmocom.org>2020-03-16 10:32:42 +0000
commit431dcd00f04787c072c957e4781e19a46946567d (patch)
tree4143d2fecfd70204b2f5f6b2f89725c0235c09fc
parent8c760f8f056361fa08d5c89280e92486cb62ba0c (diff)
trxcon/scheduler: substitute lost TDMA frames on Downlink
It may happen that one or more Downlink bursts are lost on their way to the MS due to a variety of reasons. Modern transceivers supporting TRXDv1 protocol would substitute lost bursts with so-called NOPE indications. Hovewer, neither fake_trx.py nor grgsm_trx do support this feature at the moment. We can still detect and compensate TDMA frame loss per logical channels in the same way as it's already done in osmo-bts-trx. In short, we should keep TDMA frame number of the last received burst in the logical channel state, and using the appropriate multiframe layout, check if there were any gaps between TDMA frame number of the current burst and the stored one. Change-Id: I3551d79796a3730565c2c70577e9d134e636f275
-rw-r--r--src/host/trxcon/sched_trx.c141
-rw-r--r--src/host/trxcon/sched_trx.h14
2 files changed, 100 insertions, 55 deletions
diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c
index e6e759a6..ba75b6f7 100644
--- a/src/host/trxcon/sched_trx.c
+++ b/src/host/trxcon/sched_trx.c
@@ -321,9 +321,6 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn)
if (ts == NULL)
return -EINVAL;
- /* Flush TS frame counter */
- ts->mf_last_fn = 0;
-
/* Undefine multiframe layout */
ts->mf_layout = NULL;
@@ -491,6 +488,9 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan)
/* Reset ciphering state */
memset(&lchan->a5, 0x00, sizeof(lchan->a5));
+
+ /* Reset TDMA frame statistics */
+ memset(&lchan->tdma, 0x00, sizeof(lchan->tdma));
}
int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
@@ -610,8 +610,65 @@ static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
}
}
+static int subst_frame_loss(struct trx_lchan_state *lchan,
+ trx_lchan_rx_func *handler,
+ uint32_t fn)
+{
+ const struct trx_multiframe *mf;
+ const struct trx_frame *fp;
+ unsigned int elapsed, i;
+
+ /* Wait until at least one TDMA frame is processed */
+ if (lchan->tdma.num_proc == 0)
+ return -EAGAIN;
+
+ /* Short alias for the current multiframe */
+ mf = lchan->ts->mf_layout;
+
+ /* How many frames elapsed since the last one? */
+ elapsed = TDMA_FN_SUB(fn, lchan->tdma.last_proc);
+ if (elapsed > mf->period) {
+ LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%u) "
+ "since the last processed fn=%u\n", mf->period,
+ elapsed, lchan->tdma.last_proc);
+ } else if (elapsed == 0) {
+ LOGP(DSCHD, LOGL_ERROR, "No TDMA frames elapsed since the last processed "
+ "fn=%u, must be a bug?\n", lchan->tdma.last_proc);
+ return -EIO;
+ }
+
+ /* TODO: make bits constant */
+ static sbit_t bits[148] = { 0 };
+ struct trx_meas_set fake_meas = {
+ .fn = lchan->tdma.last_proc,
+ .rssi = -120,
+ .toa256 = 0,
+ };
+
+ /* Traverse from fp till the current frame */
+ for (i = 0; i < elapsed - 1; i++) {
+ fp = &mf->frames[TDMA_FN_INC(&fake_meas.fn) % mf->period];
+ if (fp->dl_chan != lchan->type)
+ continue;
+
+ LOGP(DSCHD, LOGL_NOTICE, "Substituting lost TDMA frame %u on %s\n",
+ fake_meas.fn, trx_lchan_desc[lchan->type].name);
+
+ handler(lchan->ts->trx, lchan->ts, lchan,
+ fake_meas.fn, fp->dl_bid,
+ bits, &fake_meas);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = fake_meas.fn;
+ lchan->tdma.num_proc++;
+ lchan->tdma.num_lost++;
+ }
+
+ return 0;
+}
+
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
+ uint32_t fn, sbit_t *bits, uint16_t nbits,
const struct trx_meas_set *meas)
{
struct trx_lchan_state *lchan;
@@ -620,7 +677,6 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
trx_lchan_rx_func *handler;
enum trx_lchan_type chan;
- uint32_t fn, elapsed;
uint8_t offset, bid;
/* Check whether required timeslot is allocated and configured */
@@ -631,61 +687,42 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
return -EINVAL;
}
- /* Calculate how many frames have been elapsed */
- elapsed = TDMA_FN_SUB(burst_fn, ts->mf_last_fn);
-
- /**
- * If not too many frames have been elapsed,
- * start counting from last fn + 1
- */
- if (elapsed < 10)
- fn = TDMA_FN_SUM(ts->mf_last_fn, 1);
- else
- fn = burst_fn;
-
- while (1) {
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- bid = frame->dl_bid;
- chan = frame->dl_chan;
- handler = trx_lchan_desc[chan].rx_fn;
+ /* Get frame from multiframe */
+ offset = fn % ts->mf_layout->period;
+ frame = ts->mf_layout->frames + offset;
- /* Omit bursts which have no handler, like IDLE bursts */
- if (!handler)
- goto next_frame;
+ /* Get required info from frame */
+ bid = frame->dl_bid;
+ chan = frame->dl_chan;
+ handler = trx_lchan_desc[chan].rx_fn;
- /* Find required channel state */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- goto next_frame;
+ /* Omit bursts which have no handler, like IDLE bursts.
+ * TODO: handle noise indications during IDLE frames. */
+ if (!handler)
+ return -ENODEV;
- /* Ensure that channel is active */
- if (!lchan->active)
- goto next_frame;
+ /* Find required channel state */
+ lchan = sched_trx_find_lchan(ts, chan);
+ if (lchan == NULL)
+ return -ENODEV;
- /* Reached current fn */
- if (fn == burst_fn) {
- /* Perform A5/X decryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_dec(lchan, fn, bits);
+ /* Ensure that channel is active */
+ if (!lchan->active)
+ return 0;
- /* Put burst to handler */
- handler(trx, ts, lchan, fn, bid, bits, meas);
- }
+ /* Compensate lost TDMA frames (if any) */
+ subst_frame_loss(lchan, handler, fn);
-next_frame:
- /* Reached current fn */
- if (fn == burst_fn)
- break;
+ /* Perform A5/X decryption if required */
+ if (lchan->a5.algo)
+ sched_trx_a5_burst_dec(lchan, fn, bits);
- TDMA_FN_INC(&fn);
- }
+ /* Put burst to handler */
+ handler(trx, ts, lchan, fn, bid, bits, meas);
- /* Set last processed frame number */
- ts->mf_last_fn = fn;
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = fn;
+ lchan->tdma.num_proc++;
return 0;
}
diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h
index 44f502cd..cf63df16 100644
--- a/src/host/trxcon/sched_trx.h
+++ b/src/host/trxcon/sched_trx.h
@@ -209,6 +209,16 @@ struct trx_lchan_state {
/*! \brief AVG measurements of the last received block */
struct trx_meas_set meas_avg;
+ /*! \brief TDMA loss detection state */
+ struct {
+ /*! \brief Last processed TDMA frame number */
+ uint32_t last_proc;
+ /*! \brief Number of processed TDMA frames */
+ unsigned long num_proc;
+ /*! \brief Number of lost TDMA frames */
+ unsigned long num_lost;
+ } tdma;
+
/*! \brief SACCH state */
struct {
/*! \brief Cached measurement report (last received) */
@@ -255,8 +265,6 @@ struct trx_lchan_state {
struct trx_ts {
/*! \brief Timeslot index within a frame (0..7) */
uint8_t index;
- /*! \brief Last received frame number */
- uint32_t mf_last_fn;
/*! \brief Pointer to multiframe layout */
const struct trx_multiframe *mf_layout;
@@ -356,7 +364,7 @@ void sched_prim_drop(struct trx_lchan_state *lchan);
void sched_prim_flush_queue(struct llist_head *list);
int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
+ uint32_t fn, sbit_t *bits, uint16_t nbits,
const struct trx_meas_set *meas);
int sched_trx_handle_tx_burst(struct trx_instance *trx,
struct trx_ts *ts, struct trx_lchan_state *lchan,