diff options
Diffstat (limited to 'src/codec/gsm660.c')
-rw-r--r-- | src/codec/gsm660.c | 166 |
1 files changed, 162 insertions, 4 deletions
diff --git a/src/codec/gsm660.c b/src/codec/gsm660.c index 4f7bb099..b15bdf37 100644 --- a/src/codec/gsm660.c +++ b/src/codec/gsm660.c @@ -17,13 +17,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/bitvec.h> +#include <osmocom/core/utils.h> #include <osmocom/codec/codec.h> /* GSM EFR - subjective importance bit ordering */ @@ -257,3 +257,161 @@ const uint16_t gsm660_bitorder[260] = { 243, /* 258 -> PULSE 4_9: b0 */ 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 + * \param[in] payload_len Length of payload + * \returns true if code word is found, false otherwise + */ +bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len) +{ + struct bitvec bv; + uint16_t i; + + /* signature does not match Enhanced Full Rate SID */ + if ((rtp_payload[0] >> 4) != 0xC) + return false; + + bv.data = (uint8_t *) rtp_payload; + bv.data_len = payload_len; + + /* code word is all 1 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]+4) != ONE) + return false; + } + + 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; +} + +/*! Reset the SID field of a potentially corrupted, but still valid GSM-EFR + * SID frame in RTP encoding to its pristine state (full SID code word). + * \param[in] rtp_payload Buffer with RTP payload - must be writable! + * + * Per GSM 06.62 section 5.3, a freshly minted SID frame consists of 58 bits + * of comfort noise parameters (LSF and 4 times fixed codebook gain), 95 bits + * of SID code word (all 1s) and 91 unused bits (all 0s). Network elements + * that receive SID frames from call leg A uplink and need to retransmit them + * on leg B downlink should "rejuvenate" received SID frames prior to + * retransmission by resetting the SID field to its pristine state of all 1s; + * this function does the job. + * + * Potential TODO: it would be nice to also zero out the remaining 91 bits + * which the spec leaves as reserved, clearing out leg A radio bit errors - + * but do we really need to? + */ +void osmo_efr_sid_reset(uint8_t *rtp_payload) +{ + /* set all 95 SID code word bits to 1 */ + rtp_payload[6] |= 0x6F; + rtp_payload[7] = 0xFF; + rtp_payload[8] = 0xFF; + rtp_payload[9] |= 0x80; + rtp_payload[12] |= 0x3B; + rtp_payload[13] = 0xFF; + rtp_payload[14] = 0xFF; + rtp_payload[15] |= 0xE0; + rtp_payload[19] = 0xFF; + rtp_payload[20] = 0xFF; + rtp_payload[21] = 0xFF; + rtp_payload[25] = 0xFF; + rtp_payload[26] |= 0xFC; + rtp_payload[27] = 0xFF; + rtp_payload[28] |= 0xC0; +} + +/*! Preen potentially-SID EFR codec frame in RTP format, ensuring that it is + * either a speech frame or a valid SID, and if the latter, making it a + * perfect, error-free SID frame. + * \param[in] rtp_payload Buffer with RTP payload - must be writable! + * \returns true if the frame is good, false otherwise + */ +bool osmo_efr_sid_preen(uint8_t *rtp_payload) +{ + enum osmo_gsm631_sid_class sidc; + + sidc = osmo_efr_sid_classify(rtp_payload); + switch (sidc) { + case OSMO_GSM631_SID_CLASS_SPEECH: + return true; + case OSMO_GSM631_SID_CLASS_INVALID: + return false; + case OSMO_GSM631_SID_CLASS_VALID: + /* "Rejuvenate" this SID frame, correcting any errors */ + osmo_efr_sid_reset(rtp_payload); + return true; + default: + /* There are only 3 possible SID classifications per GSM 06.81 + * section 6.1.1, thus any other return value is a grave error + * in the code. */ + OSMO_ASSERT(0); + } +} |