<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmocom-bb/+/17352">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified

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

<div style="display:none"> Gerrit-Project: osmocom-bb </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I3551d79796a3730565c2c70577e9d134e636f275 </div>
<div style="display:none"> Gerrit-Change-Number: 17352 </div>
<div style="display:none"> Gerrit-PatchSet: 6 </div>
<div style="display:none"> Gerrit-Owner: fixeria <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>