falconia has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-abis/+/41252?usp=email )
Change subject: trau: add AMR TRAU frame interworking facility ......................................................................
trau: add AMR TRAU frame interworking facility
The simplistic design of osmo_trau2rtp() and osmo_rtp2trau() APIs is not sufficient for the greater complexity encountered in AMR. Instead let us introduce an interworking facility for AMR frames that centers around a common intermediate format, with encoding and decoding functions that convert between this internal intermediate and 3 types of external interfaces: TRAU/TFO frames, RTP in 3 possible formats (RFC 4867 BWE, RFC 4867 OA and TRAU-like TW-TS-006) and speech encoder/decoder engines.
This AMR TRAU frame interworking facility is needed in order to add AMR support to ThemWi E1 Abis MGW (replacement for the limited E1 Abis capability in OsmoMGW), and it will also be used in tw-border-mgw (ThemWi CN sw component) to terminate TW-TS-006 RTP legs, interfacing to libtwamr speech encoder & decoder and to in-band TFO per TS 28.062.
Change-Id: I42706c2f68ed29aff1bf451b37e783983f8de3b0 --- M .gitignore M include/Makefile.am A include/osmocom/trau/amr_trau.h M src/Makefile.am A src/trau/amr_rtp.c A src/trau/amr_trau.c M tests/Makefile.am A tests/amr/rtp-in-bwe.hex A tests/amr/rtp-in-oa.hex A tests/amr/rtp2trau-bwe2fr.ok A tests/amr/rtp2trau-bwe2hr.ok A tests/amr/rtp2trau-oa2fr.ok A tests/amr/rtp2trau-oa2hr.ok A tests/amr/rtp2trau-oax2fr.ok A tests/amr/rtp2trau-oax2hr.ok A tests/amr/rtp2trau_amr.c A tests/amr/trau-amr-16k-bwe.ok A tests/amr/trau-amr-16k-oa.ok A tests/amr/trau-amr-16k-oax.ok A tests/amr/trau-amr-16k.in A tests/amr/trau-amr-8k-bwe.ok A tests/amr/trau-amr-8k-oa.ok A tests/amr/trau-amr-8k-oax.ok A tests/amr/trau-amr-8k.in A tests/amr/trau2rtp_amr.c M tests/testsuite.at 26 files changed, 4,855 insertions(+), 1 deletion(-)
Approvals: falconia: Looks good to me, approved laforge: Looks good to me, but someone else must approve pespin: Looks good to me, but someone else must approve Jenkins Builder: Verified
diff --git a/.gitignore b/.gitignore index 688f868..30a1cf7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ tests/subchan_demux/.dirstamp tests/subchan_demux/subchan_demux_test tests/ipa_recv/ipa_recv_test +tests/amr/trau2rtp_amr +tests/amr/rtp2trau_amr tests/raa_prime/test_dec tests/raa_prime/test_enc tests/tfo/rtp2tfo diff --git a/include/Makefile.am b/include/Makefile.am index 7c00ccc..36d3259 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -24,6 +24,7 @@ osmocom/abis/trau_frame.h \ osmocom/abis/unixsocket_proto.h \ osmocom/abis/version.h \ + osmocom/trau/amr_trau.h \ osmocom/trau/clearmode.h \ osmocom/trau/csd_ra2.h \ osmocom/trau/csd_raa_prime.h \ diff --git a/include/osmocom/trau/amr_trau.h b/include/osmocom/trau/amr_trau.h new file mode 100644 index 0000000..83780e6 --- /dev/null +++ b/include/osmocom/trau/amr_trau.h @@ -0,0 +1,347 @@ +/* + * Interworking of AMR codec frames between TS 48.060 & 48.061 TRAU frame + * formats, RTP in RFC 4867 and TW-TS-006 formats, and speech encoder/decoder + * engines. + * + * This code was contributed to Osmocom Cellular Network Infrastructure + * project by Mother Mychaela N. Falconia of Themyscira Wireless. + * Mother Mychaela's contributions are NOT subject to copyright: + * no rights reserved, all rights relinquished. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/bits.h> +#include <osmocom/codec/codec.h> +#include <osmocom/trau/trau_frame.h> + +/*! \defgroup amr_trau AMR TRAU frame interworking facility + * @{ + * + * The present facility provides interworking of AMR codec frames between + * the following 3 possible external interfaces: + * + * 1) AMR TRAU frames as they appear on T1/E1 Abis per TS 48.060 & 48.061, + * or AMR TFO frames as they appear inside G.711 PCM sample stream + * per TS 28.062. + * + * 2) Representation of AMR codec in RTP, in either of the two formats + * (BWE or OA) defined in RFC 4867 or in the OAX (OA extended) format + * of TW-TS-006. + * + * 3) AMR speech encoder and decoder engines that operate on arrays of + * codec parameters in uint16_t or int16_t format, such as Themyscira + * libtwamr. + * + * Encoding and decoding functions for interfaces 1 and 2 are provided + * as part of the present library component. Implementation of + * interface 3 requires applying lower-level libosmocodec functions + * osmo_amr_param_to_sbits() and osmo_amr_sbits_to_param() directly + * to s_bits[] member in struct osmo_amrt_if; higher-level functions + * that interface struct osmo_amrt_if to struct amr_param_frame (libtwamr) + * can also be implemented, but they cannot be included in libosmo* + * because compile-time dependency on libtwamr is not allowed. + * + * All API functions, struct and enum definitions that are part of this + * facility are prefixed with osmo_amrt_ for namespace reasons. + */ + +/*! Every AMR frame described in a struct osmo_amrt_if has a classification + * as in speech, SID etc. This classification is captured in the following + * enum, and is based on Frame_Classification and No_Speech_Classification + * definitions in TS 48.060. These TS 48.060 definitions have been chosen + * as the basis for the common intermediate representation, rather than + * the frame type system of RFC 4867, because the latter is crippled, + * removing Speech_Degraded and Onset classifications, and furthermore + * the distinctions between good and bad speech, good and bad SID and + * Sid_First vs Sid_Update are coded in a very indirect way in RTP. + * The present TRAU-based frame classification system maps directly to + * speech encoder and decoder frame types in libtwamr (based on 3GPP + * reference implementation), hence the entire burden of converting + * between the two classification systems is moved into RTP interface + * functions. + * + * For our enum osmo_amrt_fc we have chosen 5-bit code points that map + * directly to TS 48.060: bits [4:3] are Frame_Classification per section + * 5.5.1.2.1, bits [2:0] are No_Speech_Classification per section 5.5.1.2.2. + */ +enum osmo_amrt_fc { + OSMO_AMRT_FC_NO_DATA = 0x00, + OSMO_AMRT_FC_SID_BAD = 0x04, + OSMO_AMRT_FC_SID_UPDATE = 0x05, + OSMO_AMRT_FC_ONSET = 0x06, + OSMO_AMRT_FC_SID_FIRST = 0x07, + OSMO_AMRT_FC_SPEECH_BAD = 0x08, + OSMO_AMRT_FC_SPEECH_DEGRADED = 0x10, + OSMO_AMRT_FC_SPEECH_GOOD = 0x18, +}; + +static inline bool osmo_amrt_fc_is_speech(enum osmo_amrt_fc fc) +{ + return (fc & 0x18) != 0; +} + +/*! This enum captures the 3-bit Config_Prot field of TS 28.062 section + * C.6.1.2. This field is used to embed TFO configuration messages + * in AMR TRAU or TFO frames when 3GPP Rel4 method is in use, and it + * needs to be ferried between TRAU/TFO frames and TW-TS-006. + */ +enum osmo_amrt_tfo_config_prot { + OSMO_AMRT_CFG_PROT_NO_CON = 0x00, + OSMO_AMRT_CFG_PROT_CON_REQ = 0x01, + OSMO_AMRT_CFG_PROT_DIS_REQ = 0x02, + OSMO_AMRT_CFG_PROT_CON_ACK = 0x03, + OSMO_AMRT_CFG_PROT_UL_ACK = 0x05, + OSMO_AMRT_CFG_PROT_DL_ACK = 0x06, +}; + +/*! TW-TS-006 section 5.7 adds a new Param_Info field, used to describe + * presence or absence of TFO configuration parameters beyond Config_Prot + * and Message_No fields. This enum captures Param_Info definitions + * from TW-TS-006. Unshifted values are used, exactly as they appear + * in the Config_Prot extension octet. + */ +enum osmo_amrt_tfo_param_info { + OSMO_AMRT_TFOP_ABSENT_NATIVE = 0x00, + OSMO_AMRT_TFOP_ABSENT_NO_ROOM = 0x02, + OSMO_AMRT_TFOP_ABSENT_BAD_CRC = 0x04, + OSMO_AMRT_TFOP_PRESENT_VALID = 0x06, +}; + +/*! Every AMR frame that passes through the present interworking facility + * passes through a common intermediate format on its way between the + * externally received input format and the ultimate destination format. + * The following struct carries this intermediate format. + * + * Note regarding validity: because this intermediate structure is + * strictly internal to applications performing interworking conversions, + * library functions that take this struct as input are allowed to + * expect all stated validity rules to be observed, and are allowed to + * OSMO_ASSERT on invalid inputs. + */ +struct osmo_amrt_if { + /* Fields in this structure are grouped in logical order, but also + * with an eye toward structure packing optimization: in each logical + * section enum fields appear first, followed by fields of type + * uint8_t, ubit_t or bool. + */ + + /*! Fundamental class of this frame */ + enum osmo_amrt_fc frame_class; + /*! Codec Mode Indication: if this field is valid as marked by + * cmi_valid being true, only modes 0 through 7 are valid here, + * _not_ AMR_SID or AMR_NO_DATA! */ + enum osmo_amr_type cmi; + /*! Codec Mode Request: modes [0,7] and AMR_NO_DATA are potentially + * valid here; the latter can occur if the input was RTP. */ + enum osmo_amr_type cmr; + /*! Is CMI field valid? If frame_class is OSMO_AMRT_FC_NO_DATA, + * then CMI may or may not be valid; all other frame classes + * require valid CMI. */ + bool cmi_valid; + /*! Is CMR field valid? CMR validity is independent of frame class + * and other factors. */ + bool cmr_valid; + + /*! Frame payload bits in s-bit order as used in TRAU and TFO frames, + * also most convenient for conversion from/to speech codec parameter + * arrays, _not_ the d-bit order used on Um interface and in RTP. + * In the case of SID frames, only 35 bits are stored in this array + * as in TRAU frames, not 39 bits as in RTP. In the case of Sid_First, + * we extract 35 s-bits from whichever source format we are receiving + * (TRAU or RTP), but we don't use this array when we emit Sid_First + * in output encoding direction: instead we emit all-1s in TRAU frames + * as required by TS 48.060 & 48.061 and all-0s in RTP as required by + * TS 26.101, which forms the basis for RFC 4867. Exception: when we + * emit RTP output in TW-TS-006 format, we emit whatever Sid_First + * bits we received from TRAU frame source, be they all-1s or not. + */ + ubit_t s_bits[244]; + + /*! Request or Indication Flag as defined in TS 48.060 section + * 5.5.1.2.1, present in TRAU frames and in TW-TS-006 RTP format. */ + ubit_t rif; + /*! Is RIF field valid? */ + bool rif_valid; + + /*! The 3 fields that follow after this validity flag (TA, DTXd + * and TFOE) are valid only if this validity flag is true. */ + bool ta_dtxd_tfoe_valid; + /*! Time Alignment field in TRAU frames, also used to convey TFO + * and handover notifications. */ + uint8_t ta; + /*! DTXd: bit C19 in TRAU-AMR-16k or D63 in TRAU-AMR-8k-No_Speech */ + ubit_t dtxd; + /*! TFOE: bit C20 in TRAU-AMR-16k or D64 in TRAU-AMR-8k-No_Speech */ + ubit_t tfoe; + + /*! All following TFO-related fields are valid only if this Config_Prot + * field is non-zero. */ + enum osmo_amrt_tfo_config_prot config_prot; + /*! Param_Info field is defined in TW-TS-006 section 5.7, + * see enum definition above. */ + enum osmo_amrt_tfo_param_info param_info; + /*! Message_No field is defined in TS 28.062 section C.6.1.3 */ + uint8_t message_no; + /*! Payload bits of TFO parameters as specified in TW-TS-006 + * section 5.8. */ + ubit_t tfo_param_bits[71]; +}; + +/*! Extraction of information content from a received stream of AMR TRAU + * frames is a stateful procedure, requiring state to be maintained + * from one decoded TRAU frame to the next. This structure captures + * the necessary state, as detailed in TW-TS-006 section 6.1.1. + * + * On initialization, set cmi_valid and cmr_valid to false, or zero out + * the whole struct. + */ +struct osmo_amrt_decode_state { + /*! Codec Mode Indication: if this field is valid as marked by + * cmi_valid being true, only modes 0 through 7 are valid here, + * _not_ AMR_SID or AMR_NO_DATA! */ + enum osmo_amr_type cmi; + /*! Codec Mode Request: same range restriction as for CMI. */ + enum osmo_amr_type cmr; + /*! Is CMI field valid? */ + bool cmi_valid; + /*! Is CMR field valid? */ + bool cmr_valid; +}; + +/*! The operation of decoding a received AMR TRAU frame can have three + * outcomes, in addition to filled struct osmo_amrt_if output. + * This enum defines the 3 possible result codes. + */ +enum osmo_amrt_decode_result { + /*! The frame that was received is found to be perfectly valid + * and properly formed - the normal case. + */ + OSMO_AMRT_FRAME_DEC_GOOD, + /*! The frame that was received is bad, not in the sense of Rx radio + * errors or FACCH stealing etc, but in the sense of errors in + * frame coding or CRC etc. If this condition occurs when a TRAU + * or TRAU-like MGW decodes a TRAU-UL frame, UFE needs to be + * indicated back to the BTS/CCU. Note that the frame may have been + * decoded (struct osmo_amrt_if filled with something other than + * OSMO_AMRT_FC_NO_DATA w/o CMI) if the bad CRC was CRC[2..4] + * for speech, rather than CRC1 that covers control bits. + */ + OSMO_AMRT_FRAME_DEC_BAD, + /*! We decoded the frame just fine, but the far end indicated UFE/DFE + * via TRAU-AMR-16k bit C13 or TRAU-AMR-8k-No_Speech bit D7. DFE + * condition in TRAU-AMR-8k mode is important for TRAU or TRAU-like + * MGW implementations. + */ + OSMO_AMRT_FRAME_DEC_REMOTE_BAD, +}; + +enum osmo_amrt_decode_result +osmo_amrt_decode_trau_frame(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool allow_config_prot); + +/*! Generation of AMR TRAU frames from a stream of struct osmo_amrt_if inputs + * (ultimately derived from RTP input in most cases) is likewise a stateful + * procedure, requiring state to be maintained from one encoded TRAU frame + * to the next. This structure captures the necessary state, as detailed in + * TW-TS-006 section 6.3.2. + * + * Initialization: there is no "one size fits all" answer, instead application + * implementors have to understand the problem and decide as appropriate for + * each use case. See TW-TS-006 sections 6.3.3.1 and 6.3.6. + */ +struct osmo_amrt_encode_state { + /*! Codec Mode Indication: valid range is [0,7] for TRAU-AMR-16k + * or [0,4] for TRAU-AMR-8k. In any case, no AMR_SID or AMR_NO_DATA + * can appear in this field. */ + enum osmo_amr_type cmi; + /*! Codec Mode Request: same range restriction as for CMI. */ + enum osmo_amr_type cmr; + /*! Request or Indication Flag. In between frames, this state + * variable remembers RIF of the last emitted frame. */ + ubit_t rif; + /*! What should we emit in DTXd bit? */ + ubit_t dtxd; + /*! What should we emit in TFOE bit? */ + ubit_t tfoe; +}; + +/*! In TRAU frame encoding direction, we split off CRC generation into a + * separate post-processing function, to be called after main AMR TRAU frame + * encoding functions. This split is needed so that applications can modify + * some control bits after encoding: set C5 for TFO, set C6..C11 for timing + * alignment, set UFE etc. However, the exact manner of AMR CRC computation + * depends on speech mode or No_Speech, which cannot always be derived from + * encoded struct osmo_trau_frame alone - consider speech frames with RIF=1. + * Therefore, we pass an additional CRC type code from our main AMR TRAU + * frame encoding functions to the CRC-setter post-processing function. + */ +enum osmo_amrt_crc_type { + OSMO_AMRT_CRC_NONE, + OSMO_AMRT_CRC_16k_NO_SPEECH, + OSMO_AMRT_CRC_16k_SPEECH_MR475, + OSMO_AMRT_CRC_16k_SPEECH_MR515, + OSMO_AMRT_CRC_16k_SPEECH_MR59, + OSMO_AMRT_CRC_16k_SPEECH_MR67, + OSMO_AMRT_CRC_16k_SPEECH_MR74, + OSMO_AMRT_CRC_16k_SPEECH_MR795, + OSMO_AMRT_CRC_16k_SPEECH_MR102, + OSMO_AMRT_CRC_16k_SPEECH_MR122, + OSMO_AMRT_CRC_8k_SID_UPDATE, + OSMO_AMRT_CRC_8k_SPEECH_MR475, + OSMO_AMRT_CRC_8k_SPEECH_MR515, + OSMO_AMRT_CRC_8k_SPEECH_MR59, + OSMO_AMRT_CRC_8k_SPEECH_MR67, + OSMO_AMRT_CRC_8k_SPEECH_MR74, +}; + +enum osmo_amrt_crc_type +osmo_amrt_encode_trau_frame_16k(struct osmo_amrt_encode_state *state, + struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, bool is_tfo); + +/*! In TRAU-AMR-8k output direction, it may be necessary to restrict + * the type of frames that may be emitted: for example, a TRAU-like MGW + * may need to set UFE or obey DFE from the BTS/CCU. The following + * enum controls these restrictions. + */ +enum osmo_amrt_8k_restrict { + /*! No restriction, any TRAU-AMR-8k output is allowed */ + OSMO_AMRT_OUT8k_NO_RESTRICT, + /*! Restrict to either No_Speech or lower modes */ + OSMO_AMRT_OUT8k_REQUIRE_LOW, + /*! No_Speech output is required */ + OSMO_AMRT_OUT8k_REQUIRE_NO_SPEECH, +}; + +enum osmo_amrt_crc_type +osmo_amrt_encode_trau_frame_8k(struct osmo_amrt_encode_state *state, + struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, bool is_tfo, + enum osmo_amrt_8k_restrict restr); + +int osmo_amrt_set_trau_crc(struct osmo_trau_frame *tf, + enum osmo_amrt_crc_type crc_type); + +/*! On RTP side we support 3 different formats: RFC 4867 BWE, RFC 4867 OA + * and TW-TS-006 OAX (OA extended). This enum captures the necessary + * format selector. + */ +enum osmo_amrt_rtp_format { + OSMO_AMRT_RTP_FMT_BWE, + OSMO_AMRT_RTP_FMT_OA, + OSMO_AMRT_RTP_FMT_OAX, +}; + +int osmo_amrt_decode_rtp(struct osmo_amrt_if *fr, const uint8_t *rtp_pl, + unsigned rtp_pl_len, enum osmo_amrt_rtp_format fmt); +int osmo_amrt_encode_rtp(uint8_t *pl_buf, unsigned pl_buf_size, + const struct osmo_amrt_if *fr, + enum osmo_amrt_rtp_format fmt); + +/*! @} */ diff --git a/src/Makefile.am b/src/Makefile.am index b30ebfc..c3e39b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,7 +52,9 @@ -no-undefined \ $(NULL) libosmotrau_la_LIBADD = $(COMMONLIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS) -libosmotrau_la_SOURCES = trau/csd_ra2.c \ +libosmotrau_la_SOURCES = trau/amr_rtp.c \ + trau/amr_trau.c \ + trau/csd_ra2.c \ trau/csd_v110.c \ trau/raa_prime_decode.c \ trau/raa_prime_encode.c \ diff --git a/src/trau/amr_rtp.c b/src/trau/amr_rtp.c new file mode 100644 index 0000000..8ca4d07 --- /dev/null +++ b/src/trau/amr_rtp.c @@ -0,0 +1,441 @@ +/* + * AMR TRAU interworking facility: encoding and decoding of RTP formats. + * + * This code was contributed to Osmocom Cellular Network Infrastructure + * project by Mother Mychaela N. Falconia of Themyscira Wireless. + * Mother Mychaela's contributions are NOT subject to copyright: + * no rights reserved, all rights relinquished. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/utils.h> +#include <osmocom/codec/codec.h> +#include <osmocom/trau/amr_trau.h> + +/*! \addgroup amr_trau + * @{ + */ + +static void decode_twts006_extras(struct osmo_amrt_if *fr, + const uint8_t *rtp_pl, unsigned rtp_pl_len, + unsigned num_d_bits) +{ + const uint8_t *ptr = rtp_pl + 2 + ((num_d_bits + 7) / 8); + const uint8_t *endp = rtp_pl + rtp_pl_len; + uint8_t datum; + + /* simple flags which don't require extra octets */ + if (rtp_pl[0] & 0x08) { /* RIF presence flag */ + fr->rif = (rtp_pl[0] & 0x04) >> 2; + fr->rif_valid = true; + } else { + fr->rif_valid = false; + } + if ((fr->frame_class == OSMO_AMRT_FC_SPEECH_GOOD) && (rtp_pl[1] & 0x02)) + fr->frame_class = OSMO_AMRT_FC_SPEECH_DEGRADED; + + /* set no further info in case of early return */ + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + + /* decode extra octets in the order given in TW-TS-006 section 5.3 */ + if ((fr->frame_class == OSMO_AMRT_FC_NO_DATA) && (rtp_pl[1] & 0x02)) { + if (ptr >= endp) + return; + datum = *ptr++; + fr->cmi = datum & 0x07; + fr->cmi_valid = true; + if (datum & 0x80) + fr->frame_class = OSMO_AMRT_FC_ONSET; + } + if (rtp_pl[0] & 0x02) { /* TA+DTXd+TFOE octet */ + if (ptr >= endp) + return; + datum = *ptr++; + fr->ta = (datum & 0xFC) >> 2; + fr->dtxd = (datum & 0x02) >> 1; + fr->tfoe = (datum & 0x01) >> 0; + fr->ta_dtxd_tfoe_valid = true; + } + + /* Config_Prot and TFO parameters come last */ + if (!(rtp_pl[0] & 0x01)) + return; + if (ptr >= endp) + return; + datum = *ptr++; + fr->config_prot = (datum & 0xE0) >> 5; + fr->message_no = (datum & 0x18) >> 3; + fr->param_info = (datum & 0x06); /* no shift! */ + if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON) + return; /* can't have params for No_Con */ + if (fr->param_info != OSMO_AMRT_TFOP_PRESENT_VALID) + return; /* no params included */ + if ((endp - ptr) < 9) { + /* Received bogon: cut off before TFO parameters! + * To avoid messing up TFO protocol by dropping params + * from a message that requires them, we clear out + * the config message itself. + */ + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + return; + } + osmo_pbit2ubit(fr->tfo_param_bits, ptr, 71); +} + +/*! Decode received AMR RTP payload for TRAU interworking + * + * \param[out] fr Intermediate representation of the decoded frame, primary + * output from this function. + * \param[in] rtp_pl Received RTP payload. + * \param[in] rtp_pl_len Length of \ref rtp_pl. + * \param[in] fmt Selection of which AMR RTP payload format is to be decoded. + * \returns 0 if the payload was decoded successfully, or negative if this + * received payload is invalid. + * + * This function always fills out struct osmo_amrt_if fully, setting all + * required fields and all validity flags for conditional fields, even when + * the received payload is deemed invalid. Therefore, no pre-initialization + * is needed from the caller, nor does the caller need to do any post-filling + * except for special application requirements. + */ +int osmo_amrt_decode_rtp(struct osmo_amrt_if *fr, const uint8_t *rtp_pl, + unsigned rtp_pl_len, enum osmo_amrt_rtp_format fmt) +{ + uint8_t ft; + unsigned num_d_bits, total_bits, d_offset; + ubit_t q, d_bits[244 + 2]; + + /* The minimum valid AMR RTP payload length is 2 octets, + * for both BWE and OA formats. */ + if (rtp_pl_len < 2) + goto out_bogon; + + /* CMR bits are in the same position in both BWE and OA - + * let's validate received CMR now. */ + fr->cmr = rtp_pl[0] >> 4; + if (fr->cmr > AMR_12_2 && fr->cmr != AMR_NO_DATA) + goto out_bogon; + fr->cmr_valid = true; + + /* Now consider FT, and while we handle BWE vs OA to do so, + * also enforce F=0 requirement and extract Q bit. */ + if (fmt == OSMO_AMRT_RTP_FMT_BWE) { + if (rtp_pl[0] & 0x08) /* F bit */ + goto out_bogon; + ft = ((rtp_pl[0] & 0x07) << 1) | ((rtp_pl[1] & 0x80) >> 7); + q = (rtp_pl[1] & 0x40) >> 6; + } else { + if (rtp_pl[1] & 0x80) /* F bit */ + goto out_bogon; + ft = (rtp_pl[1] & 0x78) >> 3; + q = (rtp_pl[1] & 0x04) >> 2; + } + switch (ft) { + case AMR_NO_DATA: + num_d_bits = 0; + break; + case AMR_SID: + num_d_bits = 39; + break; + default: + if (ft > AMR_12_2) + goto out_bogon; + num_d_bits = gsm690_bitlength[ft]; + break; + } + + /* Now that we know how many data bits we need for the FT we got, + * we can check whether or not the received payload is long enough. + */ + if (fmt == OSMO_AMRT_RTP_FMT_BWE) + total_bits = num_d_bits + 10; + else + total_bits = num_d_bits + 16; + if (rtp_pl_len * 8 < total_bits) + goto out_bogon; + + /* Now extract all data bits from the received payload */ + if (ft != AMR_NO_DATA) { + if (fmt == OSMO_AMRT_RTP_FMT_BWE) { + osmo_pbit2ubit(d_bits, rtp_pl + 1, num_d_bits + 2); + d_offset = 2; + } else { + osmo_pbit2ubit(d_bits, rtp_pl + 2, num_d_bits); + d_offset = 0; + } + } + + /* Now we can do the actual conversion from RTP paradigm to our + * intermediate representation that is closer to TRAU paradigm. + */ + switch (ft) { + case AMR_NO_DATA: + fr->frame_class = OSMO_AMRT_FC_NO_DATA; + fr->cmi_valid = false; + break; + case AMR_SID: + if (q) { + if (d_bits[d_offset + 35]) + fr->frame_class = OSMO_AMRT_FC_SID_UPDATE; + else + fr->frame_class = OSMO_AMRT_FC_SID_FIRST; + } else { + fr->frame_class = OSMO_AMRT_FC_SID_BAD; + } + memcpy(fr->s_bits, d_bits + d_offset, 35); + /* What idiot decided to flip the order of CMI bits? */ + fr->cmi = (d_bits[d_offset+36] << 0) | + (d_bits[d_offset+37] << 1) | + (d_bits[d_offset+38] << 2); + fr->cmi_valid = true; + break; + default: + if (q) + fr->frame_class = OSMO_AMRT_FC_SPEECH_GOOD; + else + fr->frame_class = OSMO_AMRT_FC_SPEECH_BAD; + fr->cmi = ft; + fr->cmi_valid = true; + osmo_amr_d_to_s(fr->s_bits, d_bits + d_offset, num_d_bits, + fr->cmi); + break; + } + + if (fmt == OSMO_AMRT_RTP_FMT_OAX) { + decode_twts006_extras(fr, rtp_pl, rtp_pl_len, num_d_bits); + } else { + /* no extra goodies in RFC 4867 formats */ + fr->rif_valid = false; + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + } + return 0; + +out_bogon: + fr->frame_class = OSMO_AMRT_FC_NO_DATA; + fr->cmi_valid = false; + fr->cmr_valid = false; + fr->rif_valid = false; + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + return -EINVAL; +} + +/*! Encode AMR frame into RTP payload + * + * \param[out] pl_buf Caller-provided buffer for generated RTP payload. + * \param[in] pl_buf_size Available space in \ref pl_buf. + * \param[in] fr AMR frame to encode. + * \param[in] fmt Selection of which AMR RTP payload format is to be emitted. + * \returns number of bytes of generated payload if successful, + * or negative on errors. + */ +int osmo_amrt_encode_rtp(uint8_t *pl_buf, unsigned pl_buf_size, + const struct osmo_amrt_if *fr, + enum osmo_amrt_rtp_format fmt) +{ + unsigned num_d_bits, base_bits, base_bytes, req_bytes; + uint8_t cmr, ft; + ubit_t q, d_bits[244 + 10]; + unsigned d_offset; + uint8_t *outptr = pl_buf; + + /* Begin by figuring out how much space we need */ + switch (fr->frame_class) { + case OSMO_AMRT_FC_SPEECH_GOOD: + case OSMO_AMRT_FC_SPEECH_DEGRADED: + case OSMO_AMRT_FC_SPEECH_BAD: + num_d_bits = gsm690_bitlength[fr->cmi]; + break; + case OSMO_AMRT_FC_SID_FIRST: + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + num_d_bits = 39; + break; + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + num_d_bits = 0; + break; + default: + return -EINVAL; + } + if (fmt == OSMO_AMRT_RTP_FMT_BWE) + base_bits = num_d_bits + 10; + else + base_bits = num_d_bits + 16; + base_bytes = (base_bits + 7) / 8; + req_bytes = base_bytes; + + /* additional space requirements for TW-TS-006 output */ + if (fmt == OSMO_AMRT_RTP_FMT_OAX) { + /* FT15 extension octet */ + switch (fr->frame_class) { + case OSMO_AMRT_FC_NO_DATA: + if (fr->cmi_valid) + req_bytes++; + break; + case OSMO_AMRT_FC_ONSET: + req_bytes++; + break; + default: + break; + } + /* TA+DTXd+TFOE octet */ + if (fr->ta_dtxd_tfoe_valid) + req_bytes++; + /* Config_Prot and TFO parameters */ + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) { + req_bytes++; + if (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID) + req_bytes += 9; + } + } + + if (pl_buf_size < req_bytes) + return -ENOSPC; + + /* now generate our output */ + if (fr->cmr_valid) + cmr = fr->cmr; + else + cmr = AMR_NO_DATA; + switch (fr->frame_class) { + case OSMO_AMRT_FC_SPEECH_GOOD: + case OSMO_AMRT_FC_SPEECH_DEGRADED: + ft = fr->cmi; + q = 1; + break; + case OSMO_AMRT_FC_SPEECH_BAD: + ft = fr->cmi; + q = 0; + break; + case OSMO_AMRT_FC_SID_FIRST: + case OSMO_AMRT_FC_SID_UPDATE: + ft = AMR_SID; + q = 1; + break; + case OSMO_AMRT_FC_SID_BAD: + ft = AMR_SID; + q = 0; + break; + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + ft = AMR_NO_DATA; + q = 1; + break; + default: + OSMO_ASSERT(0); + } + + if (fmt == OSMO_AMRT_RTP_FMT_BWE) { + d_bits[0] = (cmr >> 3) & 1; + d_bits[1] = (cmr >> 2) & 1; + d_bits[2] = (cmr >> 1) & 1; + d_bits[3] = (cmr >> 0) & 1; + d_bits[4] = 0; /* F bit */ + d_bits[5] = (ft >> 3) & 1; + d_bits[6] = (ft >> 2) & 1; + d_bits[7] = (ft >> 1) & 1; + d_bits[8] = (ft >> 0) & 1; + d_bits[9] = q; + d_offset = 10; + } else { + *outptr++ = (cmr << 4); + *outptr++ = (ft << 3) | (q << 2); + d_offset = 0; + } + + switch (fr->frame_class) { + case OSMO_AMRT_FC_SPEECH_GOOD: + case OSMO_AMRT_FC_SPEECH_DEGRADED: + case OSMO_AMRT_FC_SPEECH_BAD: + osmo_amr_s_to_d(d_bits + d_offset, fr->s_bits, num_d_bits, + fr->cmi); + break; + case OSMO_AMRT_FC_SID_FIRST: + /* What is the correct s-bits filler for SID_FIRST in RTP? + * RFC 4867 defers to 3GPP TS 26.101 on all such matters, + * and the latter spec says (in section 4.2.3) that these + * filler bits shall be 0 - the opposite of all-1s filler + * in TRAU frames! Our approach: we obey this indirect + * stipulation of RFC 4867 when configured to emit + * IETF-compliant (and late-3GPP-compliant) BWE or OA output; + * but if we are configured to emit TW-TS-006 format, + * we emit whatever s-bits we got, for better network debug + * visibility. */ + if (fmt == OSMO_AMRT_RTP_FMT_OAX) + memcpy(d_bits + d_offset, fr->s_bits, 35); + else + memset(d_bits + d_offset, 0, 35); + d_bits[d_offset + 35] = 0; /* STI */ + d_bits[d_offset + 36] = (fr->cmi >> 0) & 1; + d_bits[d_offset + 37] = (fr->cmi >> 1) & 1; + d_bits[d_offset + 38] = (fr->cmi >> 2) & 1; + break; + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + memcpy(d_bits + d_offset, fr->s_bits, 35); + d_bits[d_offset + 35] = 1; /* STI */ + d_bits[d_offset + 36] = (fr->cmi >> 0) & 1; + d_bits[d_offset + 37] = (fr->cmi >> 1) & 1; + d_bits[d_offset + 38] = (fr->cmi >> 2) & 1; + break; + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + break; + default: + OSMO_ASSERT(0); + } + outptr += osmo_ubit2pbit(outptr, d_bits, num_d_bits + d_offset); + + if (fmt != OSMO_AMRT_RTP_FMT_OAX) + return outptr - pl_buf; + + /* now add TW-TS-006 extensions as needed */ + if (fr->rif_valid) + pl_buf[0] |= 0x08 | (fr->rif << 2); + + /* FT15 extension octet */ + switch (fr->frame_class) { + case OSMO_AMRT_FC_NO_DATA: + if (fr->cmi_valid) { + pl_buf[1] |= 0x02; + *outptr++ = fr->cmi; + } + break; + case OSMO_AMRT_FC_ONSET: + pl_buf[1] |= 0x02; + *outptr++ = fr->cmi | 0x80; + break; + case OSMO_AMRT_FC_SPEECH_DEGRADED: + pl_buf[1] |= 0x02; + break; + default: + break; + } + + /* TA+DTXd+TFOE octet */ + if (fr->ta_dtxd_tfoe_valid) { + pl_buf[0] |= 0x02; + *outptr++ = (fr->ta << 2) | (fr->dtxd << 1) | (fr->tfoe << 0); + } + + /* Config_Prot and TFO parameters come last */ + if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON) + return outptr - pl_buf; + + pl_buf[0] |= 0x01; + *outptr++ = (fr->config_prot << 5) | (fr->message_no << 3) | + fr->param_info; + if (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID) + outptr += osmo_ubit2pbit(outptr, fr->tfo_param_bits, 71); + return outptr - pl_buf; +} + +/*! @} */ diff --git a/src/trau/amr_trau.c b/src/trau/amr_trau.c new file mode 100644 index 0000000..aee1b2c --- /dev/null +++ b/src/trau/amr_trau.c @@ -0,0 +1,2178 @@ +/* + * AMR TRAU interworking facility: encoding and decoding of TRAU frames. + * + * This code was contributed to Osmocom Cellular Network Infrastructure + * project by Mother Mychaela N. Falconia of Themyscira Wireless. + * Mother Mychaela's contributions are NOT subject to copyright: + * no rights reserved, all rights relinquished. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/crc8gen.h> +#include <osmocom/core/utils.h> +#include <osmocom/codec/codec.h> +#include <osmocom/trau/amr_trau.h> +#include <osmocom/trau/trau_frame.h> + +/*! \addgroup amr_trau + * @{ + */ + +/* + * AMR TRAU parity (same as EFR and HR) + * + * g(x) = x^3 + x^1 + 1 + */ +static const struct osmo_crc8gen_code gsm0860_amr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +/* Please refer to TS 28.062 Table C.6.1.5-1 for the details of how TFO + * config parameters are mapped to different TRAU frame types. The parameters + * are broken down into groups A, B and C, and that last block C is missing + * in 16k frames for MR102 and 8k frames for SID. When block C is missing + * from the transmitted frame, parameters in that block have to assume + * fixed values listed in that table. The following datum gives these fixed + * parameters, in the form in which we need them for TW-TS-006. + */ +static const ubit_t tfo_blockC_fixed_params[26] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* SCS_2 */ + 0, /* OM_2 */ + 1, 0, 0, /* MACS_2 */ + 0, /* ATVN_2 */ + 1, 1, 1, 1, 1, 1, 1, 1, /* SCS_3 */ + 0, /* OM_3 */ + 1, 0, 0, /* MACS_3 */ + 0, /* ATVN_3 */ +}; + +/*** TRAU frame decoding direction ***/ + +static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num) +{ + int i; + uint32_t ret = 0; + + for (i = offset; i < offset + num; i++) { + ret = ret << 1; + if (bitbuf[i]) + ret |= 1; + } + return ret; +} + +/* This helper function is called when TRAU frame decoding ran into + * an unrecoverable error, such that the received frame has to be + * declared totally bad with no useful information content. In particular, + * if CRC1 covering control bits is bad, we cannot trust _any_ info + * from those control bits! + */ +static void decode_handle_error(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr) +{ + state->cmi_valid = false; + state->cmr_valid = false; + + fr->frame_class = OSMO_AMRT_FC_NO_DATA; + fr->cmi_valid = false; + fr->cmr_valid = false; + fr->rif_valid = false; + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; +} + +static int decode_16k_no_speech(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf) +{ + ubit_t crc_collect[25 + 61]; + int crc_stat; + + /* Start with CRC verification: if CRC is bad, there is no point + * in decoding anything else. */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 31, 61); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, crc_collect, + 25 + 61, tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + + /* CRC is good - now decode the rest */ + fr->frame_class = get_bits(tf->d_bits, 31, 3); + switch (fr->frame_class) { + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + /* no need to save s-bits */ + break; + case OSMO_AMRT_FC_SID_FIRST: + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + /* For all SID variants, we need to extract s-bits, + * to be passed into RTP - even for Sid_First, where + * these bits are expected to be all ones. */ + memcpy(fr->s_bits, tf->d_bits + 57, 35); + break; + default: + /* invalid No_Speech_Classification code */ + return -EINVAL; + } + + /* We are past all potential error cases: finish the work. */ + fr->cmi = get_bits(tf->d_bits, 34, 3); + fr->cmr = get_bits(tf->d_bits, 37, 3); + fr->cmi_valid = true; + fr->cmr_valid = true; + + state->cmi = fr->cmi; + state->cmr = fr->cmr; + state->cmi_valid = true; + state->cmr_valid = true; + + return 0; +} + +static int decode_16k_speech_per_mode(struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool *bad_crc_234, bool *bad_mr102_crc4) +{ + ubit_t crc_collect[82]; /* largest in mode 6 */ + int crc_stat; + + /* For each mode we follow the same logic: + * + * - Check CRC1 first: if it is bad, we cannot trust control bits + * and the frame is totally bad. + * + * - Accept and extract s-bits from the frame. + * + * - Check the other 3 subframe CRCs. If any of them is bad, + * we change frame classification to Speech_Bad, but we don't + * have to toss the entire frame. + * + * - For MR102 only, if CRC4 is bad, we have to note this detail + * for potential impact on TFO config parameters. + */ + switch (fr->cmi) { + case AMR_4_75: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 44, 16); + memcpy(crc_collect + 41, tf->d_bits + 61, 2); + memcpy(crc_collect + 43, tf->d_bits + 64, 9); + memcpy(crc_collect + 52, tf->d_bits + 88, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 56, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 44, 48); + memcpy(fr->s_bits + 48, tf->d_bits + 95, 13); + memcpy(fr->s_bits + 61, tf->d_bits + 111, 21); + memcpy(fr->s_bits + 82, tf->d_bits + 135, 13); + /* check CRC2 */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + tf->d_bits + 95, 2, + tf->d_bits + 108); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 111, 2); + memcpy(crc_collect + 2, tf->d_bits + 128, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 6, + tf->d_bits + 132); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + tf->d_bits + 135, 2, + tf->d_bits + 148); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_5_15: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 46, 16); + memcpy(crc_collect + 41, tf->d_bits + 64, 11); + memcpy(crc_collect + 52, tf->d_bits + 87, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 57, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 46, 46); + memcpy(fr->s_bits + 46, tf->d_bits + 95, 19); + memcpy(fr->s_bits + 65, tf->d_bits + 117, 19); + memcpy(fr->s_bits + 84, tf->d_bits + 139, 19); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 2); + memcpy(crc_collect + 2, tf->d_bits + 109, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 114); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 117, 2); + memcpy(crc_collect + 2, tf->d_bits + 131, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 136); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 139, 2); + memcpy(crc_collect + 2, tf->d_bits + 153, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 158); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_5_90: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 41, 17); + memcpy(crc_collect + 42, tf->d_bits + 67, 8); + memcpy(crc_collect + 50, tf->d_bits + 88, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 54, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 41, 51); + memcpy(fr->s_bits + 51, tf->d_bits + 95, 21); + memcpy(fr->s_bits + 72, tf->d_bits + 119, 25); + memcpy(fr->s_bits + 97, tf->d_bits + 147, 21); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 3); + memcpy(crc_collect + 3, tf->d_bits + 112, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 116); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 119, 8); + memcpy(crc_collect + 8, tf->d_bits + 140, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 12, + tf->d_bits + 144); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 147, 3); + memcpy(crc_collect + 3, tf->d_bits + 164, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 168); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_6_70: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 37, 17); + crc_collect[42] = tf->d_bits[56]; + crc_collect[43] = tf->d_bits[60]; + memcpy(crc_collect + 44, tf->d_bits + 63, 8); + memcpy(crc_collect + 52, tf->d_bits + 85, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 57, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 37, 55); + memcpy(fr->s_bits + 55, tf->d_bits + 95, 25); + memcpy(fr->s_bits + 80, tf->d_bits + 123, 29); + memcpy(fr->s_bits + 109, tf->d_bits + 155, 25); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 113, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 9, + tf->d_bits + 120); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 123, 8); + memcpy(crc_collect + 8, tf->d_bits + 145, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 13, + tf->d_bits + 152); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 155, 4); + memcpy(crc_collect + 4, tf->d_bits + 173, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 9, + tf->d_bits + 180); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_7_40: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 34, 20); + memcpy(crc_collect + 45, tf->d_bits + 55, 3); + memcpy(crc_collect + 48, tf->d_bits + 60, 6); + memcpy(crc_collect + 54, tf->d_bits + 85, 2); + memcpy(crc_collect + 56, tf->d_bits + 88, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 59, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 34, 58); + memcpy(fr->s_bits + 58, tf->d_bits + 95, 29); + memcpy(fr->s_bits + 87, tf->d_bits + 127, 32); + memcpy(fr->s_bits + 119, tf->d_bits + 162, 29); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 3); + memcpy(crc_collect + 3, tf->d_bits + 117, 2); + memcpy(crc_collect + 5, tf->d_bits + 120, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 8, + tf->d_bits + 124); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 127, 6); + memcpy(crc_collect + 6, tf->d_bits + 152, 2); + memcpy(crc_collect + 8, tf->d_bits + 155, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 11, + tf->d_bits + 159); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 162, 3); + memcpy(crc_collect + 3, tf->d_bits + 184, 2); + memcpy(crc_collect + 5, tf->d_bits + 187, 2); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 191); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_7_95: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 31, 35); + memcpy(crc_collect + 60, tf->d_bits + 83, 2); + crc_collect[62] = tf->d_bits[87]; + crc_collect[63] = tf->d_bits[90]; + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 64, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 31, 61); + memcpy(fr->s_bits + 61, tf->d_bits + 95, 32); + memcpy(fr->s_bits + 93, tf->d_bits + 130, 34); + memcpy(fr->s_bits + 127, tf->d_bits + 167, 32); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 118, 2); + memcpy(crc_collect + 6, tf->d_bits + 122, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 10, + tf->d_bits + 127); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 130, 8); + memcpy(crc_collect + 8, tf->d_bits + 155, 2); + memcpy(crc_collect + 10, tf->d_bits + 159, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 14, + tf->d_bits + 164); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 167, 4); + memcpy(crc_collect + 4, tf->d_bits + 190, 2); + memcpy(crc_collect + 6, tf->d_bits + 194, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 10, + tf->d_bits + 199); + if (crc_stat) + *bad_crc_234 = true; + break; + case AMR_10_2: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits, 45); + memcpy(crc_collect + 70, tf->d_bits + 46, 8); + memcpy(crc_collect + 78, tf->d_bits + 85, 2); + memcpy(crc_collect + 80, tf->d_bits + 88, 2); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 82, + tf->d_bits + 92); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 20, 72); + memcpy(fr->s_bits + 72, tf->d_bits + 95, 43); + memcpy(fr->s_bits + 115, tf->d_bits + 141, 46); + memcpy(fr->s_bits + 161, tf->d_bits + 190, 43); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 131, 2); + memcpy(crc_collect + 6, tf->d_bits + 134, 2); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 8, + tf->d_bits + 138); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 141, 8); + memcpy(crc_collect + 8, tf->d_bits + 180, 2); + memcpy(crc_collect + 10, tf->d_bits + 183, 2); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 12, + tf->d_bits + 187); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 190, 4); + memcpy(crc_collect + 4, tf->d_bits + 226, 2); + memcpy(crc_collect + 6, tf->d_bits + 229, 2); + memcpy(crc_collect + 8, tf->d_bits + 233, 20); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 28, + tf->d_bits + 253); + if (crc_stat) { + *bad_crc_234 = true; + *bad_mr102_crc4 = true; + } + break; + case AMR_12_2: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits, 29); + memcpy(crc_collect + 54, tf->d_bits + 38, 12); + memcpy(crc_collect + 66, tf->d_bits + 86, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 69, + tf->d_bits + 91); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits, 91); + memcpy(fr->s_bits + 91, tf->d_bits + 94, 50); + memcpy(fr->s_bits + 141, tf->d_bits + 147, 53); + memcpy(fr->s_bits + 194, tf->d_bits + 203, 50); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 94, 9); + memcpy(crc_collect + 9, tf->d_bits + 139, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 12, + tf->d_bits + 144); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 147, 12); + memcpy(crc_collect + 12, tf->d_bits + 195, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 15, + tf->d_bits + 200); + if (crc_stat) + *bad_crc_234 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 203, 5); + memcpy(crc_collect + 5, tf->d_bits + 209, 3); + memcpy(crc_collect + 8, tf->d_bits + 248, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 11, + tf->d_bits + 253); + if (crc_stat) + *bad_crc_234 = true; + break; + default: + OSMO_ASSERT(0); + } + + fr->cmi_valid = true; + return 0; +} + +static int decode_16k_speech(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool *bad_crc_234, bool *bad_mr102_crc4) +{ + int rc; + + /* The bulk of decoding and CRC verification depends on RIF */ + if (fr->rif) { + /* CMR frame - saved CMI is required */ + if (!state->cmi_valid) + return -EINVAL; + fr->cmi = state->cmi; + rc = decode_16k_speech_per_mode(fr, tf, bad_crc_234, + bad_mr102_crc4); + if (rc < 0) + return rc; + /* output and state change specific to CMR frame */ + fr->cmr = get_bits(tf->c_bits, 22, 3); + fr->cmr_valid = true; + state->cmr = fr->cmr; + state->cmr_valid = true; + state->cmi_valid = false; + } else { + /* CMI frame */ + fr->cmi = get_bits(tf->c_bits, 22, 3); + rc = decode_16k_speech_per_mode(fr, tf, bad_crc_234, + bad_mr102_crc4); + if (rc < 0) + return rc; + /* output and state change specific to CMI frame */ + if (state->cmr_valid) { + fr->cmr = state->cmr; + fr->cmr_valid = true; + } else { + fr->cmr_valid = false; + } + state->cmi = fr->cmi; + state->cmi_valid = true; + state->cmr_valid = false; + } + + if (*bad_crc_234) + fr->frame_class = OSMO_AMRT_FC_SPEECH_BAD; + else + fr->frame_class = get_bits(tf->c_bits, 20, 2) << 3; + return 0; +} + +static void decode_16k_tfo_mr102(struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf) +{ + /* CRC has already been checked as part of speech frame decoding; + * now we just need to extract the parameters. However, only a + * reduced set of params is transmitted in this mode; for the + * missing ones, we fill in fixed values as prescribed in TS 28.062 + * Table C.6.1.5-1. */ + memcpy(fr->tfo_param_bits, tf->d_bits, 20); + memset(fr->tfo_param_bits + 20, 0, 5); + memcpy(fr->tfo_param_bits + 25, tf->d_bits + 233, 20); + memcpy(fr->tfo_param_bits + 45, tfo_blockC_fixed_params, 26); + fr->param_info = OSMO_AMRT_TFOP_PRESENT_VALID; +} + +static void decode_16k_tfo(struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool bad_mr102_crc4) +{ + int crc_stat; + + fr->config_prot = get_bits(tf->c_bits, 13, 3); + if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON) + return; + fr->message_no = get_bits(tf->c_bits, 16, 2); + + /* Is it a speech frame? To understand the logic that follows, + * please refer to TS 28.062 Table C.6.1.5-1. */ + if (osmo_amrt_fc_is_speech(fr->frame_class)) { + switch (fr->cmi) { + case AMR_12_2: + fr->param_info = OSMO_AMRT_TFOP_ABSENT_NO_ROOM; + return; + case AMR_10_2: + if (bad_mr102_crc4) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + decode_16k_tfo_mr102(fr, tf); + return; + default: + break; + } + } + + /* We have to check all 3 TFO-specific CRCs */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits, 28, + tf->d_bits + 28); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 233, + 20, tf->d_bits + 253); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 202, + 28, tf->d_bits + 230); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + + /* CRCs are good - extract the parameters */ + memcpy(fr->tfo_param_bits, tf->d_bits, 25); + memcpy(fr->tfo_param_bits + 25, tf->d_bits + 233, 20); + memcpy(fr->tfo_param_bits + 45, tf->d_bits + 202, 26); + fr->param_info = OSMO_AMRT_TFOP_PRESENT_VALID; +} + +static enum osmo_amrt_decode_result +decode_16k(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, bool allow_config_prot) +{ + bool bad_crc_234 = false, bad_mr102_crc4 = false; + int rc; + + /* Unless we have to kill it later because of CRC error, RIF is always + * valid and readily extracted. */ + fr->rif = tf->c_bits[11]; + fr->rif_valid = true; + + /* speech vs No_Speech frames require different approach to decoding */ + if (tf->c_bits[20] || tf->c_bits[21]) + rc = decode_16k_speech(state, fr, tf, &bad_crc_234, + &bad_mr102_crc4); + else + rc = decode_16k_no_speech(state, fr, tf); + if (rc < 0) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + + /* CRC1 has been verified as part of decoding, and we know that all + * control bits are valid. */ + fr->ta = get_bits(tf->c_bits, 5, 6); + fr->dtxd = tf->c_bits[18]; + fr->tfoe = tf->c_bits[19]; + fr->ta_dtxd_tfoe_valid = true; + + if (allow_config_prot) + decode_16k_tfo(fr, tf, bad_mr102_crc4); + else + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + + if (bad_crc_234) + return OSMO_AMRT_FRAME_DEC_BAD; + if (!tf->c_bits[12]) + return OSMO_AMRT_FRAME_DEC_REMOTE_BAD; + return OSMO_AMRT_FRAME_DEC_GOOD; +} + +static void decode_8k_tfo(struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, bool is_sid) +{ + int crc_stat; + + fr->config_prot = get_bits(tf->d_bits, 54, 3); + if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON) + return; + fr->message_no = get_bits(tf->d_bits, 57, 2); + + /* CRC_A and CRC_B exist whether we are SID or not */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 64, + 28, tf->d_bits + 92); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, tf->d_bits + 95, + 20, tf->d_bits + 115); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + /* CRC_C exists only if our No_Speech type is not SID */ + if (!is_sid) { + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + tf->d_bits + 16, 28, + tf->d_bits + 44); + if (crc_stat) { + fr->param_info = OSMO_AMRT_TFOP_ABSENT_BAD_CRC; + return; + } + } + + /* CRCs are good - extract the parameters */ + memcpy(fr->tfo_param_bits, tf->d_bits + 64, 25); + memcpy(fr->tfo_param_bits + 25, tf->d_bits + 95, 20); + if (is_sid) + memcpy(fr->tfo_param_bits + 45, tfo_blockC_fixed_params, 26); + else + memcpy(fr->tfo_param_bits + 45, tf->d_bits + 16, 26); + fr->param_info = OSMO_AMRT_TFOP_PRESENT_VALID; +} + +static enum osmo_amrt_decode_result +decode_8k_low_no_speech(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool allow_config_prot) +{ + ubit_t crc_collect[56]; + int crc_stat; + + /* The definition of TRAU-AMR-8k frame format in TS 48.061 has this + * design defect: 8k No_Speech frames include CRC only if the + * No_Speech_Classification is either Sid_Update or Sid_Bad, but not + * for Sid_First, No_Data or Onset. In contrast, all TRAU-AMR-16k + * frame types feature CRC protection. I call this aspect of TS 48.061 + * (the half-rate spec) a design defect because it would not have cost + * anything to enable CRC computation and checking for all No_Speech + * frame types, whether the 35-bit SID parameters field carries those + * or all-1s filler. But the spec is what it is, and other existing + * implementations, whether in E1 BTS or in TFO-interoperating + * transcoders anywhere on the global PSTN, emit Sid_First and No_Data + * frames without CRC. If we were to check CRC upfront for all 8k + * No_Speech frames, we would reject those spec-valid frames - hence + * we have no choice but to accept them without any CRC protection + * for control bits. + */ + + /* If C1,C2 are anything other than 1,0 we got a bogon - + * just like bad CRC, if we had one which we could always check. */ + if (tf->c_bits[0] != 1 || tf->c_bits[1] != 0) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + /* With C1,C2 good, C3 is RIF */ + fr->rif = tf->c_bits[2]; + fr->rif_valid = true; + + fr->frame_class = get_bits(tf->d_bits, 7, 3); + switch (fr->frame_class) { + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + if (allow_config_prot) + decode_8k_tfo(fr, tf, false); + else + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + break; + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + /* The only 8k No_Speech frames that have CRC! */ + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits, 51); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 56, + tf->d_bits + 51); + if (crc_stat) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + memcpy(fr->s_bits, tf->d_bits + 16, 35); + if (allow_config_prot) + decode_8k_tfo(fr, tf, true); + else + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + break; + case OSMO_AMRT_FC_SID_FIRST: + if (allow_config_prot) + decode_8k_tfo(fr, tf, false); + else + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + /* TS 48.061 section 5.2.4.2.1 allows SID bits to be replaced + * with TFO parameters in the case of Sid_First. In our + * "normal" Sid_First handling (8k frame format and no TFO + * parameters, or always in 16k format), we prefer to extract + * the dummy 35 bits of what would have been SID parameters + * and make them available for debug etc - but if we know + * that they have been replaced with TFO parameters, + * we fill in dummy all-ones here. */ + if (fr->config_prot == OSMO_AMRT_CFG_PROT_NO_CON) + memcpy(fr->s_bits, tf->d_bits + 16, 35); + else + memset(fr->s_bits, 1, 35); + break; + default: + /* invalid No_Speech_Classification code */ + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + + /* We are past all potential error cases: finish the work. */ + fr->cmi = get_bits(tf->d_bits, 10, 3); + fr->cmr = get_bits(tf->d_bits, 13, 3); + fr->cmi_valid = true; + fr->cmr_valid = true; + fr->ta = get_bits(tf->d_bits, 0, 6); + fr->dtxd = tf->d_bits[62]; + fr->tfoe = tf->d_bits[63]; + fr->ta_dtxd_tfoe_valid = true; + + state->cmi = fr->cmi; + state->cmr = fr->cmr; + state->cmi_valid = true; + state->cmr_valid = true; + + if (!tf->d_bits[6]) + return OSMO_AMRT_FRAME_DEC_REMOTE_BAD; + return OSMO_AMRT_FRAME_DEC_GOOD; +} + +static int decode_8k_speech_per_mode(struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool *bad_crc_2, bool *bad_crc_34) +{ + ubit_t crc_collect[37]; /* largest in mode 1 */ + int crc_stat; + + /* For each mode we follow the same logic: + * + * - Check CRC1 first: if it is bad, we cannot trust control bits + * and the frame is totally bad. + * + * - Accept and extract s-bits from the frame. + * + * - Check the other 3 subframe CRCs. If any of them is bad, + * we change frame classification to Speech_Bad, but we don't + * have to toss the entire frame. + */ + switch (fr->cmi) { + case AMR_4_75: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits + 3, 16); + memcpy(crc_collect + 21, tf->d_bits + 20, 2); + memcpy(crc_collect + 23, tf->d_bits + 23, 9); + memcpy(crc_collect + 32, tf->d_bits + 47, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 36, + tf->d_bits + 51); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 3, 48); + memcpy(fr->s_bits + 48, tf->d_bits + 59, 13); + memcpy(fr->s_bits + 61, tf->d_bits + 75, 21); + memcpy(fr->s_bits + 82, tf->d_bits + 99, 13); + /* check CRC2 */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + tf->d_bits + 54, 7, + tf->d_bits + 72); + if (crc_stat) + *bad_crc_2 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 75, 2); + memcpy(crc_collect + 2, tf->d_bits + 92, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 6, + tf->d_bits + 96); + if (crc_stat) + *bad_crc_34 = true; + /* check CRC4 */ + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + tf->d_bits + 99, 2, + tf->d_bits + 112); + if (crc_stat) + *bad_crc_34 = true; + break; + case AMR_5_15: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits + 5, 16); + memcpy(crc_collect + 21, tf->d_bits + 23, 11); + memcpy(crc_collect + 32, tf->d_bits + 46, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 37, + tf->d_bits + 51); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits + 5, 46); + memcpy(fr->s_bits + 46, tf->d_bits + 59, 19); + memcpy(fr->s_bits + 65, tf->d_bits + 81, 19); + memcpy(fr->s_bits + 84, tf->d_bits + 103, 19); + /* check CRC2 */ + memcpy(crc_collect, tf->d_bits + 54, 7); + memcpy(crc_collect + 7, tf->d_bits + 73, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 12, + tf->d_bits + 78); + if (crc_stat) + *bad_crc_2 = true; + /* check CRC3 */ + memcpy(crc_collect, tf->d_bits + 81, 2); + memcpy(crc_collect + 2, tf->d_bits + 95, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 100); + if (crc_stat) + *bad_crc_34 = true; + /* check CRC4 */ + memcpy(crc_collect, tf->d_bits + 103, 2); + memcpy(crc_collect + 2, tf->d_bits + 117, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 7, + tf->d_bits + 122); + if (crc_stat) + *bad_crc_34 = true; + break; + case AMR_5_90: + /* check CRC1 */ + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits, 17); + memcpy(crc_collect + 22, tf->d_bits + 26, 8); + memcpy(crc_collect + 30, tf->d_bits + 47, 4); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, + crc_collect, 34, + tf->d_bits + 51); + if (crc_stat) + return -EINVAL; + /* copy out s-bits */ + memcpy(fr->s_bits, tf->d_bits, 51); + memcpy(fr->s_bits + 51, tf->d_bits + 59, 21 + 25 + 21); + /* no CRC2-4 for this mode */ + break; + default: + OSMO_ASSERT(0); + } + + fr->cmi_valid = true; + return 0; +} + +static enum osmo_amrt_decode_result +decode_8k_low_speech(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, const struct osmo_trau_frame *tf, + bool allow_config_prot) +{ + uint8_t c1_3 = get_bits(tf->c_bits, 0, 3); + bool bad_crc_2 = false, bad_crc_34 = false; + int rc; + + /* The bulk of decoding and CRC verification depends on RIF - + * and for 8k, RIF itself is coded indirectly. */ + if (c1_3 >= 3) { + /* CMR frame - saved CMI is required */ + if (!state->cmi_valid || state->cmi > AMR_5_90) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + fr->cmi = state->cmi; + rc = decode_8k_speech_per_mode(fr, tf, &bad_crc_2, &bad_crc_34); + if (rc < 0) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + /* output and state change specific to CMR frame */ + fr->rif = 1; + fr->rif_valid = true; + fr->cmr = c1_3 - 3; + fr->cmr_valid = true; + state->cmr = fr->cmr; + state->cmr_valid = true; + state->cmi_valid = false; + } else { + /* CMI frame */ + fr->cmi = c1_3; + rc = decode_8k_speech_per_mode(fr, tf, &bad_crc_2, &bad_crc_34); + if (rc < 0) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + /* output and state change specific to CMI frame */ + fr->rif = 0; + fr->rif_valid = true; + if (state->cmr_valid) { + fr->cmr = state->cmr; + fr->cmr_valid = true; + } else { + fr->cmr_valid = false; + } + state->cmi = fr->cmi; + state->cmi_valid = true; + state->cmr_valid = false; + } + + if (bad_crc_2 || bad_crc_34) + fr->frame_class = OSMO_AMRT_FC_SPEECH_BAD; + else + fr->frame_class = get_bits(tf->c_bits, 3, 2) << 3; + + /* extra info for TFO etc */ + fr->ta_dtxd_tfoe_valid = false; + if (allow_config_prot && !bad_crc_2) { + fr->config_prot = get_bits(tf->d_bits, 54, 3); + fr->message_no = get_bits(tf->d_bits, 57, 2); + fr->param_info = OSMO_AMRT_TFOP_ABSENT_NO_ROOM; + } else { + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + } + + if (bad_crc_2 || bad_crc_34) + return OSMO_AMRT_FRAME_DEC_BAD; + return OSMO_AMRT_FRAME_DEC_GOOD; +} + +static enum osmo_amrt_decode_result +decode_8k_low(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, bool allow_config_prot) +{ + if (tf->c_bits[3] || tf->c_bits[4]) + return decode_8k_low_speech(state, fr, tf, allow_config_prot); + return decode_8k_low_no_speech(state, fr, tf, allow_config_prot); +} + +static void decode_8k_67_74_common(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + unsigned d_offset, bool *bad_cmr) +{ + uint8_t c1_3 = get_bits(tf->c_bits, 0, 3); + uint8_t d1_3 = get_bits(tf->d_bits, d_offset, 3); + + /* first figure out speech class */ + switch (c1_3) { + case 0: + case 2: + fr->frame_class = OSMO_AMRT_FC_SPEECH_BAD; + break; + default: + fr->frame_class = OSMO_AMRT_FC_SPEECH_GOOD; + break; + } + + /* now figure out RIF and CMI/CMR situation */ + switch (c1_3) { + case 0: + case 1: + /* codes for RIF=0 */ + fr->rif = 0; + fr->rif_valid = true; + if (state->cmr_valid) { + fr->cmr = state->cmr; + fr->cmr_valid = true; + } else { + fr->cmr_valid = false; + } + state->cmi = fr->cmi; + state->cmi_valid = true; + state->cmr_valid = false; + break; + case 2: + /* RIF=1, CMR in Speech_Bad data bits */ + fr->rif = 1; + fr->rif_valid = true; + if (d1_3 >= 3) { + fr->cmr = d1_3 - 3; + fr->cmr_valid = true; + state->cmr = fr->cmr; + state->cmr_valid = true; + } else { + *bad_cmr = true; + fr->cmr_valid = false; + state->cmr_valid = false; + } + state->cmi_valid = false; + break; + default: + /* RIF=1, CMR given like in low speech frames */ + fr->rif = 1; + fr->rif_valid = true; + fr->cmr = c1_3 - 3; + fr->cmr_valid = true; + state->cmr = fr->cmr; + state->cmr_valid = true; + state->cmi_valid = false; + break; + } +} + +static enum osmo_amrt_decode_result +decode_8k_6k7(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf) +{ + ubit_t crc_collect[3 + 17 + 2 + 8 + 5]; + int crc_stat; + bool bad_cmr = false; + + /* As usual, check CRC before decoding anything else */ + memcpy(crc_collect, tf->c_bits, 3); + memcpy(crc_collect + 3, tf->d_bits, 17); + crc_collect[20] = tf->d_bits[19]; + crc_collect[21] = tf->d_bits[23]; + memcpy(crc_collect + 22, tf->d_bits + 26, 8); + memcpy(crc_collect + 30, tf->d_bits + 48, 5); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, crc_collect, 35, + tf->d_bits + 55); + if (crc_stat) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + + /* Extract info content from this frame, + * without considering C1..C3 yet. */ + fr->cmi = AMR_6_70; + fr->cmi_valid = true; + memcpy(fr->s_bits, tf->d_bits, 55); + memcpy(fr->s_bits + 55, tf->d_bits + 58, 79); + + /* decode speech class and RIF/CMI/CMR */ + decode_8k_67_74_common(state, fr, tf, 0, &bad_cmr); + + /* no extra info */ + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + + if (bad_cmr) + return OSMO_AMRT_FRAME_DEC_BAD; + return OSMO_AMRT_FRAME_DEC_GOOD; +} + +static enum osmo_amrt_decode_result +decode_8k_7k4(struct osmo_amrt_decode_state *state, struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf) +{ + ubit_t crc_collect[3 + 20 + 3 + 6 + 2 + 3]; + int crc_stat; + bool bad_cmr = false; + + /* As usual, check CRC before decoding anything else */ + memcpy(crc_collect, tf->c_bits, 3); + memcpy(crc_collect + 3, tf->d_bits, 20); + memcpy(crc_collect + 23, tf->d_bits + 21, 3); + memcpy(crc_collect + 26, tf->d_bits + 26, 6); + memcpy(crc_collect + 32, tf->d_bits + 51, 2); + memcpy(crc_collect + 34, tf->d_bits + 54, 3); + crc_stat = osmo_crc8gen_check_bits(&gsm0860_amr_crc3, crc_collect, 37, + tf->d_bits + 58); + if (crc_stat) { + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } + + /* Extract info content from this frame, + * without considering C1..C3 yet. */ + fr->cmi = AMR_7_40; + fr->cmi_valid = true; + memcpy(fr->s_bits, tf->d_bits, 58); + memcpy(fr->s_bits + 58, tf->d_bits + 61, 90); + + /* decode speech class and RIF/CMI/CMR */ + decode_8k_67_74_common(state, fr, tf, 5, &bad_cmr); + + /* no extra info */ + fr->ta_dtxd_tfoe_valid = false; + fr->config_prot = OSMO_AMRT_CFG_PROT_NO_CON; + + if (bad_cmr) + return OSMO_AMRT_FRAME_DEC_BAD; + return OSMO_AMRT_FRAME_DEC_GOOD; +} + +/*! High-level decode a received AMR TRAU frame + * + * \param[inout] state Decoding state maintained from one frame to the next. + * \param[out] fr Intermediate representation of the decoded frame, primary + * output from this function. + * \param[in] tf Received TRAU frame after low-level decoding by + * osmo_trau_frame_decode_{8,16}k() functions. + * \param[in] allow_config_prot Should we accept Rel4 style of TFO config + * protocol as part of the decoded frame? + * \returns code indicating status of the decoded frame in the sense of UFE/DFE. + * + * This function always fills out struct osmo_amrt_if fully, setting all + * required fields and all validity flags for conditional fields, even when + * we return OSMO_AMRT_FRAME_DEC_BAD. Therefore, no pre-initialization + * is needed from the caller, nor does the caller need to do any post-filling + * except for special application requirements. + * + * Additional notes regarding \ref allow_config_prot flag: at first glance, + * it makes the most sense for this function to always decode whatever we + * received in TRAU frame C-bits and D-bits, put the distilled result into + * struct osmo_amrt_if, and let the application apply logic that suppresses + * Config_Prot if necessary. However, there exist some E1 BTS that implement + * only Rel5 version of TFO config protocol, not Rel4, and to make it worse + * (I am pointing at you, Nokia), they put total garbage into bit positions + * where Rel4 TFO config protocol would go. Therefore, if an E1 Abis MGW + * is configured to speak Rel5 version of TFO config protocol, or none at all, + * it makes sense to skip CPU cycles that would be spent decoding garbage + * in Rel4 TFO config bits. + */ +enum osmo_amrt_decode_result +osmo_amrt_decode_trau_frame(struct osmo_amrt_decode_state *state, + struct osmo_amrt_if *fr, + const struct osmo_trau_frame *tf, + bool allow_config_prot) +{ + switch (tf->type) { + case OSMO_TRAU16_FT_AMR: + return decode_16k(state, fr, tf, allow_config_prot); + case OSMO_TRAU8_AMR_LOW: + return decode_8k_low(state, fr, tf, allow_config_prot); + case OSMO_TRAU8_AMR_6k7: + return decode_8k_6k7(state, fr, tf); + case OSMO_TRAU8_AMR_7k4: + return decode_8k_7k4(state, fr, tf); + default: + decode_handle_error(state, fr); + return OSMO_AMRT_FRAME_DEC_BAD; + } +} + +/*** TRAU frame encoding direction ***/ + +static void set_bits(ubit_t *bitbuf, int offset, int num, uint32_t val) +{ + uint32_t mask; + + for (mask = 1 << (num - 1); mask; mask >>= 1) { + if (val & mask) + bitbuf[offset] = 1; + else + bitbuf[offset] = 0; + offset++; + } +} + +/* This preprocessing function is common between 16k and 8k TRAU frame + * encoding functions, updating state according to inputs and handling + * some basic cases of speech frame stealing. + */ +static enum osmo_amrt_fc +encode_common_preproc(struct osmo_amrt_encode_state *state, + const struct osmo_amrt_if *fr, + enum osmo_amr_type max_mode_for_channel) +{ + enum osmo_amrt_fc fc = fr->frame_class; + + /* CMR logic per TW-TS-006 section 6.3.4 */ + if (fr->cmr_valid) { + if (fr->cmr <= max_mode_for_channel) + state->cmr = fr->cmr; + else + state->cmr = max_mode_for_channel; + } + + /* RIF logic per TW-TS-006 section 6.3.3 */ + if (fr->rif_valid) { + if (fr->rif && osmo_amrt_fc_is_speech(fc) && + (state->rif || fr->cmi != state->cmi)) + fc = OSMO_AMRT_FC_NO_DATA; + state->rif = fr->rif; + } else { + if (osmo_amrt_fc_is_speech(fc) && !state->rif && + fr->cmi != state->cmi && fr->cmi <= max_mode_for_channel) + state->rif = 0; /* two CMI outputs in a row */ + else + state->rif = !state->rif; + } + + /* Old state CMI remains only if we got OSMO_AMRT_FC_NO_DATA w/o CMI, + * or if we got a bogon in the sense of someone trying to send + * MR795, MR102 or MR122 to an 8 kbit/s TRAU channel. + */ + if (fr->cmi_valid) { + if (fr->cmi <= max_mode_for_channel) + state->cmi = fr->cmi; + else + fc = OSMO_AMRT_FC_NO_DATA; + } + + /* DTXd and TFOE output per TW-TS-006 section 6.3.6 */ + if (fr->ta_dtxd_tfoe_valid) { + state->dtxd = fr->dtxd; + state->tfoe = fr->tfoe; + } + + /* no more commonality between 16k and 8k */ + return fc; +} + +static bool encode_16k_steal_for_tfo(const struct osmo_amrt_if *fr) +{ + static const ubit_t five_zeros[5] = {0, 0, 0, 0, 0}; + int rc; + + if (fr->param_info != OSMO_AMRT_TFOP_PRESENT_VALID) + return false; + switch (fr->cmi) { + case AMR_12_2: + return true; + case AMR_10_2: + rc = memcmp(fr->tfo_param_bits + 20, five_zeros, 5); + if (rc != 0) + return true; + rc = memcmp(fr->tfo_param_bits + 45, tfo_blockC_fixed_params, + 26); + if (rc != 0) + return true; + return false; + default: + return false; + } +} + +static void encode_16k_set_cbits(struct osmo_trau_frame *tf, + enum osmo_amrt_fc fc, + const struct osmo_amrt_if *fr, + const struct osmo_amrt_encode_state *state, + bool is_tfo) +{ + uint8_t ta; + + set_bits(tf->c_bits, 0, 5, TRAU_FT_AMR); + + if (fr->ta_dtxd_tfoe_valid && fr->ta >= 0x38 && fr->ta <= 0x3C) + ta = fr->ta; + else + ta = is_tfo ? 0x38 : 0; + set_bits(tf->c_bits, 5, 6, ta); + + tf->c_bits[11] = state->rif; + tf->c_bits[12] = 1; /* UFE */ + + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) { + set_bits(tf->c_bits, 13, 3, fr->config_prot); + set_bits(tf->c_bits, 16, 2, fr->message_no); + } else { + memset(tf->c_bits + 13, 0, 5); + } + + tf->c_bits[18] = state->dtxd; + tf->c_bits[19] = state->tfoe; + + tf->c_bits[20] = (fc & 0x10) >> 4; + tf->c_bits[21] = (fc & 0x08) >> 3; + if (osmo_amrt_fc_is_speech(fc)) { + /* speech frame */ + set_bits(tf->c_bits, 22, 3, + state->rif ? state->cmr : state->cmi); + } else { + /* No_Speech frame */ + memset(tf->c_bits + 22, 0, 3); + } +} + +static void encode_16k_no_speech(struct osmo_trau_frame *tf, + enum osmo_amrt_fc fc, + const struct osmo_amrt_if *fr, + const struct osmo_amrt_encode_state *state) +{ + memset(tf->d_bits, 1, 31); + + set_bits(tf->d_bits, 31, 3, fc); + set_bits(tf->d_bits, 34, 3, state->cmi); + set_bits(tf->d_bits, 37, 3, state->cmr); + /* No PAB or TAE in our pristine output; the application may fill + * those in after our encoding but before computing CRC. */ + memset(tf->d_bits + 40, 0, 3); + /* unused "reserved for TFO" bits */ + memset(tf->d_bits + 43, 1, 14); + + /* D58..D92 depend on No_Speech_Classification */ + switch (fc) { + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + memcpy(tf->d_bits + 57, fr->s_bits, 35); + break; + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + case OSMO_AMRT_FC_SID_FIRST: + memset(tf->d_bits + 57, 1, 35); + break; + default: + OSMO_ASSERT(0); + } + + memset(tf->d_bits + 95, 1, 256 - 95); +} + +static void encode_16k_speech(struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr) +{ + switch (fr->cmi) { + case AMR_4_75: + memset(tf->d_bits, 1, 44); + memcpy(tf->d_bits + 44, fr->s_bits, 48); + memcpy(tf->d_bits + 95, fr->s_bits + 48, 13); + memcpy(tf->d_bits + 111, fr->s_bits + 61, 21); + memcpy(tf->d_bits + 135, fr->s_bits + 82, 13); + memset(tf->d_bits + 151, 1, 256 - 151); + break; + case AMR_5_15: + memset(tf->d_bits, 1, 46); + memcpy(tf->d_bits + 46, fr->s_bits, 46); + memcpy(tf->d_bits + 95, fr->s_bits + 46, 19); + memcpy(tf->d_bits + 117, fr->s_bits + 65, 19); + memcpy(tf->d_bits + 139, fr->s_bits + 84, 19); + memset(tf->d_bits + 161, 1, 256 - 161); + break; + case AMR_5_90: + memset(tf->d_bits, 1, 41); + memcpy(tf->d_bits + 41, fr->s_bits, 51); + memcpy(tf->d_bits + 95, fr->s_bits + 51, 21); + memcpy(tf->d_bits + 119, fr->s_bits + 72, 25); + memcpy(tf->d_bits + 147, fr->s_bits + 97, 21); + memset(tf->d_bits + 171, 1, 256 - 171); + break; + case AMR_6_70: + memset(tf->d_bits, 1, 37); + memcpy(tf->d_bits + 37, fr->s_bits, 55); + memcpy(tf->d_bits + 95, fr->s_bits + 55, 25); + memcpy(tf->d_bits + 123, fr->s_bits + 80, 29); + memcpy(tf->d_bits + 155, fr->s_bits + 109, 25); + memset(tf->d_bits + 183, 1, 256 - 183); + break; + case AMR_7_40: + memset(tf->d_bits, 1, 34); + memcpy(tf->d_bits + 34, fr->s_bits, 58); + memcpy(tf->d_bits + 95, fr->s_bits + 58, 29); + memcpy(tf->d_bits + 127, fr->s_bits + 87, 32); + memcpy(tf->d_bits + 162, fr->s_bits + 119, 29); + memset(tf->d_bits + 194, 1, 256 - 194); + break; + case AMR_7_95: + memset(tf->d_bits, 1, 31); + memcpy(tf->d_bits + 31, fr->s_bits, 61); + memcpy(tf->d_bits + 95, fr->s_bits + 61, 32); + memcpy(tf->d_bits + 130, fr->s_bits + 93, 34); + memcpy(tf->d_bits + 167, fr->s_bits + 127, 32); + memset(tf->d_bits + 202, 1, 256 - 202); + break; + case AMR_10_2: + memset(tf->d_bits, 1, 20); + memcpy(tf->d_bits + 20, fr->s_bits, 72); + memcpy(tf->d_bits + 95, fr->s_bits + 72, 43); + memcpy(tf->d_bits + 141, fr->s_bits + 115, 46); + memcpy(tf->d_bits + 190, fr->s_bits + 161, 43); + memset(tf->d_bits + 233, 1, 20); + break; + case AMR_12_2: + memcpy(tf->d_bits, fr->s_bits, 91); + memcpy(tf->d_bits + 94, fr->s_bits + 91, 50); + memcpy(tf->d_bits + 147, fr->s_bits + 141, 53); + memcpy(tf->d_bits + 203, fr->s_bits + 194, 50); + break; + default: + OSMO_ASSERT(0); + } +} + +static void encode_16k_tfo_regular(struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr) +{ + if (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID) { + memcpy(tf->d_bits, fr->tfo_param_bits, 25); + memset(tf->d_bits + 25, 0, 3); + memcpy(tf->d_bits + 233, fr->tfo_param_bits + 25, 20); + memcpy(tf->d_bits + 202, fr->tfo_param_bits + 45, 26); + memset(tf->d_bits + 228, 0, 2); + } else { + /* Par_Type=0,0; the rest is don't-care fill */ + memset(tf->d_bits, 0, 28); + memset(tf->d_bits + 233, 0, 20); + memset(tf->d_bits + 202, 0, 28); + } + /* all 3 CRCs need to be set correctly no matter how we set the bits */ + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits, 28, + tf->d_bits + 28); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 233, 20, + tf->d_bits + 253); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 202, 28, + tf->d_bits + 230); +} + +static void encode_16k_tfo_mr102(struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr) +{ + if (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID) { + memcpy(tf->d_bits, fr->tfo_param_bits, 20); + memcpy(tf->d_bits + 233, fr->tfo_param_bits + 25, 20); + } else { + /* Par_Type=0,0; the rest is don't-care fill */ + memset(tf->d_bits, 0, 20); + memset(tf->d_bits + 233, 0, 20); + } +} + +static void encode_16k_tfo(struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, enum osmo_amrt_fc fc) +{ + if (!osmo_amrt_fc_is_speech(fc)) { + encode_16k_tfo_regular(tf, fr); + return; + } + switch (fr->cmi) { + case AMR_12_2: + break; + case AMR_10_2: + encode_16k_tfo_mr102(tf, fr); + break; + default: + encode_16k_tfo_regular(tf, fr); + break; + } +} + +/*! High-level encode an AMR TRAU frame into TRAU-AMR-16k format + * + * \param[inout] state Encoding state maintained from one frame to the next. + * \param[out] tf TRAU frame to be generated. + * \param[in] fr Frame content to be encoded. + * \param[in] is_tfo TFO frame is to be emitted, rather than regular TRAU. + * \returns code indicating the type of TRAU frame CRC to be computed later. + * + * All necessary fields of struct osmo_trau_frame are filled out by this + * function (followed by osmo_amrt_set_trau_crc()) with the exception of + * .dir and .dl_ta_usec - those two fields must be set by the application, + * either before or after calling the present function, but before passing + * the frame to osmo_trau_frame_encode() or osmo_trau_frame_encode_tfo() + * for final low-level encoding. + */ +enum osmo_amrt_crc_type +osmo_amrt_encode_trau_frame_16k(struct osmo_amrt_encode_state *state, + struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, bool is_tfo) +{ + enum osmo_amrt_fc fc; + enum osmo_amrt_crc_type crc_type; + + fc = encode_common_preproc(state, fr, AMR_12_2); + + /* Additional case of speech frame stealing for TFO params */ + if (osmo_amrt_fc_is_speech(fc) && fr->config_prot) { + if (encode_16k_steal_for_tfo(fr)) + fc = OSMO_AMRT_FC_NO_DATA; + } + + /* Proceed with output */ + tf->type = OSMO_TRAU16_FT_AMR; + encode_16k_set_cbits(tf, fc, fr, state, is_tfo); + if (osmo_amrt_fc_is_speech(fc)) { + encode_16k_speech(tf, fr); + crc_type = OSMO_AMRT_CRC_16k_SPEECH_MR475 + fr->cmi; + } else { + encode_16k_no_speech(tf, fc, fr, state); + crc_type = OSMO_AMRT_CRC_16k_NO_SPEECH; + } + memset(tf->t_bits, 1, 4); + + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) + encode_16k_tfo(tf, fr, fc); + + return crc_type; +} + +static bool encode_8k_steal_for_tfo(const struct osmo_amrt_if *fr, + enum osmo_amrt_fc fc) +{ + int rc; + + switch (fc) { + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + case OSMO_AMRT_FC_SID_FIRST: + return false; + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + if (fr->param_info != OSMO_AMRT_TFOP_PRESENT_VALID) + return false; + rc = memcmp(fr->tfo_param_bits + 45, tfo_blockC_fixed_params, + 26); + return (rc != 0); + case OSMO_AMRT_FC_SPEECH_GOOD: + case OSMO_AMRT_FC_SPEECH_DEGRADED: + case OSMO_AMRT_FC_SPEECH_BAD: + switch (fr->cmi) { + case AMR_4_75: + case AMR_5_15: + case AMR_5_90: + return (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID); + case AMR_6_70: + case AMR_7_40: + return true; + default: + OSMO_ASSERT(0); + } + default: + OSMO_ASSERT(0); + } +} + +static enum osmo_amrt_crc_type +encode_8k_no_speech(struct osmo_trau_frame *tf, enum osmo_amrt_fc fc, + const struct osmo_amrt_if *fr, + const struct osmo_amrt_encode_state *state, bool is_tfo) +{ + uint8_t ta; + enum osmo_amrt_crc_type crc_type; + + if (fr->ta_dtxd_tfoe_valid && fr->ta >= 0x38 && fr->ta <= 0x3C) + ta = fr->ta; + else + ta = is_tfo ? 0x38 : 0; + + tf->type = OSMO_TRAU8_AMR_LOW; + tf->c_bits[0] = 1; + tf->c_bits[1] = 0; + tf->c_bits[2] = state->rif; + tf->c_bits[3] = 0; + tf->c_bits[4] = 0; + + set_bits(tf->d_bits, 0, 6, ta); + tf->d_bits[6] = 1; /* UFE/DFE */ + set_bits(tf->d_bits, 7, 3, fc); + set_bits(tf->d_bits, 10, 3, state->cmi); + set_bits(tf->d_bits, 13, 3, state->cmr); + + /* D17..D51 depend on No_Speech_Classification */ + switch (fc) { + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + crc_type = OSMO_AMRT_CRC_8k_SID_UPDATE; + memcpy(tf->d_bits + 16, fr->s_bits, 35); + break; + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + case OSMO_AMRT_FC_SID_FIRST: + /* Per TS 48.061 section 5.2.4.2.1, these frame types + * (unlike their TRAU-AMR-16k counterparts) have no CRC. + * Not only the 35 bits of SID parameters, but also the + * following 3 bits for CRC, "shall be set to all '1' in + * these cases". Because it is "shall" and not "should", + * we comply and resist the natural urge to fill in CRC + * for all No_Speech frame types. + */ + crc_type = OSMO_AMRT_CRC_NONE; + memset(tf->d_bits + 16, 1, 38); + break; + default: + OSMO_ASSERT(0); + } + + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) { + set_bits(tf->d_bits, 54, 3, fr->config_prot); + set_bits(tf->d_bits, 57, 2, fr->message_no); + } else { + memset(tf->d_bits + 54, 0, 5); + } + /* No PAB or TAE in our pristine output; the application may fill + * those in after our encoding but before computing CRC. */ + memset(tf->d_bits + 59, 0, 3); + tf->d_bits[62] = state->dtxd; + tf->d_bits[63] = state->tfoe; + + memset(tf->d_bits + 64, 1, 126 - 64); + tf->t_bits[0] = 1; + + return crc_type; +} + +static void encode_8k_sp_common_low(struct osmo_trau_frame *tf, + enum osmo_amrt_fc fc, + const struct osmo_amrt_if *fr, + const struct osmo_amrt_encode_state *state) +{ + uint8_t c1_3; + + tf->type = OSMO_TRAU8_AMR_LOW; + + if (state->rif) + c1_3 = state->cmr + 3; + else + c1_3 = state->cmi; + set_bits(tf->c_bits, 0, 3, c1_3); + tf->c_bits[3] = (fc & 0x10) >> 4; + tf->c_bits[4] = (fc & 0x08) >> 3; + + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) { + set_bits(tf->d_bits, 54, 3, fr->config_prot); + set_bits(tf->d_bits, 57, 2, fr->message_no); + } else { + memset(tf->d_bits + 54, 0, 5); + } + + tf->t_bits[0] = 1; +} + +static void encode_8k_sp_common_high(struct osmo_trau_frame *tf, + enum osmo_amrt_fc fc, + const struct osmo_amrt_encode_state *state, + unsigned d_offset) +{ + bool is_bad; + uint8_t cmr_shifted = state->cmr + 3; + + switch (fc) { + case OSMO_AMRT_FC_SPEECH_GOOD: + case OSMO_AMRT_FC_SPEECH_DEGRADED: + is_bad = false; + break; + case OSMO_AMRT_FC_SPEECH_BAD: + is_bad = true; + break; + default: + OSMO_ASSERT(0); + } + + if (state->rif) { + if (is_bad) { + set_bits(tf->c_bits, 0, 3, 2); + set_bits(tf->d_bits, d_offset, 3, cmr_shifted); + } else { + set_bits(tf->c_bits, 0, 3, cmr_shifted); + } + } else { + set_bits(tf->c_bits, 0, 3, is_bad ? 0 : 1); + } +} + +static void encode_8k_speech(struct osmo_trau_frame *tf, enum osmo_amrt_fc fc, + const struct osmo_amrt_if *fr, + const struct osmo_amrt_encode_state *state) +{ + switch (fr->cmi) { + case AMR_4_75: + encode_8k_sp_common_low(tf, fc, fr, state); + memset(tf->d_bits, 1, 3); + memcpy(tf->d_bits + 3, fr->s_bits, 48); + memcpy(tf->d_bits + 59, fr->s_bits + 48, 13); + memcpy(tf->d_bits + 75, fr->s_bits + 61, 21); + memcpy(tf->d_bits + 99, fr->s_bits + 82, 13); + memset(tf->d_bits + 115, 1, 11); + break; + case AMR_5_15: + encode_8k_sp_common_low(tf, fc, fr, state); + memset(tf->d_bits, 1, 5); + memcpy(tf->d_bits + 5, fr->s_bits, 46); + memcpy(tf->d_bits + 59, fr->s_bits + 46, 19); + memcpy(tf->d_bits + 81, fr->s_bits + 65, 19); + memcpy(tf->d_bits + 103, fr->s_bits + 84, 19); + tf->d_bits[125] = 1; + break; + case AMR_5_90: + encode_8k_sp_common_low(tf, fc, fr, state); + memcpy(tf->d_bits, fr->s_bits, 51); + memcpy(tf->d_bits + 59, fr->s_bits + 51, 21 + 25 + 21); + break; + case AMR_6_70: + tf->type = OSMO_TRAU8_AMR_6k7; + memcpy(tf->d_bits, fr->s_bits, 55); + memcpy(tf->d_bits + 58, fr->s_bits + 55, 79); + encode_8k_sp_common_high(tf, fc, state, 0); + break; + case AMR_7_40: + tf->type = OSMO_TRAU8_AMR_7k4; + memcpy(tf->d_bits, fr->s_bits, 58); + memcpy(tf->d_bits + 61, fr->s_bits + 58, 90); + encode_8k_sp_common_high(tf, fc, state, 5); + break; + default: + OSMO_ASSERT(0); + } +} + +static void encode_8k_tfo(struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, enum osmo_amrt_fc fc) +{ + bool is_sid; + + switch (fc) { + case OSMO_AMRT_FC_NO_DATA: + case OSMO_AMRT_FC_ONSET: + case OSMO_AMRT_FC_SID_FIRST: + is_sid = false; + break; + case OSMO_AMRT_FC_SID_UPDATE: + case OSMO_AMRT_FC_SID_BAD: + is_sid = true; + break; + default: + return; + } + + if (fr->param_info == OSMO_AMRT_TFOP_PRESENT_VALID) { + memcpy(tf->d_bits + 64, fr->tfo_param_bits, 25); + memset(tf->d_bits + 89, 0, 3); + memcpy(tf->d_bits + 95, fr->tfo_param_bits + 25, 20); + if (!is_sid) { + memcpy(tf->d_bits + 16, fr->tfo_param_bits + 45, 26); + memset(tf->d_bits + 42, 0, 2); + } + } else { + /* Par_Type=0,0; the rest is don't-care fill */ + memset(tf->d_bits + 64, 0, 28); + memset(tf->d_bits + 95, 0, 20); + if (!is_sid) + memset(tf->d_bits + 16, 0, 28); + } + /* CRC_A and CRC_B are always present */ + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 64, 28, + tf->d_bits + 92); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 95, 20, + tf->d_bits + 115); + if (!is_sid) { + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 16, 28, + tf->d_bits + 44); + } +} + +/*! High-level encode an AMR TRAU frame into TRAU-AMR-8k format + * + * \param[inout] state Encoding state maintained from one frame to the next. + * \param[out] tf TRAU frame to be generated. + * \param[in] fr Frame content to be encoded. + * \param[in] is_tfo TFO frame is to be emitted, rather than regular TRAU. + * \param[in] restr Restrictions on the type of frame that can be emitted. + * \returns code indicating the type of TRAU frame CRC to be computed later. + * + * All necessary fields of struct osmo_trau_frame are filled out by this + * function (followed by osmo_amrt_set_trau_crc()) with the exception of + * .dir and .dl_ta_usec - those two fields must be set by the application, + * either before or after calling the present function, but before passing + * the frame to osmo_trau_frame_encode() or osmo_trau_frame_encode_tfo() + * for final low-level encoding. + */ +enum osmo_amrt_crc_type +osmo_amrt_encode_trau_frame_8k(struct osmo_amrt_encode_state *state, + struct osmo_trau_frame *tf, + const struct osmo_amrt_if *fr, bool is_tfo, + enum osmo_amrt_8k_restrict restr) +{ + enum osmo_amrt_fc fc; + enum osmo_amrt_crc_type crc_type; + + fc = encode_common_preproc(state, fr, AMR_7_40); + + /* Apply requested restrictions */ + switch (restr) { + case OSMO_AMRT_OUT8k_NO_RESTRICT: + break; + case OSMO_AMRT_OUT8k_REQUIRE_LOW: + if (osmo_amrt_fc_is_speech(fc) && fr->cmi >= AMR_5_90) + fc = OSMO_AMRT_FC_NO_DATA; + break; + case OSMO_AMRT_OUT8k_REQUIRE_NO_SPEECH: + if (osmo_amrt_fc_is_speech(fc)) + fc = OSMO_AMRT_FC_NO_DATA; + break; + default: + /* Don't want to assert just for this... + * Treat it as no restriction. */ + break; + } + + /* Frame stealing for TFO Config_Prot or parameters */ + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) { + if (encode_8k_steal_for_tfo(fr, fc)) + fc = OSMO_AMRT_FC_NO_DATA; + } + + /* Frame stealing for messages in TA field */ + if (fr->ta_dtxd_tfoe_valid && fr->ta >= 0x38 && fr->ta <= 0x3C && + osmo_amrt_fc_is_speech(fc)) + fc = OSMO_AMRT_FC_NO_DATA; + + /* Proceed with output */ + if (osmo_amrt_fc_is_speech(fc)) { + encode_8k_speech(tf, fc, fr, state); + crc_type = OSMO_AMRT_CRC_8k_SPEECH_MR475 + fr->cmi; + } else { + crc_type = encode_8k_no_speech(tf, fc, fr, state, is_tfo); + } + + if (fr->config_prot != OSMO_AMRT_CFG_PROT_NO_CON) + encode_8k_tfo(tf, fr, fc); + + return crc_type; +} + +/*! Set CRC fields in prepared AMR TRAU frame + * + * \param[inout] tf TRAU frame to be completed with CRC bits. + * \param[in] crc_type CRC type code returned earlier by + * osmo_amrt_encode_trau_frame_16k() or osmo_amrt_encode_trau_frame_8k(). + * \returns 0 if successful, or negative if \ref crc_type is invalid. + * + * This function should be called after initial encoding with + * osmo_amrt_encode_trau_frame_{8,16}k() and after any application-specific + * modifications to that encoded frame, just before passing the finished + * frame to osmo_trau_frame_encode() or osmo_trau_frame_encode_tfo() + * for final low-level encoding. + */ +int osmo_amrt_set_trau_crc(struct osmo_trau_frame *tf, + enum osmo_amrt_crc_type crc_type) +{ + ubit_t crc_collect[86]; /* 16k No_Speech is the longest */ + + switch (crc_type) { + case OSMO_AMRT_CRC_NONE: + break; + case OSMO_AMRT_CRC_16k_NO_SPEECH: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 31, 61); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 86, + tf->d_bits + 92); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR475: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 44, 16); + memcpy(crc_collect + 41, tf->d_bits + 61, 2); + memcpy(crc_collect + 43, tf->d_bits + 64, 9); + memcpy(crc_collect + 52, tf->d_bits + 88, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 56, + tf->d_bits + 92); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 95, 2, + tf->d_bits + 108); + memcpy(crc_collect, tf->d_bits + 111, 2); + memcpy(crc_collect + 2, tf->d_bits + 128, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 6, + tf->d_bits + 132); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 135, 2, + tf->d_bits + 148); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR515: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 46, 16); + memcpy(crc_collect + 41, tf->d_bits + 64, 11); + memcpy(crc_collect + 52, tf->d_bits + 87, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 57, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 2); + memcpy(crc_collect + 2, tf->d_bits + 109, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 114); + memcpy(crc_collect, tf->d_bits + 117, 2); + memcpy(crc_collect + 2, tf->d_bits + 131, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 136); + memcpy(crc_collect, tf->d_bits + 139, 2); + memcpy(crc_collect + 2, tf->d_bits + 153, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 158); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR59: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 41, 17); + memcpy(crc_collect + 42, tf->d_bits + 67, 8); + memcpy(crc_collect + 50, tf->d_bits + 88, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 54, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 3); + memcpy(crc_collect + 3, tf->d_bits + 112, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 116); + memcpy(crc_collect, tf->d_bits + 119, 8); + memcpy(crc_collect + 8, tf->d_bits + 140, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 12, + tf->d_bits + 144); + memcpy(crc_collect, tf->d_bits + 147, 3); + memcpy(crc_collect + 3, tf->d_bits + 164, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 168); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR67: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 37, 17); + crc_collect[42] = tf->d_bits[56]; + crc_collect[43] = tf->d_bits[60]; + memcpy(crc_collect + 44, tf->d_bits + 63, 8); + memcpy(crc_collect + 52, tf->d_bits + 85, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 57, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 113, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 9, + tf->d_bits + 120); + memcpy(crc_collect, tf->d_bits + 123, 8); + memcpy(crc_collect + 8, tf->d_bits + 145, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 13, + tf->d_bits + 152); + memcpy(crc_collect, tf->d_bits + 155, 4); + memcpy(crc_collect + 4, tf->d_bits + 173, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 9, + tf->d_bits + 180); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR74: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 34, 20); + memcpy(crc_collect + 45, tf->d_bits + 55, 3); + memcpy(crc_collect + 48, tf->d_bits + 60, 6); + memcpy(crc_collect + 54, tf->d_bits + 85, 2); + memcpy(crc_collect + 56, tf->d_bits + 88, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 59, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 3); + memcpy(crc_collect + 3, tf->d_bits + 117, 2); + memcpy(crc_collect + 5, tf->d_bits + 120, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 8, + tf->d_bits + 124); + memcpy(crc_collect, tf->d_bits + 127, 6); + memcpy(crc_collect + 6, tf->d_bits + 152, 2); + memcpy(crc_collect + 8, tf->d_bits + 155, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 11, + tf->d_bits + 159); + memcpy(crc_collect, tf->d_bits + 162, 3); + memcpy(crc_collect + 3, tf->d_bits + 184, 2); + memcpy(crc_collect + 5, tf->d_bits + 187, 2); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 191); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR795: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits + 31, 35); + memcpy(crc_collect + 60, tf->d_bits + 83, 2); + crc_collect[62] = tf->d_bits[87]; + crc_collect[63] = tf->d_bits[90]; + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 64, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 118, 2); + memcpy(crc_collect + 6, tf->d_bits + 122, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 10, + tf->d_bits + 127); + memcpy(crc_collect, tf->d_bits + 130, 8); + memcpy(crc_collect + 8, tf->d_bits + 155, 2); + memcpy(crc_collect + 10, tf->d_bits + 159, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 14, + tf->d_bits + 164); + memcpy(crc_collect, tf->d_bits + 167, 4); + memcpy(crc_collect + 4, tf->d_bits + 190, 2); + memcpy(crc_collect + 6, tf->d_bits + 194, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 10, + tf->d_bits + 199); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR102: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits, 45); + memcpy(crc_collect + 70, tf->d_bits + 46, 8); + memcpy(crc_collect + 78, tf->d_bits + 85, 2); + memcpy(crc_collect + 80, tf->d_bits + 88, 2); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 82, + tf->d_bits + 92); + memcpy(crc_collect, tf->d_bits + 95, 4); + memcpy(crc_collect + 4, tf->d_bits + 131, 2); + memcpy(crc_collect + 6, tf->d_bits + 134, 2); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 8, + tf->d_bits + 138); + memcpy(crc_collect, tf->d_bits + 141, 8); + memcpy(crc_collect + 8, tf->d_bits + 180, 2); + memcpy(crc_collect + 10, tf->d_bits + 183, 2); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 12, + tf->d_bits + 187); + memcpy(crc_collect, tf->d_bits + 190, 4); + memcpy(crc_collect + 4, tf->d_bits + 226, 2); + memcpy(crc_collect + 6, tf->d_bits + 229, 2); + memcpy(crc_collect + 8, tf->d_bits + 233, 20); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 28, + tf->d_bits + 253); + break; + case OSMO_AMRT_CRC_16k_SPEECH_MR122: + memcpy(crc_collect, tf->c_bits, 25); + memcpy(crc_collect + 25, tf->d_bits, 29); + memcpy(crc_collect + 54, tf->d_bits + 38, 12); + memcpy(crc_collect + 66, tf->d_bits + 86, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 69, + tf->d_bits + 91); + memcpy(crc_collect, tf->d_bits + 94, 9); + memcpy(crc_collect + 9, tf->d_bits + 139, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 12, + tf->d_bits + 144); + memcpy(crc_collect, tf->d_bits + 147, 12); + memcpy(crc_collect + 12, tf->d_bits + 195, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 15, + tf->d_bits + 200); + memcpy(crc_collect, tf->d_bits + 203, 5); + memcpy(crc_collect + 5, tf->d_bits + 209, 3); + memcpy(crc_collect + 8, tf->d_bits + 248, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 11, + tf->d_bits + 253); + break; + case OSMO_AMRT_CRC_8k_SID_UPDATE: + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits, 51); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 56, + tf->d_bits + 51); + break; + case OSMO_AMRT_CRC_8k_SPEECH_MR475: + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits + 3, 16); + memcpy(crc_collect + 21, tf->d_bits + 20, 2); + memcpy(crc_collect + 23, tf->d_bits + 23, 9); + memcpy(crc_collect + 32, tf->d_bits + 47, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 36, + tf->d_bits + 51); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 54, 7, + tf->d_bits + 72); + memcpy(crc_collect, tf->d_bits + 75, 2); + memcpy(crc_collect + 2, tf->d_bits + 92, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 6, + tf->d_bits + 96); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, tf->d_bits + 99, 2, + tf->d_bits + 112); + break; + case OSMO_AMRT_CRC_8k_SPEECH_MR515: + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits + 5, 16); + memcpy(crc_collect + 21, tf->d_bits + 23, 11); + memcpy(crc_collect + 32, tf->d_bits + 46, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 37, + tf->d_bits + 51); + memcpy(crc_collect, tf->d_bits + 54, 7); + memcpy(crc_collect + 7, tf->d_bits + 73, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 12, + tf->d_bits + 78); + memcpy(crc_collect, tf->d_bits + 81, 2); + memcpy(crc_collect + 2, tf->d_bits + 95, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 100); + memcpy(crc_collect, tf->d_bits + 103, 2); + memcpy(crc_collect + 2, tf->d_bits + 117, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 7, + tf->d_bits + 122); + break; + case OSMO_AMRT_CRC_8k_SPEECH_MR59: + memcpy(crc_collect, tf->c_bits, 5); + memcpy(crc_collect + 5, tf->d_bits, 17); + memcpy(crc_collect + 22, tf->d_bits + 26, 8); + memcpy(crc_collect + 30, tf->d_bits + 47, 4); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 34, + tf->d_bits + 51); + break; + case OSMO_AMRT_CRC_8k_SPEECH_MR67: + memcpy(crc_collect, tf->c_bits, 3); + memcpy(crc_collect + 3, tf->d_bits, 17); + crc_collect[20] = tf->d_bits[19]; + crc_collect[21] = tf->d_bits[23]; + memcpy(crc_collect + 22, tf->d_bits + 26, 8); + memcpy(crc_collect + 30, tf->d_bits + 48, 5); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 35, + tf->d_bits + 55); + break; + case OSMO_AMRT_CRC_8k_SPEECH_MR74: + memcpy(crc_collect, tf->c_bits, 3); + memcpy(crc_collect + 3, tf->d_bits, 20); + memcpy(crc_collect + 23, tf->d_bits + 21, 3); + memcpy(crc_collect + 26, tf->d_bits + 26, 6); + memcpy(crc_collect + 32, tf->d_bits + 51, 2); + memcpy(crc_collect + 34, tf->d_bits + 54, 3); + osmo_crc8gen_set_bits(&gsm0860_amr_crc3, crc_collect, 37, + tf->d_bits + 58); + break; + default: + return -EINVAL; + } + + return 0; +} + +/*! @} */ diff --git a/tests/Makefile.am b/tests/Makefile.am index e214bda..c36a409 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,6 +6,8 @@ e1inp_ipa_bts_test \ subchan_demux/subchan_demux_test \ ipa_recv/ipa_recv_test \ + amr/rtp2trau_amr \ + amr/trau2rtp_amr \ raa_prime/test_dec \ raa_prime/test_enc \ tfo/rtp2tfo \ @@ -46,6 +48,12 @@ ipa_recv_ipa_recv_test_SOURCES = ipa_recv/ipa_recv_test.c ipa_recv_ipa_recv_test_LDADD = $(ABIS_LA_LIBS)
+amr_rtp2trau_amr_SOURCES = amr/rtp2trau_amr.c trau_conv/tw5reader.c +amr_rtp2trau_amr_LDADD = $(TRAU_LA_LIBS) + +amr_trau2rtp_amr_SOURCES = amr/trau2rtp_amr.c +amr_trau2rtp_amr_LDADD = $(TRAU_LA_LIBS) + raa_prime_test_dec_SOURCES = raa_prime/test_dec.c raa_prime_test_dec_LDADD = $(TRAU_LA_LIBS)
@@ -100,6 +108,14 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ subchan_demux/subchan_demux_test.ok \ ipa_recv/ipa_recv_test.ok \ + amr/rtp-in-bwe.hex amr/rtp-in-oa.hex \ + amr/rtp2trau-bwe2fr.ok amr/rtp2trau-bwe2hr.ok \ + amr/rtp2trau-oa2fr.ok amr/rtp2trau-oa2hr.ok \ + amr/rtp2trau-oax2fr.ok amr/rtp2trau-oax2hr.ok \ + amr/trau-amr-16k-bwe.ok amr/trau-amr-16k-oa.ok amr/trau-amr-16k-oax.ok \ + amr/trau-amr-16k.in \ + amr/trau-amr-8k-bwe.ok amr/trau-amr-8k-oa.ok amr/trau-amr-8k-oax.ok \ + amr/trau-amr-8k.in \ raa_prime/d144-ul-input.asc raa_prime/nokia-tcsm2-atrau.hex \ rtp_test/rtp_test.ok \ tfo/enc_test_efr.in tfo/enc_test_fr.in tfo/enc_test_hr.in \ diff --git a/tests/amr/rtp-in-bwe.hex b/tests/amr/rtp-in-bwe.hex new file mode 100644 index 0000000..8924eac --- /dev/null +++ b/tests/amr/rtp-in-bwe.hex @@ -0,0 +1,73 @@ +# This unit test data file is a collection of AMR RTP payloads in BWE +# (bandwidth-efficient) format. This input is intended to be fed to +# rtp2trau_amr test program, generating either TRAU-AMR-16k or TRAU-AMR-8k. + +# Regular speech frames (DHF bit pattern), step through all modes, +# two frames in each mode to allow for normal CMI/CMR alternation. + +F07E2759FC45824D6F78665A1000 +F07E2759FC45824D6F78665A1000 +F0C7EE59FDFC7F7D51EFCB98701800 +F0C7EE59FDFC7F7D51EFCB98701800 +F17A567CD7F7F97A599FFEF022206022 +F17A567CD7F7F97A599FFEF022206022 +F1FC2E79AFDA001C8270F4F2F20212285563 +F1FC2E79AFDA001C8270F4F2F20212285563 +F27E1C73D22007B109FC3F0FDCC6226188818880 +F27E1C73D22007B109FC3F0FDCC6226188818880 +F2D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +F2D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +F347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +F347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 + +# The same 8 DHFs, but marked as bad (Q=0). + +F03E2759FC45824D6F78665A1000 +F03E2759FC45824D6F78665A1000 +F087EE59FDFC7F7D51EFCB98701800 +F087EE59FDFC7F7D51EFCB98701800 +F13A567CD7F7F97A599FFEF022206022 +F13A567CD7F7F97A599FFEF022206022 +F1BC2E79AFDA001C8270F4F2F20212285563 +F1BC2E79AFDA001C8270F4F2F20212285563 +F23E1C73D22007B109FC3F0FDCC6226188818880 +F23E1C73D22007B109FC3F0FDCC6226188818880 +F290ACCC820604F5FA45B9EA97A035FF7A04AE302000 +F290ACCC820604F5FA45B9EA97A035FF7A04AE302000 +F307F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +F307F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +F382155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +F382155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 + +# Test passing of CMR from RTP to TRAU frame output: do 8 DHFs once again, +# but step CMR in the opposite order from that of CMI advance. + +707E2759FC45824D6F78665A1000 +707E2759FC45824D6F78665A1000 +60C7EE59FDFC7F7D51EFCB98701800 +60C7EE59FDFC7F7D51EFCB98701800 +517A567CD7F7F97A599FFEF022206022 +517A567CD7F7F97A599FFEF022206022 +41FC2E79AFDA001C8270F4F2F20212285563 +41FC2E79AFDA001C8270F4F2F20212285563 +327E1C73D22007B109FC3F0FDCC6226188818880 +327E1C73D22007B109FC3F0FDCC6226188818880 +22D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +22D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +1347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +1347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +03C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +03C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 + +# Test encoding of SID and No_Data frames. + +F4400000000100 +F7C0 +F7C0 +F449B1E0DA0500 + +# Test marked-bad SID update. + +F409B1E0DA0500 diff --git a/tests/amr/rtp-in-oa.hex b/tests/amr/rtp-in-oa.hex new file mode 100644 index 0000000..684a2ff --- /dev/null +++ b/tests/amr/rtp-in-oa.hex @@ -0,0 +1,104 @@ +# This unit test data file is a collection of AMR RTP payloads in octet-aligned +# format, with some (but not all) of them also exercising OAX extensions of +# TW-TS-006. This input is intended to be fed to rtp2trau_amr test program +# in multiple modes: accepting RTP payloads with OAX enabled or disabled, +# generating either TRAU-AMR-16k or TRAU-AMR-8k. + +# Regular speech frames (DHF bit pattern), step through all modes, +# two frames in each mode to allow for normal CMI/CMR alternation. + +F004F89D67F1160935BDE1996840 +F004F89D67F1160935BDE1996840 +F00C1FB967F7F1FDF547BF2E61C060 +F00C1FB967F7F1FDF547BF2E61C060 +F014E959F35FDFE5E9667FFBC088818088 +F014E959F35FDFE5E9667FFBC088818088 +F01CF0B9E6BF68007209C3D3CBC80848A1558C +F01CF0B9E6BF68007209C3D3CBC80848A1558C +F024F871CF48801EC427F0FC3F7318898622062200 +F024F871CF48801EC427F0FC3F7318898622062200 +F02C42B332081813D7E916E7AA5E80D7FDE812B8C080 +F02C42B332081813D7E916E7AA5E80D7FDE812B8C080 +F0341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +F0341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 + +# Do MR59 and MR74 DHFs once again, but with Speech_Degraded indication. +# Representing this state in RTP requires use of a TW-TS-006 extension, +# hence we get to test osmo_amrt_decode_rtp() behaviour in terms of +# allowing or disallowing these extensions in RTP input. + +F016E959F35FDFE5E9667FFBC088818088 +F016E959F35FDFE5E9667FFBC088818088 +F026F871CF48801EC427F0FC3F7318898622062200 +F026F871CF48801EC427F0FC3F7318898622062200 + +# Now do the same frames yet again, but with Speech_Bad indication. +# This one is supported in standard RFC 4867 representation. + +F010E959F35FDFE5E9667FFBC088818088 +F010E959F35FDFE5E9667FFBC088818088 +F020F871CF48801EC427F0FC3F7318898622062200 +F020F871CF48801EC427F0FC3F7318898622062200 + +# Test passing of CMR from RTP to TRAU frame output: do 8 DHFs once again, +# but step CMR in the opposite order from that of CMI advance. + +7004F89D67F1160935BDE1996840 +7004F89D67F1160935BDE1996840 +600C1FB967F7F1FDF547BF2E61C060 +600C1FB967F7F1FDF547BF2E61C060 +5014E959F35FDFE5E9667FFBC088818088 +5014E959F35FDFE5E9667FFBC088818088 +401CF0B9E6BF68007209C3D3CBC80848A1558C +401CF0B9E6BF68007209C3D3CBC80848A1558C +3024F871CF48801EC427F0FC3F7318898622062200 +3024F871CF48801EC427F0FC3F7318898622062200 +202C42B332081813D7E916E7AA5E80D7FDE812B8C080 +202C42B332081813D7E916E7AA5E80D7FDE812B8C080 +10341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +10341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +003C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +003C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 + +# Automatic adaption to implicit CMI/CMR phase in RTP stream: if the used codec +# mode changes at a point in the received RTP stream where free-running RIF +# alternation would normally produce RIF=1, the phase is flipped by emitting +# two RIF=0 frames in a row. Test this mechanism. + +F004F89D67F1160935BDE1996840 +F004F89D67F1160935BDE1996840 +F004F89D67F1160935BDE1996840 +F014E959F35FDFE5E9667FFBC088818088 +F014E959F35FDFE5E9667FFBC088818088 +F014E959F35FDFE5E9667FFBC088818088 + +# Test encoding of SID and No_Data frames. + +F044FFFFFFFFE4 +F07C +F07C +F04426C7836814 + +# Test marked-bad SID update. + +F04026C7836814 + +# TW-TS-006 extended capabilities: direct setting of RIF that would allow +# multiple RIF=0 or RIF=1 frames in a row (must be No_Speech for RIF=1), +# and explicit mode indication for No_Data frames. + +FC7E04 +FC7E04 +F87E04 +F87E04 + +# ONSET frame followed by some speech frames, also exercising TA+DTXd+TFOE +# extension. + +F07E82 +F214E959F35FDFE5E9667FFBC08881808800 +F214E959F35FDFE5E9667FFBC088818088E1 +F214E959F35FDFE5E9667FFBC08881808802 +F214E959F35FDFE5E9667FFBC08881808803 diff --git a/tests/amr/rtp2trau-bwe2fr.ok b/tests/amr/rtp2trau-bwe2fr.ok new file mode 100644 index 0000000..b166f68 --- /dev/null +++ b/tests/amr/rtp2trau-bwe2fr.ok @@ -0,0 +1,53 @@ +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fffffffffffffffc4e9e33087bbf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8fffffffffffff0e98befa00dc06f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8ffffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8ffffffffff689c697dec00dfa07f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8ffffffffc38e2f4a80080008007ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048d1ffffffffffffe93a78cc09945fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8dfffffffffffffe93a78cc09944fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048d3fffffffffffffc4e9e33087bef80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8dffffffffffffffc4e9e33087bdf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048d5ffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8dfffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048d7fffffffffff0e98befa00dc01f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8dffffffffffff0e98befa00dc00f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048d9ffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8dfffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048dbffffffff689c697dec00dfa06f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8dfffffffff689c697dec00dfa01f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048ddffffffc38e2f4a80080008000ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8dfffffffc38e2f4a80080008005ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048de1854ddcb5aab5c00080008003dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8de1854ddcb5aab5c0008000800fdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fdfffffffffffffc4e9e33087baf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8fbffffffffffff0f18bf7a087bbf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8f9fffffffffff0e98befa00dc05f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8f7ffffffffff871c5f7d003bc07ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8f5ffffffff689c697dec00dfa02f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8f3ffffffc38e2f4a80080008005ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8f01854ddcb5aab5c00080008001dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048c1ffffffffedc7ffffffffffffeffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffffadc7fff26e3c1da07ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff8dc7fff26e3c1da01ffffffffffffffffffffffffffffffffffffffffffff diff --git a/tests/amr/rtp2trau-bwe2hr.ok b/tests/amr/rtp2trau-bwe2hr.ok new file mode 100644 index 0000000..7c72ed1 --- /dev/null +++ b/tests/amr/rtp2trau-bwe2hr.ok @@ -0,0 +1,53 @@ +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +00ff8649e9e3988394b89ef0d7d8e0c1fab6dfff +00fff649e9e3988394909ef0d7d8e0c1fab6dfff +00ff9762bab8e680fbf89e81c5dfb7fbd7d1f7ff +00fff762bab8e680fbd09e81c5dfb7fbd7d1f7ff +00fca478e2fdf480fbe89e80f3ec87e7eff083ef +00fcf478e2fdf480fbd09e80f3ec87e7eff083ef +00fc83c6976fa00bc07f898fc2c1b8bc83d8e380 +00fca3c6976fa00bc05f898fc2c1b8bc83d8e380 +3f078145f7a006f0176208fc4c1ba6f01b003780 +3f77a145f7a006f00f6208fc4c1ba6f01b003780 +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07e145f7a006f0036208fc4c1ba6f01b003780 +0081c051bffffffffff883ffffffffffffffffff +0081d051bffffffffff883ffffffffffffffffff +0081c050fffffffffff883ffffffffffffffffff +0081d050fffffffffff883ffffffffffffffffff +0081c050bffffffffff883ffffffffffffffffff +0081d050bffffffffff883ffffffffffffffffff +0081c36abffffffffff883ffffffffffffffffff +0081d04abffffffffff883ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +0081d26a89d8f89ba0a083ffffffffffffffffff +0081c24a89d8f89ba08883ffffffffffffffffff diff --git a/tests/amr/rtp2trau-oa2fr.ok b/tests/amr/rtp2trau-oa2fr.ok new file mode 100644 index 0000000..acaa511 --- /dev/null +++ b/tests/amr/rtp2trau-oa2fr.ok @@ -0,0 +1,60 @@ +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fffffffffffffffc4e9e33087bbf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8fffffffffffff0e98befa00dc06f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8ffffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8ffffffffff689c697dec00dfa07f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8ffffffffc38e2f4a80080008007ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8ffffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048d5ffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8dfffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048d9ffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8dfffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fdfffffffffffffc4e9e33087baf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8fbffffffffffff0f18bf7a087bbf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8f9fffffffffff0e98befa00dc05f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8f7ffffffffff871c5f7d003bc07ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8f5ffffffff689c697dec00dfa02f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8f3ffffffc38e2f4a80080008005ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8f01854ddcb5aab5c00080008001dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8c1ffffffffedc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffadc7fff26e3c1da06ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff8dc7fff26e3c1da00ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff diff --git a/tests/amr/rtp2trau-oa2hr.ok b/tests/amr/rtp2trau-oa2hr.ok new file mode 100644 index 0000000..08b9ab1 --- /dev/null +++ b/tests/amr/rtp2trau-oa2hr.ok @@ -0,0 +1,60 @@ +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 +00fca478e2fdf480fbe89e80f3ec87e7eff083ef +00fcf478e2fdf480fbd09e80f3ec87e7eff083ef +3f078145f7a006f0176208fc4c1ba6f01b003780 +3f77a145f7a006f00f6208fc4c1ba6f01b003780 +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07e145f7a006f0036208fc4c1ba6f01b003780 +0081c051bffffffffff883ffffffffffffffffff +0081d051bffffffffff883ffffffffffffffffff +0081c050fffffffffff883ffffffffffffffffff +0081d050fffffffffff883ffffffffffffffffff +0081c050bffffffffff883ffffffffffffffffff +0081d050bffffffffff883ffffffffffffffffff +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +0081d36abffffffffff883ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +0081d04abffffffffff883ffffffffffffffffff +0081c26a89d8f89ba0a883ffffffffffffffffff +0081d24a89d8f89ba08083ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +0081d04abffffffffff883ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +0081d04abffffffffff883ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef diff --git a/tests/amr/rtp2trau-oax2fr.ok b/tests/amr/rtp2trau-oax2fr.ok new file mode 100644 index 0000000..04a17bd --- /dev/null +++ b/tests/amr/rtp2trau-oax2fr.ok @@ -0,0 +1,60 @@ +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fffffffffffffffc4e9e33087bbf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8fffffffffffff0e98befa00dc06f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8ffffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8ffffffffff689c697dec00dfa07f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8ffffffffc38e2f4a80080008007ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048e5ffffffffffff0f18bf7a087bdf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8efffffffffffff0f18bf7a087bdf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048e9ffffffffff871c5f7d003bc03ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8efffffffffff871c5f7d003bc06ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048d5ffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8dfffffffffffff0f18bf7a087bef8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048d9ffffffffff871c5f7d003bc01ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8dfffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8fdfffffffffffffc4e9e33087baf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8fbffffffffffff0f18bf7a087bbf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8f9fffffffffff0e98befa00dc05f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8f7ffffffffff871c5f7d003bc07ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8f5ffffffff689c697dec00dfa02f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8f3ffffffc38e2f4a80080008005ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8f01854ddcb5aab5c00080008001dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8ffffffffffffffe93a78cc09947fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8c1ffffffffedc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffadc7fff26e3c1da06ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff8dc7fff26e3c1da00ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff19c7ffffffffffff9ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff19c7ffffffffffff9ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff19c7ffffffffffff8ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff19c7ffffffffffff8ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffffcdc7ffffffffffff8ffffffffffffffffffffffffffffffffffffffffffff +00009804835ffffffffffff0f18bf7a087bbf8079fd88f9fbff083ddbfffffffffffffffffffffff +00009b8c87fffffffffffff0f18bf7a087b8f8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048b5ffffffffffff0f18bf7a087b8f8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8ffffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff diff --git a/tests/amr/rtp2trau-oax2hr.ok b/tests/amr/rtp2trau-oax2hr.ok new file mode 100644 index 0000000..d9a5754 --- /dev/null +++ b/tests/amr/rtp2trau-oax2hr.ok @@ -0,0 +1,60 @@ +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +00fca878e2fdf480fbe09e80f3ec87e7eff083ef +00fcf878e2fdf480fbd89e80f3ec87e7eff083ef +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 +00fca478e2fdf480fbe89e80f3ec87e7eff083ef +00fcf478e2fdf480fbd09e80f3ec87e7eff083ef +3f078145f7a006f0176208fc4c1ba6f01b003780 +3f77a145f7a006f00f6208fc4c1ba6f01b003780 +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffff62bab8e680fbc09e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fcf3c6976fa00bc00f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07e145f7a006f0036208fc4c1ba6f01b003780 +0081c051bffffffffff883ffffffffffffffffff +0081d051bffffffffff883ffffffffffffffffff +0081c050fffffffffff883ffffffffffffffffff +0081d050fffffffffff883ffffffffffffffffff +0081c050bffffffffff883ffffffffffffffffff +0081d050bffffffffff883ffffffffffffffffff +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fffe49e9e3988394989ef0d7d8e0c1fab6dfff +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +0081d36abffffffffff883ffffffffffffffffff +0081c04abffffffffff883ffffffffffffffffff +0081d04abffffffffff883ffffffffffffffffff +0081c26a89d8f89ba0a883ffffffffffffffffff +0081d24a89d8f89ba08083ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081d052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081c052bffffffffff883ffffffffffffffffff +0081d34abffffffffff883ffffffffffffffffff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00f1d04abffffffffff881ffffffffffffffffff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcfc78e2fdf480fbe89e80f3ec87e7eff083ef diff --git a/tests/amr/rtp2trau_amr.c b/tests/amr/rtp2trau_amr.c new file mode 100644 index 0000000..23a2f9a --- /dev/null +++ b/tests/amr/rtp2trau_amr.c @@ -0,0 +1,204 @@ +/* + * This program is an exerciser for AMR TRAU frame interworking facility + * of <osmocom/trau/amr_trau.h> in the direction from RTP input decoding + * to TRAU frame encoding. It reads RTP payloads (BWE, OA or OAX) from + * a TW-TS-005 Annex C hex file and converts them to either TRAU-AMR-16k + * or TRAU-AMR-8k frames as specified on the command line, exercising + * a chain of libosmotrau functions in the process. + * + * Author: Mychaela N. Falconia falcon@freecalypso.org, 2025 - however, + * Mother Mychaela's contributions are NOT subject to copyright. + * No rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/utils.h> +#include <osmocom/codec/codec.h> +#include <osmocom/trau/trau_frame.h> +#include <osmocom/trau/amr_trau.h> + +#include "../trau_conv/tw5reader.h" + +static enum osmo_trau_frame_direction direction; +static bool trau_8k_mode; +static enum osmo_amrt_rtp_format rtp_format; +static struct osmo_amrt_encode_state enc_state; +static FILE *out_file; + +static void emit_hex_frame(const uint8_t *frame, unsigned nbytes) +{ + unsigned n; + + for (n = 0; n < nbytes; n++) + fprintf(out_file, "%02x", frame[n]); + putc('\n', out_file); +} + +static void output_stage_16k(const struct osmo_amrt_if *fr, + const char *filename, unsigned lineno) +{ + struct osmo_trau_frame tf; + ubit_t tf_bits[320]; + uint8_t tf_bytes[40]; + enum osmo_amrt_crc_type crc_type; + int rc; + + crc_type = osmo_amrt_encode_trau_frame_16k(&enc_state, &tf, fr, false); + osmo_amrt_set_trau_crc(&tf, crc_type); + tf.dir = direction; + tf.dl_ta_usec = 0; + rc = osmo_trau_frame_encode(tf_bits, sizeof(tf_bits), &tf); + if (rc != 320) { + fprintf(stderr, + "%s line %u: osmo_trau_frame_encode() returned %d\n", + filename, lineno, rc); + exit(1); + } + osmo_ubit2pbit(tf_bytes, tf_bits, sizeof(tf_bytes) * 8); + emit_hex_frame(tf_bytes, sizeof(tf_bytes)); +} + +static void output_stage_8k(const struct osmo_amrt_if *fr, + const char *filename, unsigned lineno) +{ + struct osmo_trau_frame tf; + ubit_t tf_bits[160]; + uint8_t tf_bytes[20]; + enum osmo_amrt_crc_type crc_type; + int rc; + + crc_type = osmo_amrt_encode_trau_frame_8k(&enc_state, &tf, fr, false, + OSMO_AMRT_OUT8k_NO_RESTRICT); + osmo_amrt_set_trau_crc(&tf, crc_type); + tf.dir = direction; + tf.dl_ta_usec = 0; + rc = osmo_trau_frame_encode(tf_bits, sizeof(tf_bits), &tf); + if (rc != 160) { + fprintf(stderr, + "%s line %u: osmo_trau_frame_encode() returned %d\n", + filename, lineno, rc); + exit(1); + } + osmo_ubit2pbit(tf_bytes, tf_bits, sizeof(tf_bytes) * 8); + emit_hex_frame(tf_bytes, sizeof(tf_bytes)); +} + +static void process_record(const uint8_t *rtp_pl, unsigned rtp_pl_len, + const char *filename, unsigned lineno) +{ + struct osmo_amrt_if fr; + int rc; + + rc = osmo_amrt_decode_rtp(&fr, rtp_pl, rtp_pl_len, rtp_format); + if (rc < 0) { + fprintf(out_file, "# input line %u: RTP decode error\n", + lineno); + } + if (trau_8k_mode) + output_stage_8k(&fr, filename, lineno); + else + output_stage_16k(&fr, filename, lineno); +} + +static void process_file(const char *infname, const char *outfname) +{ + FILE *inf; + unsigned lineno; + uint8_t frame[TWTS005_MAX_FRAME]; + unsigned frame_len; + int rc; + + inf = fopen(infname, "r"); + if (!inf) { + perror(infname); + exit(1); + } + if (outfname) { + out_file = fopen(outfname, "w"); + if (!out_file) { + perror(outfname); + exit(1); + } + } else { + out_file = stdout; + } + + lineno = 0; + for (;;) { + rc = twts005_read_frame(inf, &lineno, frame, &frame_len); + if (rc < 0) { + fprintf(stderr, "%s line %u: not valid TW-TS-005\n", + infname, lineno); + exit(1); + } + if (!rc) + break; + process_record(frame, frame_len, infname, lineno); + } + + fclose(inf); + if (outfname) { + fclose(out_file); + out_file = NULL; + } +} + +int main(int argc, char **argv) +{ + int opt; + extern int optind; + + trau_8k_mode = false; + direction = OSMO_TRAU_DIR_DL; + rtp_format = OSMO_AMRT_RTP_FMT_OA; + while ((opt = getopt(argc, argv, "8bux")) != EOF) { + switch (opt) { + case '8': + trau_8k_mode = true; + break; + case 'b': + rtp_format = OSMO_AMRT_RTP_FMT_BWE; + break; + case 'u': + direction = OSMO_TRAU_DIR_UL; + break; + case 'x': + rtp_format = OSMO_AMRT_RTP_FMT_OAX; + break; + default: + goto usage; + } + } + if (argc < optind + 1 || argc > optind + 2) + goto usage; + + /* encoder initial state */ + enc_state.cmi = AMR_4_75; + enc_state.cmr = trau_8k_mode ? AMR_7_40 : AMR_12_2; + enc_state.rif = 1; + enc_state.dtxd = 1; + enc_state.tfoe = 1; + process_file(argv[optind], argv[optind + 1]); + exit(0); + +usage: fprintf(stderr, "usage: %s [options] input-file [output-file]\n", + argv[0]); + exit(1); +} diff --git a/tests/amr/trau-amr-16k-bwe.ok b/tests/amr/trau-amr-16k-bwe.ok new file mode 100644 index 0000000..18ecf7a --- /dev/null +++ b/tests/amr/trau-amr-16k-bwe.ok @@ -0,0 +1,124 @@ +# input line 23 frame decoding status: good +F06389E1FC02E04D67F800001100 +# input line 24 frame decoding status: good +007649F43382E16D07FA84965980 +# input line 25 frame decoding status: good +0058C8FFFC00C30E7FFDFDE83600 +# input line 26 frame decoding status: good +004DE071FD03418CFFF800000000 +# input line 27 frame decoding status: good +004DA631FD81C10D7FF800000000 +# input line 28 frame decoding status: good +007FE071FD83410CFFF800000000 +# input line 29 frame decoding status: good +004DA631FD81C10D7FF800000000 +# input line 30 frame decoding status: good +74400000000000 +# input line 31 frame decoding status: good +77C0 +# input line 32 frame decoding status: good +77C0 +# input line 33 frame decoding status: good +7449B1E0DA0400 +# input line 34 frame decoding status: good +77C0 +# input line 55 frame decoding status: good +F07E2759FC45824D6F78665A1000 +# input line 56 frame decoding status: good +007E2759FC45824D6F78665A1000 +# input line 57 frame decoding status: good +00C7EE59FDFC7F7D51EFCB98701800 +# input line 58 frame decoding status: good +10C7EE59FDFC7F7D51EFCB98701800 +# input line 59 frame decoding status: good +117A567CD7F7F97A599FFEF022206022 +# input line 60 frame decoding status: good +217A567CD7F7F97A599FFEF022206022 +# input line 61 frame decoding status: good +21FC2E79AFDA001C8270F4F2F20212285563 +# input line 62 frame decoding status: good +31FC2E79AFDA001C8270F4F2F20212285563 +# input line 63 frame decoding status: good +327E1C73D22007B109FC3F0FDCC6226188818880 +# input line 64 frame decoding status: good +427E1C73D22007B109FC3F0FDCC6226188818880 +# input line 65 frame decoding status: good +42D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +# input line 66 frame decoding status: good +52D0ACCC820604F5FA45B9EA97A035FF7A04AE302000 +# input line 67 frame decoding status: good +5347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +# input line 68 frame decoding status: good +6347F1C8B1E200CA2A70A000C2F25D570FBD467E00000A54C8F800 +# input line 69 frame decoding status: good +63C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 70 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 94 frame decoding status: UFE +F7C0 +# input line 95 frame decoding status: good +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 96 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 97 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 107 frame decoding status: good +77C0 +# input line 108 frame decoding status: good +77C0 +# input line 109 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 110 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 111 frame decoding status: good +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 112 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 113 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 114 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 115 frame decoding status: UFE +F7C0 +# input line 116 frame decoding status: good +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 117 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 124 frame decoding status: good +77C0 +# input line 125 frame decoding status: good +77C0 +# input line 126 frame decoding status: good +77C0 +# input line 127 frame decoding status: good +77C0 +# input line 128 frame decoding status: good +77C0 +# input line 129 frame decoding status: good +77C0 +# input line 130 frame decoding status: good +77C0 +# input line 131 frame decoding status: good +77C0 +# input line 137 frame decoding status: good +7449B1E0DA0400 +# input line 138 frame decoding status: good +7449B1E0DA0500 +# input line 139 frame decoding status: good +7449B1E0DA0480 +# input line 140 frame decoding status: good +7449B1E0DA0780 +# input line 146 frame decoding status: good +77C0 +# input line 147 frame decoding status: UFE +F7C0 +# input line 148 frame decoding status: good +F3C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 149 frame decoding status: UFE +7382155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 150 frame decoding status: good +73C2155B65131C68682079FAB4810911200003B360AE0446000025F11E539DD0 +# input line 151 frame decoding status: UFE +7382155B65131C6868207BFAB4810911200003B360AE0446000025F11E539DD0 +# input line 152 frame decoding status: UFE +F7C0 diff --git a/tests/amr/trau-amr-16k-oa.ok b/tests/amr/trau-amr-16k-oa.ok new file mode 100644 index 0000000..858ec63 --- /dev/null +++ b/tests/amr/trau-amr-16k-oa.ok @@ -0,0 +1,124 @@ +# input line 23 frame decoding status: good +F0048E2787F00B81359FE0000044 +# input line 24 frame decoding status: good +0004D927D0CE0B85B41FEA125966 +# input line 25 frame decoding status: good +00046323FFF0030C39FFF7F7A0D8 +# input line 26 frame decoding status: good +00043781C7F40D0633FFE0000000 +# input line 27 frame decoding status: good +00043698C7F6070435FFE0000000 +# input line 28 frame decoding status: good +0004FF81C7F60D0433FFE0000000 +# input line 29 frame decoding status: good +00043698C7F6070435FFE0000000 +# input line 30 frame decoding status: good +70440000000000 +# input line 31 frame decoding status: good +707C +# input line 32 frame decoding status: good +707C +# input line 33 frame decoding status: good +704426C7836810 +# input line 34 frame decoding status: good +707C +# input line 55 frame decoding status: good +F004F89D67F1160935BDE1996840 +# input line 56 frame decoding status: good +0004F89D67F1160935BDE1996840 +# input line 57 frame decoding status: good +000C1FB967F7F1FDF547BF2E61C060 +# input line 58 frame decoding status: good +100C1FB967F7F1FDF547BF2E61C060 +# input line 59 frame decoding status: good +1014E959F35FDFE5E9667FFBC088818088 +# input line 60 frame decoding status: good +2014E959F35FDFE5E9667FFBC088818088 +# input line 61 frame decoding status: good +201CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 62 frame decoding status: good +301CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 63 frame decoding status: good +3024F871CF48801EC427F0FC3F7318898622062200 +# input line 64 frame decoding status: good +4024F871CF48801EC427F0FC3F7318898622062200 +# input line 65 frame decoding status: good +402C42B332081813D7E916E7AA5E80D7FDE812B8C080 +# input line 66 frame decoding status: good +502C42B332081813D7E916E7AA5E80D7FDE812B8C080 +# input line 67 frame decoding status: good +50341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +# input line 68 frame decoding status: good +60341FC722C7880328A9C280030BC9755C3EF519F80000295323E000 +# input line 69 frame decoding status: good +603C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 70 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 94 frame decoding status: UFE +F07C +# input line 95 frame decoding status: good +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 96 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 97 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 107 frame decoding status: good +707C +# input line 108 frame decoding status: good +707C +# input line 109 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 110 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 111 frame decoding status: good +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 112 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 113 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 114 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 115 frame decoding status: UFE +F07C +# input line 116 frame decoding status: good +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 117 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 124 frame decoding status: good +707C +# input line 125 frame decoding status: good +707C +# input line 126 frame decoding status: good +707C +# input line 127 frame decoding status: good +707C +# input line 128 frame decoding status: good +707C +# input line 129 frame decoding status: good +707C +# input line 130 frame decoding status: good +707C +# input line 131 frame decoding status: good +707C +# input line 137 frame decoding status: good +704426C7836810 +# input line 138 frame decoding status: good +704426C7836814 +# input line 139 frame decoding status: good +704426C7836812 +# input line 140 frame decoding status: good +704426C783681E +# input line 146 frame decoding status: good +707C +# input line 147 frame decoding status: UFE +F07C +# input line 148 frame decoding status: good +F03C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 149 frame decoding status: UFE +703808556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 150 frame decoding status: good +703C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E7740 +# input line 151 frame decoding status: UFE +703808556D944C71A1A081EFEAD204244480000ECD82B81118000097C4794E7740 +# input line 152 frame decoding status: UFE +F07C diff --git a/tests/amr/trau-amr-16k-oax.ok b/tests/amr/trau-amr-16k-oax.ok new file mode 100644 index 0000000..2073ad1 --- /dev/null +++ b/tests/amr/trau-amr-16k-oax.ok @@ -0,0 +1,124 @@ +# input line 23 frame decoding status: good +FA048E2787F00B81359FE000004403 +# input line 24 frame decoding status: good +0E04D927D0CE0B85B41FEA12596603 +# input line 25 frame decoding status: good +0A046323FFF0030C39FFF7F7A0D803 +# input line 26 frame decoding status: good +0E043781C7F40D0633FFE000000003 +# input line 27 frame decoding status: good +0A043698C7F6070435FFE000000003 +# input line 28 frame decoding status: good +0E04FF81C7F60D0433FFE000000003 +# input line 29 frame decoding status: good +0A043698C7F6070435FFE000000003 +# input line 30 frame decoding status: good +7E44FFFFFFFFE003 +# input line 31 frame decoding status: good +7A7E0003 +# input line 32 frame decoding status: good +7E7E0003 +# input line 33 frame decoding status: good +7A4426C783681003 +# input line 34 frame decoding status: good +7E7E0003 +# input line 55 frame decoding status: good +FA04F89D67F1160935BDE199684003 +# input line 56 frame decoding status: good +0E04F89D67F1160935BDE199684003 +# input line 57 frame decoding status: good +0A0C1FB967F7F1FDF547BF2E61C06003 +# input line 58 frame decoding status: good +1E0C1FB967F7F1FDF547BF2E61C06003 +# input line 59 frame decoding status: good +1A14E959F35FDFE5E9667FFBC08881808803 +# input line 60 frame decoding status: good +2E14E959F35FDFE5E9667FFBC08881808803 +# input line 61 frame decoding status: good +2A1CF0B9E6BF68007209C3D3CBC80848A1558C03 +# input line 62 frame decoding status: good +3E1CF0B9E6BF68007209C3D3CBC80848A1558C03 +# input line 63 frame decoding status: good +3A24F871CF48801EC427F0FC3F731889862206220003 +# input line 64 frame decoding status: good +4E24F871CF48801EC427F0FC3F731889862206220003 +# input line 65 frame decoding status: good +4A2C42B332081813D7E916E7AA5E80D7FDE812B8C08003 +# input line 66 frame decoding status: good +5E2C42B332081813D7E916E7AA5E80D7FDE812B8C08003 +# input line 67 frame decoding status: good +5A341FC722C7880328A9C280030BC9755C3EF519F80000295323E00003 +# input line 68 frame decoding status: good +6E341FC722C7880328A9C280030BC9755C3EF519F80000295323E00003 +# input line 69 frame decoding status: good +6A3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 70 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 94 frame decoding status: UFE +F07C +# input line 95 frame decoding status: good +FA3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 96 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 97 frame decoding status: good +7A3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 107 frame decoding status: good +7E7E0703 +# input line 108 frame decoding status: good +7A7E0703 +# input line 109 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 110 frame decoding status: good +7A3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 111 frame decoding status: good +FA3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 112 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 113 frame decoding status: good +7A3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 114 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 115 frame decoding status: UFE +F07C +# input line 116 frame decoding status: good +FA3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 117 frame decoding status: good +7E3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 124 frame decoding status: good +7E7E0003 +# input line 125 frame decoding status: good +7E7E0203 +# input line 126 frame decoding status: good +7E7E0403 +# input line 127 frame decoding status: good +7E7E0703 +# input line 128 frame decoding status: good +7A7E0003 +# input line 129 frame decoding status: good +7A7E0203 +# input line 130 frame decoding status: good +7A7E0403 +# input line 131 frame decoding status: good +7A7E0703 +# input line 137 frame decoding status: good +7A4426C783681003 +# input line 138 frame decoding status: good +7A4426C783681403 +# input line 139 frame decoding status: good +7A4426C783681203 +# input line 140 frame decoding status: good +7A4426C783681E03 +# input line 146 frame decoding status: good +7A7E0403 +# input line 147 frame decoding status: UFE +F07C +# input line 148 frame decoding status: good +FA3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 149 frame decoding status: UFE +7E3808556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 150 frame decoding status: good +7A3C08556D944C71A1A081E7EAD204244480000ECD82B81118000097C4794E774003 +# input line 151 frame decoding status: UFE +7E3808556D944C71A1A081EFEAD204244480000ECD82B81118000097C4794E774003 +# input line 152 frame decoding status: UFE +F07C diff --git a/tests/amr/trau-amr-16k.in b/tests/amr/trau-amr-16k.in new file mode 100644 index 0000000..004ad00 --- /dev/null +++ b/tests/amr/trau-amr-16k.in @@ -0,0 +1,152 @@ +# This unit test data file is a collection of TRAU-AMR-16k frames, to be +# presented to osmo_amrt_decode_trau_frame() in sequence by way of trau2rtp_amr +# test program. + +# The following frames have been emitted by Nokia TCSM2 TRAU under these +# conditions: +# +# - PCM circuit type F; +# - TRAU channel activated in AMR mode with DTXd enabled; +# - CMR set to 0 (MR475) in the UL frame stream fed to the TRAU; +# - G.711 PCMA input was constant 0x54 (idle). +# +# Since the input was a form of silence (but not EHF) and DTXd was enabled, +# the TRAU emitted 7 speech frames (encoding the silence) followed by AMR DTX +# pattern of one Sid_Update every 8 frames. +# +# Please note that unlike FR/EFR, AMR TRAU frame format and semantics are +# exactly the same in UL and DL directions. Therefore, frames emitted +# by a historical hardware TRAU are just as good for unit test purposes +# as those emitted by an E1 BTS - but much easier to produce in Themyscira lab. + +RESET +000098048f1fffffffffffe3c4ebb0809900f801be0e909e81afffffffffffffffffffffffffffff +0000980c8f1ffffffffffff6a4ebba26fb3f8083ff1e90878bffffffffffffffffffffffffffffff +000098048f1fffffffffffd8e471bff9d803ff7dbd3e809ee5afffffffffffffffffffffffffffff +0000980c8f1fffffffffffcdf02cf8a09895f80dbc06809e81afffffffffffffffffffffffffffff +000098048f1fffffffffffcdd307b8a09837f80dbc06809e81afffffffffffffffffffffffffffff +0000980c8f1ffffffffffffff02cf8a09831f80dbc06809e81afffffffffffffffffffffffffffff +000098048f1fffffffffffcdd307b8a09837f80dbc06809e81afffffffffffffffffffffffffffff +0000980c8c1ffffffffe9c7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff09c7ffffffffffffeffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff09c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffa9c7fff26e3c1da05ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff09c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff + +# The following frames have been emitted by the same Nokia TCSM2 TRAU +# under these slightly different conditions: +# +# - G.711 PCMA input was constant 0xD5 (silence); +# - CMR in the UL frame stream fed to the TRAU (itt-ater-16 test program) +# was manually stepped through all 8 modes. +# +# G.711 A-law (but not mu-law) has this quirk whereby the silence code (0xD5) +# decodes into 0x0008 in 16-bit linear PCM, rather than solid 0 - and all GSM +# speech encoders, including AMR, interpret a frame of all 0x0008 samples as +# an encoder homing frame (EHF). TCSM2 AMR speech encoder also behaves in this +# manner, hence the DL output from the TRAU channel was nothing but endless +# DHFs. This stream of DHFs exhibits mode switches on RIF=0 output frames +# whenever CMR input to the TRAU was changed. +# +# The following extract exhibits just two frames of each mode, a pair of +# RIF=0 and RIF=1 output. + +RESET +000098048f1ffffffffffffe93a78cc09946fb85bd8c883ecdafffffffffffffffffffffffffffff +0000980c8f1ffffffffffffe93a78cc09941fb85bd8c883ecdafffffffffffffffffffffffffffff +000098048f3fffffffffffffc4e9e33087b8f80cadf6ff75f47bffffffffffffffffffffffffffff +0000980c8f3fffffffffffffc4e9e33087bdf80cadf6ff75f47bffffffffffffffffffffffffffff +000098048f5ffffffffffff0f18bf7a087bcf8079fd88f9fbff083ddbfffffffffffffffffffffff +0000980c8f5ffffffffffff0f18bf7a087bff8079fd88f9fbff083ddbfffffffffffffffffffffff +000098048f7fffffffffff0e98befa00dc07f898fc21c171bc03fb18f013ffffffffffffffffffff +0000980c8f7fffffffffff0e98befa00dc02f898fc21c171bc03fb18f013ffffffffffffffffffff +000098048f9ffffffffff871c5f7d003bc04ec418fc5ec1bd37886d880de807fffffffffffffffff +0000980c8f9ffffffffff871c5f7d003bc06ec418fc5ec1bd37886d880de807fffffffffffffffff +000098048fbffffffff689c697dec00dfa00f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +0000980c8fbffffffff689c697dec00dfa05f3c0c3d2ed8e9a6f881f94d0bc83bfffffffffffffff +000098048fdffffffc38e2f4a80080008002ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +0000980c8fdffffffc38e2f4a80080008001ed72b19dfd469881ad6b9ec09f60df21ab868fffffef +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f + +# Note this quirk of Nokia TCSM2, visible in all of the DL outputs showcased +# above: when it emits speech frames, it sets CMR in its DL output to the same +# mode as its speech output mode (DL CMI, controlled by UL CMR) - but when it +# emits AMR DTX pattern (Sid_First, then occasional Sid_Update among No_Data +# frames), it emits CMR=7 in that DTXd output. This behaviour is peculiar, +# and may be considered a bug: codec modes in UL and DL directions are +# independent of each other, hence there is no good reason for a TRAU to tell +# the BTS to limit its UL mode based on what the BTS told the TRAU regarding +# DL mode. TS 48.060 even tells TRAU implementors that they should always emit +# CMR=7 in DL output when they are not forwarding frames from TFO - but Nokia +# implemented it wrong on TCSM2. According to some docs, this bug is fixed in +# Nokia TCSM3i - but Themyscira HQ does not have that hw. +# +# When this quirk of TCSM2 is combined with CMR/CMI alternation in sequences +# of speech frames and the stream of TRAU frames is converted to RTP, the +# change in CMR will lag the change in FT by one frame. + +# Test decoder behaviour when a RIF=1 frame occurs first out of reset, +# followed by RIF=0. Mode 7 DHFs were taken from TCSM2 output above, +# but reordered for this test. + +RESET +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f + +# The following test sequence, pieced together from TCSM2 DL outputs with some +# creative reordering, exercises the following cases: +# +# - Beginning with RIF=1, but on a No_Data frame; +# - Two RIF=0 speech frames in a row; +# - Two RIF=1 speech frames in a row. + +RESET +0000980c8c1ffffffff1fc7ffffffffffffeffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff1fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f + +# No_Data frames in a weird, unnatural order that can never be emitted by a +# correctly functioning BTS or TRAU - but because these frames are a form of +# No_Speech, our library should decode them correctly no matter what. + +RESET +0000980c8c1ffffffff09c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff0dc7ffffffffffffcffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff19c7ffffffffffff9ffffffffffffffffffffffffffffffffffffffffffff +0000980c8c1ffffffff1fc7ffffffffffffeffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff09c7ffffffffffffeffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff0dc7ffffffffffffdffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff19c7ffffffffffff8ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff1fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff + +# Sid_Update frames for modes 0, 2, 4 and 7, extracted from a TCSM2 session +# in DTXd mode, but grouped together in an unnatural way for this test. + +RESET +000098048c1ffffffffa9c7fff26e3c1da05ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffadc7fff26e3c1da06ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffb9c7fff26e3c1da03ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffffbfc7fff26e3c1da04ffffffffffffffffffffffffffffffffffffffffffff + +# Test handling of CRC errors. Each test frame was constructed by taking one +# from TCSM2 DL output and corrupting some bits. + +RESET +000098048c1ffffffff19c7ffffffffffff8ffffffffffffffffffffffffffffffffffffffffffff +000098048c1ffffffff19c7ffffffffffffbffffffffffffffffffffffffffffffffffffffffffff +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438dac8480bc7ed01f +000098048fe1854ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f +0000980c8fe1854ddcb5aab5c00080008007dac3fd9a86f2c7128041ac2abc438fac8480bc7ed05f +000098048fe1855ddcb5aab5c0008000800bdac3fd9a86f2c7128041ac2abc438dac8480bc7ed05f diff --git a/tests/amr/trau-amr-8k-bwe.ok b/tests/amr/trau-amr-8k-bwe.ok new file mode 100644 index 0000000..502bf07 --- /dev/null +++ b/tests/amr/trau-amr-8k-bwe.ok @@ -0,0 +1,66 @@ +# input line 23 frame decoding status: good +F0764DE1FC07004CE7F800001000 +# input line 24 frame decoding status: good +0058CB31FC13610CFFFC80004000 +# input line 25 frame decoding status: good +004DFAB1FC19C10D7FF800000000 +# input line 26 frame decoding status: good +004D8F31FC1B410CFFF800000000 +# input line 27 frame decoding status: good +007FC631FC19C10D7FF800000000 +# input line 28 frame decoding status: good +004D8F31FC1B410CFFF800000000 +# input line 29 frame decoding status: good +007FC631FC19C10D7FF800000000 +# input line 30 frame decoding status: good +04400000000000 +# input line 31 frame decoding status: good +07C0 +# input line 32 frame decoding status: good +07C0 +# input line 33 frame decoding status: good +0449B1E0DA0400 +# input line 34 frame decoding status: good +07C0 +# input line 56 frame decoding status: good +F07E2759FC45824D6F78665A1000 +# input line 57 frame decoding status: good +007E2759FC45824D6F78665A1000 +# input line 58 frame decoding status: good +00C7EE59FDFC7F7D51EFCB98701800 +# input line 59 frame decoding status: good +10C7EE59FDFC7F7D51EFCB98701800 +# input line 60 frame decoding status: good +117A567CD7F7F97A599FFEF022206022 +# input line 61 frame decoding status: good +217A567CD7F7F97A599FFEF022206022 +# input line 62 frame decoding status: good +21FC2E79AFDA001C8270F4F2F20212285563 +# input line 63 frame decoding status: good +31FC2E79AFDA001C8270F4F2F20212285563 +# input line 64 frame decoding status: good +327E1C73D22007B109FC3F0FDCC6226188818880 +# input line 65 frame decoding status: good +427E1C73D22007B109FC3F0FDCC6226188818880 +# input line 82 frame decoding status: UFE +F7C0 +# input line 83 frame decoding status: good +F17A567CD7F7F97A599FFEF022206022 +# input line 84 frame decoding status: good +217A567CD7F7F97A599FFEF022206022 +# input line 85 frame decoding status: good +217A567CD7F7F97A599FFEF022206022 +# input line 91 frame decoding status: good +07C0 +# input line 92 frame decoding status: good +07C0 +# input line 93 frame decoding status: DFE +07C0 +# input line 94 frame decoding status: good +07C0 +# input line 95 frame decoding status: good +07C0 +# input line 96 frame decoding status: good +07C0 +# input line 97 frame decoding status: good +07C0 diff --git a/tests/amr/trau-amr-8k-oa.ok b/tests/amr/trau-amr-8k-oa.ok new file mode 100644 index 0000000..6269582 --- /dev/null +++ b/tests/amr/trau-amr-8k-oa.ok @@ -0,0 +1,66 @@ +# input line 23 frame decoding status: good +F004D93787F01C01339FE0000040 +# input line 24 frame decoding status: good +0004632CC7F04D8433FFF2000100 +# input line 25 frame decoding status: good +000437EAC7F0670435FFE0000000 +# input line 26 frame decoding status: good +0004363CC7F06D0433FFE0000000 +# input line 27 frame decoding status: good +0004FF18C7F0670435FFE0000000 +# input line 28 frame decoding status: good +0004363CC7F06D0433FFE0000000 +# input line 29 frame decoding status: good +0004FF18C7F0670435FFE0000000 +# input line 30 frame decoding status: good +00440000000000 +# input line 31 frame decoding status: good +007C +# input line 32 frame decoding status: good +007C +# input line 33 frame decoding status: good +004426C7836810 +# input line 34 frame decoding status: good +007C +# input line 56 frame decoding status: good +F004F89D67F1160935BDE1996840 +# input line 57 frame decoding status: good +0004F89D67F1160935BDE1996840 +# input line 58 frame decoding status: good +000C1FB967F7F1FDF547BF2E61C060 +# input line 59 frame decoding status: good +100C1FB967F7F1FDF547BF2E61C060 +# input line 60 frame decoding status: good +1014E959F35FDFE5E9667FFBC088818088 +# input line 61 frame decoding status: good +2014E959F35FDFE5E9667FFBC088818088 +# input line 62 frame decoding status: good +201CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 63 frame decoding status: good +301CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 64 frame decoding status: good +3024F871CF48801EC427F0FC3F7318898622062200 +# input line 65 frame decoding status: good +4024F871CF48801EC427F0FC3F7318898622062200 +# input line 82 frame decoding status: UFE +F07C +# input line 83 frame decoding status: good +F014E959F35FDFE5E9667FFBC088818088 +# input line 84 frame decoding status: good +2014E959F35FDFE5E9667FFBC088818088 +# input line 85 frame decoding status: good +2014E959F35FDFE5E9667FFBC088818088 +# input line 91 frame decoding status: good +007C +# input line 92 frame decoding status: good +007C +# input line 93 frame decoding status: DFE +007C +# input line 94 frame decoding status: good +007C +# input line 95 frame decoding status: good +007C +# input line 96 frame decoding status: good +007C +# input line 97 frame decoding status: good +007C diff --git a/tests/amr/trau-amr-8k-oax.ok b/tests/amr/trau-amr-8k-oax.ok new file mode 100644 index 0000000..4e85db8 --- /dev/null +++ b/tests/amr/trau-amr-8k-oax.ok @@ -0,0 +1,66 @@ +# input line 23 frame decoding status: good +F804D93787F01C01339FE0000040 +# input line 24 frame decoding status: good +0C04632CC7F04D8433FFF2000100 +# input line 25 frame decoding status: good +080437EAC7F0670435FFE0000000 +# input line 26 frame decoding status: good +0C04363CC7F06D0433FFE0000000 +# input line 27 frame decoding status: good +0804FF18C7F0670435FFE0000000 +# input line 28 frame decoding status: good +0C04363CC7F06D0433FFE0000000 +# input line 29 frame decoding status: good +0804FF18C7F0670435FFE0000000 +# input line 30 frame decoding status: good +0E44FFFFFFFFE003 +# input line 31 frame decoding status: good +0A7E0003 +# input line 32 frame decoding status: good +0E7E0003 +# input line 33 frame decoding status: good +0A4426C783681003 +# input line 34 frame decoding status: good +0E7E0003 +# input line 56 frame decoding status: good +F804F89D67F1160935BDE1996840 +# input line 57 frame decoding status: good +0C04F89D67F1160935BDE1996840 +# input line 58 frame decoding status: good +080C1FB967F7F1FDF547BF2E61C060 +# input line 59 frame decoding status: good +1C0C1FB967F7F1FDF547BF2E61C060 +# input line 60 frame decoding status: good +1814E959F35FDFE5E9667FFBC088818088 +# input line 61 frame decoding status: good +2C14E959F35FDFE5E9667FFBC088818088 +# input line 62 frame decoding status: good +281CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 63 frame decoding status: good +3C1CF0B9E6BF68007209C3D3CBC80848A1558C +# input line 64 frame decoding status: good +3824F871CF48801EC427F0FC3F7318898622062200 +# input line 65 frame decoding status: good +4C24F871CF48801EC427F0FC3F7318898622062200 +# input line 82 frame decoding status: UFE +F07C +# input line 83 frame decoding status: good +F814E959F35FDFE5E9667FFBC088818088 +# input line 84 frame decoding status: good +2C14E959F35FDFE5E9667FFBC088818088 +# input line 85 frame decoding status: good +2814E959F35FDFE5E9667FFBC088818088 +# input line 91 frame decoding status: good +0E7E0003 +# input line 92 frame decoding status: good +0A7E0003 +# input line 93 frame decoding status: DFE +0E7E0003 +# input line 94 frame decoding status: good +0A7E0003 +# input line 95 frame decoding status: good +0A7E0003 +# input line 96 frame decoding status: good +0E7E0003 +# input line 97 frame decoding status: good +0E7E0003 diff --git a/tests/amr/trau-amr-8k.in b/tests/amr/trau-amr-8k.in new file mode 100644 index 0000000..ad42413 --- /dev/null +++ b/tests/amr/trau-amr-8k.in @@ -0,0 +1,97 @@ +# This unit test data file is a collection of TRAU-AMR-8k frames, to be +# presented to osmo_amrt_decode_trau_frame() in sequence by way of trau2rtp_amr +# test program. + +# The following frames have been emitted by Nokia TCSM2 TRAU under these +# conditions: +# +# - PCM circuit type F; +# - TRAU channel activated in AMR-8k mode with DTXd enabled; +# - CMR set to 0 (MR475) in the UL frame stream fed to the TRAU; +# - G.711 PCMA input was constant 0x54 (idle). +# +# Since the input was a form of silence (but not EHF) and DTXd was enabled, +# the TRAU emitted 7 speech frames (encoding the silence) followed by AMR DTX +# pattern of one Sid_Update every 8 frames. +# +# Please note that unlike FR/EFR, AMR TRAU frame format and semantics are +# exactly the same in UL and DL directions. Therefore, frames emitted +# by a historical hardware TRAU are just as good for unit test purposes +# as those emitted by an E1 BTS - but much easier to produce in Themyscira lab. + +RESET +00fd8e53bb94908390889e8097c0f0c1f886dfff +00f6bc72e39e958f80889e81d7c0b192f886dfff +00f38d7ed1ee948380989e81d7c0b0b5f886dfff +00f3bd63e39e948380b89e81d7c0b0b5f886dfff +00ff8f71c1ee948380a09e81d7c0b0b5f886dfff +00f3bd63e39e948380b89e81d7c0b0b5f886dfff +00ff8f71c1ee948380a09e81d7c0b0b5f886dfff +0081d360bffffffffff883ffffffffffffffffff +0081c040bffffffffff883ffffffffffffffffff +0081d040bffffffffff883ffffffffffffffffff +0081c26089d8f89ba09883ffffffffffffffffff +0081d040bffffffffff883ffffffffffffffffff + +# The following frames have been emitted by the same Nokia TCSM2 TRAU +# under these slightly different conditions: +# +# - G.711 PCMA input was constant 0xD5 (silence); +# - CMR in the UL frame stream fed to the TRAU (itt-ater-16 test program) +# was manually stepped through modes 0 through 4, the 5 possible modes +# for TRAU-AMR-8k. +# +# G.711 A-law (but not mu-law) has this quirk whereby the silence code (0xD5) +# decodes into 0x0008 in 16-bit linear PCM, rather than solid 0 - and all GSM +# speech encoders, including AMR, interpret a frame of all 0x0008 samples as +# an encoder homing frame (EHF). TCSM2 AMR speech encoder also behaves in this +# manner, hence the DL output from the TRAU channel was nothing but endless +# DHFs. This stream of DHFs exhibits mode switches on RIF=0 output frames +# whenever CMR input to the TRAU was changed. +# +# The following extract exhibits just two frames of each mode, a pair of +# RIF=0 and RIF=1 output. + +RESET +00ff8e49e9e3988394b09ef0d7d8e0c1fab6dfff +00ffbe49e9e3988394809ef0d7d8e0c1fab6dfff +00ff9f62bab8e680fbe89e81c5dfb7fbd7d1f7ff +00ffcf62bab8e680fbf89e81c5dfb7fbd7d1f7ff +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcdc78e2fdf480fbe09e80f3ec87e7eff083ef +00fc93c6976fa00bc06f898fc2c1b8bc83d8e380 +00fce3c6976fa00bc01f898fc2c1b8bc83d8e380 +3f079145f7a006f0076208fc4c1ba6f01b003780 +3f07f145f7a006f0136208fc4c1ba6f01b003780 + +# Note this quirk of Nokia TCSM2, visible in all of the DL outputs showcased +# above: for both speech and DTXd No_Speech frames in 8k mode, it sets CMR in +# its DL output to the same mode as its speech output mode (DL CMI, controlled +# by UL CMR). See the corresponding comment in trau-amr-16k.in for further +# explanation. +# +# When this quirk of TCSM2 is combined with CMR/CMI alternation in sequences +# of speech frames and the stream of TRAU frames is converted to RTP, the +# change in CMR will lag the change in FT by one frame. + +# Test decoder behaviour when a RIF=1 frame occurs first out of reset, +# followed by RIF=0. Mode 2 DHFs were taken from TCSM2 output above, +# but reordered for this test. + +RESET +00fcdc78e2fdf480fbe09e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef +00fcdc78e2fdf480fbe09e80f3ec87e7eff083ef +00fcac78e2fdf480fbd09e80f3ec87e7eff083ef + +# Test decoding of No_Data frames beginning with RIF=1, including shifts in +# RIF order (repeated RIF=0 or RIF=1) and UFE/DFE indication. + +RESET +0081d040bffffffffff883ffffffffffffffffff +0081c040bffffffffff883ffffffffffffffffff +0080d040bffffffffff883ffffffffffffffffff +0081c040bffffffffff883ffffffffffffffffff +0081c040bffffffffff883ffffffffffffffffff +0081d040bffffffffff883ffffffffffffffffff +0081d040bffffffffff883ffffffffffffffffff diff --git a/tests/amr/trau2rtp_amr.c b/tests/amr/trau2rtp_amr.c new file mode 100644 index 0000000..b081e2d --- /dev/null +++ b/tests/amr/trau2rtp_amr.c @@ -0,0 +1,249 @@ +/* + * This program is an exerciser for AMR TRAU frame interworking facility + * of <osmocom/trau/amr_trau.h> in the direction from TRAU frame decoding + * to RTP output encoding. + * + * Author: Mychaela N. Falconia falcon@freecalypso.org, 2025 - however, + * Mother Mychaela's contributions are NOT subject to copyright. + * No rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/utils.h> +#include <osmocom/trau/trau_frame.h> +#include <osmocom/trau/amr_trau.h> + +static enum osmo_trau_frame_direction direction; +static bool trau_8k_mode; +static enum osmo_amrt_rtp_format rtp_format; +static bool allow_config_prot; +static struct osmo_amrt_decode_state dec_state; +static FILE *out_file; + +static void do_trau_frame_16k(struct osmo_trau_frame *tf, const char *hex_str) +{ + uint8_t input_bytes[40]; + ubit_t trau_bits[320]; + int rc; + + osmo_hexparse(hex_str, input_bytes, sizeof(input_bytes)); + osmo_pbit2ubit(trau_bits, input_bytes, sizeof(trau_bits)); + rc = osmo_trau_frame_decode_16k(tf, trau_bits, direction); + if (rc < 0) { + fprintf(stderr, + "error: osmo_trau_frame_decode_16k() returned %d\n", + rc); + exit(1); + } +} + +static void do_trau_frame_8k(struct osmo_trau_frame *tf, const char *hex_str) +{ + uint8_t input_bytes[20]; + ubit_t trau_bits[160]; + int rc; + + osmo_hexparse(hex_str, input_bytes, sizeof(input_bytes)); + osmo_pbit2ubit(trau_bits, input_bytes, sizeof(trau_bits)); + rc = osmo_trau_frame_decode_8k(tf, trau_bits, direction); + if (rc < 0) { + fprintf(stderr, + "error: osmo_trau_frame_decode_8k() returned %d\n", + rc); + exit(1); + } +} + +static void emit_hex_frame(const uint8_t *frame, unsigned nbytes) +{ + unsigned n; + + for (n = 0; n < nbytes; n++) + fprintf(out_file, "%02X", frame[n]); + putc('\n', out_file); +} + +static void process_record(const char *hex_input, int lineno) +{ + struct osmo_trau_frame tf; + struct osmo_amrt_if fr; + enum osmo_amrt_decode_result dec_rc; + const char *dec_rc_str; + uint8_t rtp_pl[40]; /* maximum RTP payload length for TW-TS-005 */ + int rc; + + if (trau_8k_mode) + do_trau_frame_8k(&tf, hex_input); + else + do_trau_frame_16k(&tf, hex_input); + dec_rc = osmo_amrt_decode_trau_frame(&dec_state, &fr, &tf, + allow_config_prot); + switch (dec_rc) { + case OSMO_AMRT_FRAME_DEC_GOOD: + dec_rc_str = "good"; + break; + case OSMO_AMRT_FRAME_DEC_BAD: + dec_rc_str = "UFE"; + break; + case OSMO_AMRT_FRAME_DEC_REMOTE_BAD: + dec_rc_str = "DFE"; + break; + default: + dec_rc_str = "invalid code"; + } + fprintf(out_file, "# input line %d frame decoding status: %s\n", + lineno, dec_rc_str); + + rc = osmo_amrt_encode_rtp(rtp_pl, sizeof(rtp_pl), &fr, rtp_format); + if (rc < 0) { + fprintf(stderr, "error: osmo_amrt_encode_rtp() returned %d\n", + rc); + exit(1); + } + if (rc == 0) { + /* TW-TS-005 section 4.3.1 */ + fputs("NULL\n", out_file); + return; + } + emit_hex_frame(rtp_pl, rc); +} + +static void reset_dec_state(void) +{ + dec_state.cmi_valid = false; + dec_state.cmr_valid = false; +} + +static void process_line(char *linebuf, const char *infname, int lineno) +{ + char *cp = linebuf, *hex_str; + int ndig, expect_digits; + + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return; + /* allow RESET keyword in this stateful AMR version */ + if (strncmp(cp, "RESET", 5) == 0) { + reset_dec_state(); + return; + } + /* expect string of 40 or 80 hex digits */ + if (trau_8k_mode) + expect_digits = 40; + else + expect_digits = 80; + hex_str = cp; + for (ndig = 0; ndig < expect_digits; ndig++) { + if (!isxdigit(*cp)) + goto inv_syntax; + cp++; + } + if (*cp) { + if (!isspace(*cp)) + goto inv_syntax; + *cp++ = '\0'; + } + /* must be end of non-comment line */ + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; + + process_record(hex_str, lineno); + return; + +inv_syntax: + fprintf(stderr, "%s line %d: invalid syntax\n", infname, lineno); + exit(1); +} + +static void process_file(const char *infname, const char *outfname) +{ + FILE *inf; + char linebuf[256]; + int lineno; + + inf = fopen(infname, "r"); + if (!inf) { + perror(infname); + exit(1); + } + if (outfname) { + out_file = fopen(outfname, "w"); + if (!out_file) { + perror(outfname); + exit(1); + } + } else { + out_file = stdout; + } + for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++) + process_line(linebuf, infname, lineno); + fclose(inf); + if (outfname) { + fclose(out_file); + out_file = NULL; + } +} + +int main(int argc, char **argv) +{ + int opt; + extern int optind; + + trau_8k_mode = false; + direction = OSMO_TRAU_DIR_UL; + rtp_format = OSMO_AMRT_RTP_FMT_OA; + allow_config_prot = false; + while ((opt = getopt(argc, argv, "8bcdx")) != EOF) { + switch (opt) { + case '8': + trau_8k_mode = true; + break; + case 'b': + rtp_format = OSMO_AMRT_RTP_FMT_BWE; + break; + case 'c': + allow_config_prot = true; + break; + case 'd': + direction = OSMO_TRAU_DIR_DL; + break; + case 'x': + rtp_format = OSMO_AMRT_RTP_FMT_OAX; + break; + default: + goto usage; + } + } + if (argc < optind + 1 || argc > optind + 2) + goto usage; + + /* decoder initial state */ + reset_dec_state(); + process_file(argv[optind], argv[optind + 1]); + exit(0); + +usage: fprintf(stderr, "usage: %s [options] input-file [output-file]\n", + argv[0]); + exit(1); +} diff --git a/tests/testsuite.at b/tests/testsuite.at index f064347..307555e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -91,6 +91,42 @@ AT_CHECK([$abs_top_builddir/tests/trau_conv/trau2rtp_gen -8 -x $abs_srcdir/trau_conv/trau2rtp_hr.in], [0], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([trau2rtp_amr16k_bwe]) +AT_KEYWORDS([trau2rtp_amr16k_bwe]) +cat $abs_srcdir/amr/trau-amr-16k-bwe.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr -b $abs_srcdir/amr/trau-amr-16k.in], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([trau2rtp_amr16k_oa]) +AT_KEYWORDS([trau2rtp_amr16k_oa]) +cat $abs_srcdir/amr/trau-amr-16k-oa.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr $abs_srcdir/amr/trau-amr-16k.in], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([trau2rtp_amr16k_oax]) +AT_KEYWORDS([trau2rtp_amr16k_oax]) +cat $abs_srcdir/amr/trau-amr-16k-oax.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr -x $abs_srcdir/amr/trau-amr-16k.in], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([trau2rtp_amr8k_bwe]) +AT_KEYWORDS([trau2rtp_amr8k_bwe]) +cat $abs_srcdir/amr/trau-amr-8k-bwe.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr -8 -b $abs_srcdir/amr/trau-amr-8k.in], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([trau2rtp_amr8k_oa]) +AT_KEYWORDS([trau2rtp_amr8k_oa]) +cat $abs_srcdir/amr/trau-amr-8k-oa.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr -8 $abs_srcdir/amr/trau-amr-8k.in], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([trau2rtp_amr8k_oax]) +AT_KEYWORDS([trau2rtp_amr8k_oax]) +cat $abs_srcdir/amr/trau-amr-8k-oax.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/trau2rtp_amr -8 -x $abs_srcdir/amr/trau-amr-8k.in], [0], [expout], [ignore]) +AT_CLEANUP + AT_SETUP([rtp2trau_fr_dl1]) AT_KEYWORDS([rtp2trau_fr_dl1]) cat $abs_srcdir/trau_conv/rtp2trau_fr_dl1.ok > expout @@ -181,6 +217,42 @@ AT_CHECK([$abs_top_builddir/tests/trau_conv/rtp2trau_gen $abs_srcdir/trau_conv/hr_speech_invsid_bits.hex hr ul], [0], [expout], [ignore]) AT_CLEANUP
+AT_SETUP([rtp2trau_amr16k_bwe]) +AT_KEYWORDS([rtp2trau_amr16k_bwe]) +cat $abs_srcdir/amr/rtp2trau-bwe2fr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr -b $abs_srcdir/amr/rtp-in-bwe.hex], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([rtp2trau_amr16k_oa]) +AT_KEYWORDS([rtp2trau_amr16k_oa]) +cat $abs_srcdir/amr/rtp2trau-oa2fr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr $abs_srcdir/amr/rtp-in-oa.hex], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([rtp2trau_amr16k_oax]) +AT_KEYWORDS([rtp2trau_amr16k_oax]) +cat $abs_srcdir/amr/rtp2trau-oax2fr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr -x $abs_srcdir/amr/rtp-in-oa.hex], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([rtp2trau_amr8k_bwe]) +AT_KEYWORDS([rtp2trau_amr8k_bwe]) +cat $abs_srcdir/amr/rtp2trau-bwe2hr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr -8 -b $abs_srcdir/amr/rtp-in-bwe.hex], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([rtp2trau_amr8k_oa]) +AT_KEYWORDS([rtp2trau_amr8k_oa]) +cat $abs_srcdir/amr/rtp2trau-oa2hr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr -8 $abs_srcdir/amr/rtp-in-oa.hex], [0], [expout], [ignore]) +AT_CLEANUP + +AT_SETUP([rtp2trau_amr8k_oax]) +AT_KEYWORDS([rtp2trau_amr8k_oax]) +cat $abs_srcdir/amr/rtp2trau-oax2hr.ok > expout +AT_CHECK([$abs_top_builddir/tests/amr/rtp2trau_amr -8 -x $abs_srcdir/amr/rtp-in-oa.hex], [0], [expout], [ignore]) +AT_CLEANUP + AT_SETUP([tfo_decode_fr]) AT_KEYWORDS([tfo_decode_fr]) cat $abs_srcdir/tfo/extract_test_fr.ok > expout