codec: add SID classification functions per GSM 06.31 & 06.81

The existing osmo_{fr,efr}_check_sid() functions detect whether or not
the frame of bits passed to them constitutes an absolutely perfect
SID frame, with each of 95 SID code word bits set to 0 for FR or
1 for EFR.  However, the rules of section 6.1.1 in GSM 06.31 & 06.81
allow up to one bit to be in error for the frame to be accepted as
valid SID, and the same rules also call out a third state (invalid SID)
in between valid SID frames and other frames that are classified as
speech.  Support for these rules cannot be patched into those familiar
osmo_{fr,efr}_check_sid() functions because doing so would alter
behavior of existing programs, hence new functions need to be added.

The new functions that implement the exact rules of section 6.1.1 of
GSM 06.31 and 06.81 will need to be used if anyone needs to construct
an outgoing TRAU-UL frame (for example, as part of implementing
TS 28.062 TFO), and they are expected to be useful as part of more
sophisticated ECU implementations.

Change-Id: Ie91a52c6f04689082d8004311517d8ce0c544916
This commit is contained in:
Mychaela N. Falconia 2023-04-16 01:34:35 +00:00
parent 91f5eee672
commit ec65085d5f
3 changed files with 150 additions and 26 deletions

View File

@ -81,9 +81,20 @@ static inline bool osmo_amr_is_speech(enum osmo_amr_type ft)
}
}
/* SID ternary classification per GSM 06.31 & 06.81 section 6.1.1 */
enum osmo_gsm631_sid_class {
OSMO_GSM631_SID_CLASS_SPEECH = 0,
OSMO_GSM631_SID_CLASS_INVALID = 1,
OSMO_GSM631_SID_CLASS_VALID = 2,
};
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload);
enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload);
int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
enum osmo_amr_quality bfi);
int osmo_amr_rtp_dec(const uint8_t *payload, int payload_len, uint8_t *cmr,

View File

@ -296,6 +296,21 @@ const uint16_t gsm610_bitorder[260] = {
29, /* LARc5:0 */
};
static const uint16_t sid_code_word_bits[95] = {
/* bit numbers are relative to the RTP frame beginning,
* with signature bits included in the count. */
57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
125, 126, 128, 129, 131, 132, 134, 135, 137,
138, 140, 141, 143, 144, 146, 147, 149, 150,
169, 170, 172, 173, 175, 176, 178, 179, 181,
182, 184, 185, 187, 188, 190, 191, 193, 194,
196, 197, 199, 200, 202, 203, 205, 206, 225,
226, 228, 229, 231, 232, 234, 235, 237, 240,
243, 246, 249, 252, 255, 258, 261
};
/*! Check whether RTP frame contains FR SID code word according to
* TS 101 318 §5.1.2
* \param[in] rtp_payload Buffer with RTP payload
@ -305,16 +320,7 @@ const uint16_t gsm610_bitorder[260] = {
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
struct bitvec bv;
uint16_t i, z_bits[] = { 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
125, 126, 128, 129, 131, 132, 134, 135, 137,
138, 140, 141, 143, 144, 146, 147, 149, 150,
169, 170, 172, 173, 175, 176, 178, 179, 181,
182, 184, 185, 187, 188, 190, 191, 193, 194,
196, 197, 199, 200, 202, 203, 205, 206, 225,
226, 228, 229, 231, 232, 234, 235, 237, 240,
243, 246, 249, 252, 255, 258, 261 };
uint16_t i;
/* signature does not match Full Rate SID */
if ((rtp_payload[0] >> 4) != 0xD)
@ -323,10 +329,63 @@ bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
bv.data = (uint8_t *) rtp_payload;
bv.data_len = payload_len;
/* code word is all 0 at given bits, numbered from 1 */
for (i = 0; i < ARRAY_SIZE(z_bits); i++)
if (bitvec_get_bit_pos(&bv, z_bits[i]) != ZERO)
/* code word is all 0 at given bits */
for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
return false;
}
return true;
}
/*! Classify potentially-SID FR codec frame in RTP format according
* to the rules of GSM 06.31 §6.1.1
* \param[in] rtp_payload Buffer with RTP payload
* \returns enum osmo_gsm631_sid_class, with symbolic values
* OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
* OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
* classifications prescribed by the spec.
*
* Differences between the more familiar osmo_fr_check_sid() and the present
* function are:
*
* 1. osmo_fr_check_sid() returns true only if the SID frame is absolutely
* perfect, with all 95 bits of the SID code word zeroed. However, the
* rules of GSM 06.31 §6.1.1 allow up to one bit to be in error,
* and the frame is still accepted as valid SID.
*
* 2. The third possible state of invalid SID is not handled at all by the
* simpler osmo_fr_check_sid() function.
*
* 3. osmo_fr_check_sid() includes a check for 0xD RTP signature, and returns
* false if that signature nibble is wrong. That check is not included
* in the present version because there is no place for it in the
* ETSI-prescribed classification, it is neither speech nor SID. The
* assumption is that this function is used to classify the bit content
* of received codec frames, not their RTP encoding - the latter needs
* to be validated beforehand.
*
* Which function should one use? The answer depends on the specific
* circumstances, and needs to be addressed on a case-by-case basis.
*/
enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload)
{
struct bitvec bv;
uint16_t i, n;
bv.data = (uint8_t *) rtp_payload;
bv.data_len = GSM_FR_BYTES;
/* count not-SID-matching bits per the spec */
n = 0;
for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
n++;
if (n >= 16)
return OSMO_GSM631_SID_CLASS_SPEECH;
}
if (n >= 2)
return OSMO_GSM631_SID_CLASS_INVALID;
else
return OSMO_GSM631_SID_CLASS_VALID;
}

View File

@ -258,6 +258,21 @@ const uint16_t gsm660_bitorder[260] = {
246, /* 259 -> PULSE 4_10: b0 */
};
static const uint8_t sid_code_word_bits[95] = {
/* bit numbers are relative to "pure" EFR frame beginning,
* not counting the signature bits. */
45, 46, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
66, 67, 68, 94, 95, 96, 98, 99, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 148, 149, 150,
151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
171, 196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 212, 213, 214, 215, 216,
217, 218, 219, 220, 221
};
/*! Check whether RTP frame contains EFR SID code word according to
* TS 101 318 §5.3.2
* \param[in] rtp_payload Buffer with RTP payload
@ -268,19 +283,6 @@ bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
struct bitvec bv;
uint16_t i;
static const uint8_t sid_code_word_bits[95] = {
/* bit numbers relative to "pure" EFR frame beginning,
* not counting the signature bits. */
45, 46, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
66, 67, 68, 94, 95, 96, 98, 99, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 148, 149, 150,
151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
171, 196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 212, 213, 214, 215, 216,
217, 218, 219, 220, 221 };
/* signature does not match Enhanced Full Rate SID */
if ((rtp_payload[0] >> 4) != 0xC)
@ -297,3 +299,55 @@ bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
return true;
}
/*! Classify potentially-SID EFR codec frame in RTP format according
* to the rules of GSM 06.81 §6.1.1
* \param[in] rtp_payload Buffer with RTP payload
* \returns enum osmo_gsm631_sid_class, with symbolic values
* OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
* OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
* classifications prescribed by the spec.
*
* Differences between the more familiar osmo_efr_check_sid() and the present
* function are:
*
* 1. osmo_efr_check_sid() returns true only if the SID frame is absolutely
* perfect, with all 95 bits of the SID code word set. However, the
* rules of GSM 06.81 §6.1.1 allow up to one bit to be in error,
* and the frame is still accepted as valid SID.
*
* 2. The third possible state of invalid SID is not handled at all by the
* simpler osmo_efr_check_sid() function.
*
* 3. osmo_efr_check_sid() includes a check for 0xC RTP signature, and returns
* false if that signature nibble is wrong. That check is not included
* in the present version because there is no place for it in the
* ETSI-prescribed classification, it is neither speech nor SID. The
* assumption is that this function is used to classify the bit content
* of received codec frames, not their RTP encoding - the latter needs
* to be validated beforehand.
*
* Which function should one use? The answer depends on the specific
* circumstances, and needs to be addressed on a case-by-case basis.
*/
enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload)
{
struct bitvec bv;
uint16_t i, n;
bv.data = (uint8_t *) rtp_payload;
bv.data_len = GSM_EFR_BYTES;
/* count not-SID-matching bits per the spec */
n = 0;
for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]+4) != ONE)
n++;
if (n >= 16)
return OSMO_GSM631_SID_CLASS_SPEECH;
}
if (n >= 2)
return OSMO_GSM631_SID_CLASS_INVALID;
else
return OSMO_GSM631_SID_CLASS_VALID;
}