aboutsummaryrefslogtreecommitdiffstats
path: root/src/coding/gsm0503_coding.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/coding/gsm0503_coding.c')
-rw-r--r--src/coding/gsm0503_coding.c207
1 files changed, 130 insertions, 77 deletions
diff --git a/src/coding/gsm0503_coding.c b/src/coding/gsm0503_coding.c
index 1bec56ea..f2b01803 100644
--- a/src/coding/gsm0503_coding.c
+++ b/src/coding/gsm0503_coding.c
@@ -17,10 +17,6 @@
* 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -35,9 +31,7 @@
#include <osmocom/core/crcgen.h>
#include <osmocom/core/endian.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
-#include <osmocom/gprs/gprs_rlc.h>
-
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0503.h>
#include <osmocom/codec/codec.h>
@@ -547,10 +541,11 @@ static int osmo_conv_decode_ber_punctured(const struct osmo_conv_code *code,
res = osmo_conv_decode(code, input, output);
- if (n_bits_total || n_errors) {
- coded_len = osmo_conv_encode(code, output, recoded);
- OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len);
- }
+ if (!n_bits_total && !n_errors)
+ return res;
+
+ coded_len = osmo_conv_encode(code, output, recoded);
+ OSMO_ASSERT(ARRAY_SIZE(recoded) >= coded_len);
/* Count bit errors */
if (n_errors) {
@@ -2118,6 +2113,26 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
return 0;
}
+/* TCH/AFS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_afs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 8) soft-bits here? */
+ for (j = 0, k = 0; j < 8; j++)
+ k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
* \param[in] bursts buffer containing the symbols of 8 bursts
@@ -2160,12 +2175,10 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[250];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
- ubit_t cBd[456];
+ int i, rv, len, steal = 0, id = -1;
*n_errors = 0; *n_bits_total = 0;
static ubit_t sid_first_dummy[64] = { 0 };
sbit_t sid_update_enc[256];
- uint8_t dtx_prev;
for (i=0; i<8; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
@@ -2175,6 +2188,12 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
gsm0503_tch_fr_deinterleave(cB, iB);
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AFS frame.
+ * It's normally updated by gsm0503_detect_afs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/F frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
if (rv) {
/* Error decoding FACCH frame */
@@ -2186,16 +2205,20 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
/* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
if (dtx) {
- osmo_sbit2ubit(cBd, cB, 456);
- dtx_prev = *dtx;
- *dtx = gsm0503_detect_afs_dtx_frame(n_errors, n_bits_total, cBd);
+ const enum gsm0503_amr_dtx_frames dtx_prev = *dtx;
- if (dtx_prev == AFS_SID_UPDATE && *dtx == AMR_OTHER) {
+ *dtx = gsm0503_detect_afs_dtx_frame2(n_errors, n_bits_total, &id, cB);
+
+ switch (*dtx) {
+ case AMR_OTHER:
/* NOTE: The AFS_SID_UPDATE frame is splitted into
* two half rate frames. If the id marker frame
* (AFS_SID_UPDATE) is detected the following frame
* contains the actual comfort noised data part of
* (AFS_SID_UPDATE_CN). */
+ if (dtx_prev != AFS_SID_UPDATE)
+ break;
+ /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
*dtx = AFS_SID_UPDATE_CN;
extract_afs_sid_update(sid_update_enc, cB);
@@ -2211,35 +2234,28 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
tch_amr_sid_update_append(conv, 1,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, conv, 39);
len = 5;
goto out;
- } else if (*dtx == AFS_SID_FIRST) {
+ case AFS_SID_FIRST: /* TODO: parse CMI or CMC/CMR (16 bit) */
tch_amr_sid_update_append(sid_first_dummy, 0,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, conv, 39);
len = 5;
goto out;
- } else if (*dtx == AFS_ONSET) {
+ case AFS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ case AFS_ONSET:
len = 0;
goto out;
+ default:
+ break;
}
}
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 8; j++)
- k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
-
- if (i == 0 || k < best) {
- best = k;
- id = i;
- }
- }
-
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_afs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2390,10 +2406,12 @@ int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -2568,6 +2586,26 @@ invalid_length:
return -1;
}
+/* TCH/AHS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_ahs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0, k = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 4) soft-bits here? */
+ for (j = 0, k = 0; j < 4; j++)
+ k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
* \param[in] bursts buffer containing the symbols of 8 bursts
@@ -2612,10 +2650,8 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[135];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
- ubit_t cBd[456];
+ int i, rv, len, steal = 0, id = -1;
static ubit_t sid_first_dummy[64] = { 0 };
- uint8_t dtx_prev;
/* only unmap the stealing bits */
if (!odd) {
@@ -2631,6 +2667,13 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
/* if we found a stole FACCH, but only at correct alignment */
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AHS frame.
+ * It's normally updated by gsm0503_detect_ahs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/H frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
+
for (i = 0; i < 6; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114],
&bursts[i * 116], NULL, i >> 2);
@@ -2661,21 +2704,37 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
/* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
if (dtx) {
- osmo_sbit2ubit(cBd, cB, 456);
- dtx_prev = *dtx;
- *dtx = gsm0503_detect_ahs_dtx_frame(n_errors, n_bits_total, cBd);
-
- if (dtx_prev == AHS_SID_UPDATE && *dtx == AMR_OTHER) {
- /* NOTE: The AHS_SID_UPDATE frame is splitted into
- * two half rate frames. If the id marker frame
- * (AHS_SID_UPDATE) is detected the following frame
- * contains the actual comfort noised data part of
- * (AHS_SID_UPDATE_CN). */
+ int n_bits_total_sid;
+ int n_errors_sid;
+
+ *dtx = gsm0503_detect_ahs_dtx_frame2(n_errors, n_bits_total, &id, cB);
+ /* TODO: detect and handle AHS_SID_UPDATE + AHS_SID_UPDATE_INH */
+
+ switch (*dtx) {
+ case AHS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ /* cB[] contains 16 bits of coded in-band data and 212 bits containing
+ * the identification marker. We need to unmap/deinterleave 114 odd
+ * bits from the last two blocks, 114 even bits from the first two
+ * blocks and combine them together. */
+ gsm0503_tch_burst_unmap(&iB[0 * 114], &bursts[2 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[1 * 114], &bursts[3 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[2 * 114], &bursts[0 * 116], NULL, 1);
+ gsm0503_tch_burst_unmap(&iB[3 * 114], &bursts[1 * 116], NULL, 1);
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ /* cB[] is expected to contain 16 bits of coded in-band data and
+ * 212 bits containing the coded data (53 bits coded at 1/4 rate). */
*dtx = AHS_SID_UPDATE_CN;
osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update,
- cB + 16, conv, n_errors,
- n_bits_total);
+ cB + 16, conv, &n_errors_sid,
+ &n_bits_total_sid);
+ /* gsm0503_detect_ahs_dtx_frame2() calculates BER for the marker,
+ * osmo_conv_decode_ber() calculates BER for the coded data. */
+ if (n_errors != NULL)
+ *n_errors += n_errors_sid;
+ if (n_bits_total != NULL)
+ *n_bits_total += n_bits_total_sid;
rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv,
35, conv + 35);
if (rv != 0) {
@@ -2685,38 +2744,30 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
tch_amr_sid_update_append(conv, 1,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, conv, 39);
len = 5;
goto out;
- } else if (*dtx == AHS_SID_FIRST_P2) {
+ case AHS_SID_FIRST_P2:
tch_amr_sid_update_append(sid_first_dummy, 0,
(codec_mode_req) ? codec[*ft]
- : codec[id]);
+ : codec[id > 0 ? id : 0]);
tch_amr_reassemble(tch_data, sid_first_dummy, 39);
len = 5;
goto out;
- } else if (*dtx == AHS_SID_UPDATE || *dtx == AHS_ONSET
- || *dtx == AHS_SID_FIRST_INH
- || *dtx == AHS_SID_UPDATE_INH
- || *dtx == AHS_SID_FIRST_P1) {
+ case AHS_ONSET:
+ case AHS_SID_FIRST_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_UPDATE_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_FIRST_P1: /* TODO: parse CMI or CMC/CMR (16 bit) */
len = 0;
goto out;
+ default:
+ break;
}
}
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 4; j++)
- k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
-
- if (i == 0 || k < best) {
- best = k;
- id = i;
- }
- }
-
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_ahs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2851,10 +2902,12 @@ int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -3007,7 +3060,7 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
return -1;
}
- memcpy(cB, gsm0503_afs_ic_ubit[id], 4);
+ memcpy(cB, gsm0503_ahs_ic_ubit[id], 4);
gsm0503_tch_hr_interleave(cB, iB);