falconia has uploaded this change for review.

View Change

TCH DL, FR & EFR: reshape SIDs per 06.31/06.81 section 5.1.2

Section 5.1.2 of GSM 06.31 & 06.81 specifies the expected shape of
radio downlink for FR & EFR in the presence of SIDs: one SID frame
after each talkspurt (after speech frames), and one SID frame in
every SACCH-aligned position every 480 ms, or if the actual SACCH-
aligned position is taken up by FACCH, then just one SID frame as
soon as possible after that FACCH - and no transmitted SID frames
in other positions.

This prescribed shape of call leg B DL won't happen on its own if
the RTP stream comes from call leg A UL in a TrFO scenario - whichever
SID frames are present will be in wrong positions for leg B DL,
and even in the case of transcoded calls the responsibility for DL
SID shaping cannot be placed on the RTP stream source because that
source won't know where SACCH alignment will lie. Therefore,
the necessary DL SID reshaping has to be done in the RTP stream
receiver in OsmoBTS.

Related: OS#5996
Change-Id: I924ab21952dcf8bb03ba7ccef790474bf66fc9e5
---
M include/osmo-bts/lchan.h
M src/common/l1sap.c
2 files changed, 143 insertions(+), 0 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/14/32714/1
diff --git a/include/osmo-bts/lchan.h b/include/osmo-bts/lchan.h
index d89aa1f..23fc5a3 100644
--- a/include/osmo-bts/lchan.h
+++ b/include/osmo-bts/lchan.h
@@ -8,6 +8,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/codec/codec.h>
#include <osmocom/codec/ecu.h>
#include <osmocom/gsm/lapdm.h>
#include <osmocom/gsm/sysinfo.h>
@@ -265,6 +266,19 @@
/* last UL SPEECH resume flag */
bool is_speech_resume;
} dtx;
+ struct {
+ bool last_rtp_input_was_sid;
+ uint8_t last_sid[GSM_FR_BYTES];
+ uint8_t last_sid_len;
+ uint8_t last_sid_age;
+ /* A SID was transmitted on the DL in the period
+ * beginning with the last transmitted speech frame
+ * or the last mandatory-Tx position, whichever was
+ * more recent. */
+ bool dl_sid_transmitted;
+ /* The current frame in the DL is taken up by FACCH */
+ bool dl_facch_stealing;
+ } nonamr;
uint8_t last_cmr;
uint32_t last_fn;

diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index f24bc2f..cf3e2ac 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -1163,6 +1163,7 @@
pp_msg = lapdm_phsap_dequeue_msg_facch(lchan, le, fn);
else
pp_msg = lapdm_phsap_dequeue_msg(le);
+ lchan->tch.nonamr.dl_facch_stealing = (pp_msg != NULL);
}
if (!pp_msg) {
if (L1SAP_IS_LINK_SACCH(link_id)) {
@@ -1298,6 +1299,29 @@
}
}

+/*! \brief Check if the given FN of TCH-RTS-IND corresponds to a mandatory
+ * SID position for non-AMR codecs that follow SACCH alignment.
+ * \param[in] lchan Logical channel on which we check scheduling
+ * \param[in] fn Frame Number for which we check scheduling
+ * \returns true if this FN is a mandatory SID position, false otherwise
+ */
+static inline bool nonamr_mand_sid_position(struct gsm_lchan *lchan,
+ uint32_t fn)
+{
+ /* See GSM 05.08 section 8.3 for frame numbers - but we are
+ * specifically looking for FNs corresponding to the beginning
+ * of the complete SID frame to be transmitted, rather than all FNs
+ * where we have to put out a non-suppressed burst. */
+ if (lchan->type == GSM_LCHAN_TCH_F) {
+ return fn % 104 == 52;
+ } else {
+ if (lchan->nr)
+ return fn % 104 == 14 || fn % 104 == 66;
+ else
+ return fn % 104 == 0 || fn % 104 == 52;
+ }
+}
+
/* TCH-RTS-IND prim received from bts model */
static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind)
@@ -1308,6 +1332,7 @@
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
uint32_t fn;
+ bool is_nonamr_sid = false;

chan_nr = rts_ind->chan_nr;
fn = rts_ind->fn;
@@ -1349,8 +1374,85 @@
msgb_push(resp_msg, sizeof(*resp_l1sap));
resp_msg->l1h = resp_msg->data;
resp_l1sap = msgb_l1sap_prim(resp_msg);
+
+ /* non-AMR SID handling */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ if (lchan->type == GSM_LCHAN_TCH_F) {
+ is_nonamr_sid =
+ osmo_fr_check_sid(msgb_l2(resp_msg),
+ msgb_l2len(resp_msg));
+ } else {
+ /* FIXME: can't do the same for HR1
+ * until OS#5688 is resolved. */
+ is_nonamr_sid = false;
+ }
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ is_nonamr_sid = osmo_efr_check_sid(msgb_l2(resp_msg),
+ msgb_l2len(resp_msg));
+ break;
+ default:
+ is_nonamr_sid = false;
+ }
+ lchan->tch.nonamr.last_rtp_input_was_sid = is_nonamr_sid;
+ if (is_nonamr_sid) {
+ uint8_t copy_len;
+
+ copy_len = OSMO_MIN(msgb_l2len(resp_msg),
+ ARRAY_SIZE(lchan->tch.nonamr.last_sid));
+ memcpy(lchan->tch.nonamr.last_sid, msgb_l2(resp_msg),
+ copy_len);
+ lchan->tch.nonamr.last_sid_len = copy_len;
+ lchan->tch.nonamr.last_sid_age = 0;
+ } else
+ lchan->tch.nonamr.dl_sid_transmitted = false;
}

+ /* For non-AMR speech TCH, if our incoming RTP stream includes SID
+ * frames, we need to apply the following transformations to the
+ * downlink frame stream we actually transmit:
+ *
+ * - We need to cache the last received SID and retransmit it in
+ * SACCH-aligned frame positions, or if the SACCH-aligned frame
+ * position was stolen by FACCH, then right after that FACCH.
+ *
+ * - That cached SID needs to be aged and expired, in accord with
+ * TS 28.062 section C.3.2.1.1 paragraph 5 - the paragraph concerning
+ * expiration of cached downlink SID.
+ *
+ * - In all other frame positions, extraneous SID frames need to be
+ * dropped - we send an empty payload to the BTS model, causing it
+ * to transmit an induced BFI condition on the air (fake DTXd),
+ * or perhaps real DTXd (actually turning off Tx) in the future.
+ *
+ * If the channel mode is AMR speech or non-speech, the non-AMR speech
+ * logic that follows will have no effect.
+ */
+ if (nonamr_mand_sid_position(lchan, fn))
+ lchan->tch.nonamr.dl_sid_transmitted = false;
+ if (!resp_msg && lchan->tch.nonamr.last_rtp_input_was_sid &&
+ lchan->tch.nonamr.last_sid_age < 47) {
+ lchan->tch.nonamr.last_sid_age++;
+ if (!lchan->tch.nonamr.dl_sid_transmitted)
+ resp_msg = l1sap_msgb_alloc(lchan->tch.nonamr.last_sid_len);
+ if (resp_msg) {
+ resp_msg->l2h = msgb_put(resp_msg,
+ lchan->tch.nonamr.last_sid_len);
+ memcpy(resp_msg->l2h, lchan->tch.nonamr.last_sid,
+ lchan->tch.nonamr.last_sid_len);
+ resp_l1sap = msgb_l1sap_prim(resp_msg);
+ is_nonamr_sid = true;
+ }
+ } else if (resp_msg && is_nonamr_sid &&
+ lchan->tch.nonamr.dl_sid_transmitted) {
+ msgb_free(resp_msg);
+ resp_msg = NULL;
+ resp_l1sap = &empty_l1sap;
+ }
+ if (resp_msg && is_nonamr_sid && !lchan->tch.nonamr.dl_facch_stealing)
+ lchan->tch.nonamr.dl_sid_transmitted = true;
+
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg);

To view, visit change 32714. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I924ab21952dcf8bb03ba7ccef790474bf66fc9e5
Gerrit-Change-Number: 32714
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon@freecalypso.org>
Gerrit-MessageType: newchange