initial import of Osmocom TETRA phy and lower MAC code

This commit is contained in:
Harald Welte 2011-01-19 10:39:59 +01:00
parent a4c4e5a1ab
commit 7ee08faee0
45 changed files with 4217 additions and 0 deletions

106
src/lower_mac/crc_simple.c Normal file
View file

@ -0,0 +1,106 @@
/* CRC16 (most likely the ITU variant) working on plain bits as a trial
* to find out if simple padding to the right is going to be enough
*/
/* (C) 2011 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <lower_mac/crc_simple.h>
#include <stdio.h>
/**
* X.25 rec 2.2.7.4 Frame Check Sequence. This should be
* CRC ITU-T from the kernel or such.
*/
#define GEN_POLY 0x1021
uint16_t get_nth_bit(const uint8_t *input, int _bit)
{
uint16_t val;
int byte = _bit / 8;
int bit = 7 - _bit % 8;
val = (input[byte] & (1 << bit)) >> bit;
return val;
}
/**
* This is mostly from http://en.wikipedia.org/wiki/Computation_of_CRC
* Code fragment 2. Due some stupidity it took longer to implement than
* it should have taken.
*/
uint16_t crc16_itut_bytes(uint16_t crc, const uint8_t *input, int number_bits)
{
int i;
for (i = 0; i < number_bits; ++i) {
uint16_t bit = get_nth_bit(input, i);
crc ^= bit << 15;
if ((crc & 0x8000)) {
crc <<= 1;
crc ^= GEN_POLY;
} else {
crc <<= 1;
}
}
return crc;
}
uint16_t crc16_itut_bits(uint16_t crc, const uint8_t *input, int number_bits)
{
int i;
for (i = 0; i < number_bits; ++i) {
uint16_t bit = input[i] & 0x1;
crc ^= bit << 15;
if ((crc & 0x8000)) {
crc <<= 1;
crc ^= GEN_POLY;
} else {
crc <<= 1;
}
}
return crc;
}
uint16_t crc16_itut_poly(uint16_t crc, uint32_t poly, const uint8_t *input, int number_bits)
{
int i;
for (i = 0; i < number_bits; ++i) {
uint16_t bit = input[i] & 0x1;
crc ^= bit << 15;
if ((crc & 0x8000)) {
crc <<= 1;
crc ^= poly;
} else {
crc <<= 1;
}
}
return crc;
}
uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len)
{
return crc16_itut_bits(0xffff, bits, len);
}

View file

@ -0,0 +1,48 @@
/* (C) 2011 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CRC_16
#define CRC_16
#include <stdint.h>
/**
* Code to generate a CRC16-ITU-T as of the X.25 specification and
* compatible with Linux's implementations. At least the polynom is
* coming from the X.25 spec.
*/
/**
* This is working of bits packed together. The high bits will be
* accessed first. If you only pass one bit the array needs to contain
* a 0xF0 instead of a 0x01. This interface is compatible with the
* normal CRC interface (besides the length).
*/
uint16_t crc16_itut_bytes(uint16_t crc,
const uint8_t *input, const int number_bits);
/**
* Each byte contains one bit. Calculate the CRC16-ITU-T for it.
*/
uint16_t crc16_itut_bits(uint16_t crc,
const uint8_t *input, const int number_bits);
uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len);
#endif

View file

@ -0,0 +1,301 @@
/* Tetra convolutional encoder, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */
/* This converts from type type-2 bits into type-3 bits */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <osmocore/utils.h>
#include <tetra_common.h>
#include <lower_mac/tetra_conv_enc.h>
static char *dump_state(struct conv_enc_state *ces)
{
static char pbuf[1024];
snprintf(pbuf, sizeof(pbuf), "%u-%u-%u-%u", ces->delayed[0],
ces->delayed[1], ces->delayed[2], ces->delayed[3]);
return pbuf;
}
/* Mother code according to Section 8.2.3.1.1 */
static uint8_t conv_enc_in_bit(struct conv_enc_state *ces, uint8_t bit, uint8_t *out)
{
uint8_t g1, g2, g3, g4;
uint8_t *delayed = ces->delayed;
DEBUGP("State in: %s, Input bit: %u: ", dump_state(ces), bit);
/* G1 = 1 + D + D4 */
g1 = (bit + delayed[0] + delayed[3]) % 2;
/* G2 = 1 + D2 + D3 + D4 */
g2 = (bit + delayed[1] + delayed[2] + delayed[3]) % 2;
/* G3 = 1 + D + D2 + D4 */
g3 = (bit + delayed[0] + delayed[1] + delayed[3]) % 2;
/* G4 = 1 + D + D3 + D4 */
g4 = (bit + delayed[0] + delayed[2] + delayed[3]) % 2;
/* shift the state and input our new bit */
ces->delayed[3] = ces->delayed[2];
ces->delayed[2] = ces->delayed[1];
ces->delayed[1] = ces->delayed[0];
ces->delayed[0] = bit;
DEBUGP("Output bits: %u-%u-%u-%u, State out: %s\n", g1, g2, g3, g4,
dump_state(ces));
*out++ = g1;
*out++ = g2;
*out++ = g3;
*out++ = g4;
return (g1 | (g2 << 1) | (g3 << 2) | (g4 << 3));
}
/* in: bit-per-byte (len), out: bit-per-byte (4*len) */
int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out)
{
int i;
for (i = 0; i < len; i++) {
conv_enc_in_bit(ces, in[i], out);
out += 4;
}
return 0;
}
int conv_enc_init(struct conv_enc_state *ces)
{
memset(ces->delayed, 0, sizeof(ces->delayed));
return 0;
}
/* Puncturing */
const uint8_t P_rate2_3[] = { 0, 1, 2, 5 };
const uint8_t P_rate1_3[] = { 0, 1, 2, 3, 5, 6, 7 };
struct puncturer {
enum tetra_rcpc_puncturer type;
const uint8_t *P;
uint8_t t;
uint32_t (*i_func)(uint32_t j);
};
static uint32_t i_func_equals(uint32_t j)
{
return j;
}
static uint32_t i_func_292(uint32_t j)
{
return (j + ((j-1)/65));
}
static uint32_t i_func_148(uint32_t j)
{
return (j + ((j-1)/35));
}
/* Section 8.2.3.1.3 */
static const struct puncturer punct_2_3 = {
.type = TETRA_RCPC_PUNCT_2_3,
.P = P_rate2_3,
.t = 3,
.i_func = &i_func_equals,
};
/* Section 8.2.3.1.4 */
static const struct puncturer punct_1_3 = {
.type = TETRA_RCPC_PUNCT_1_3,
.P = P_rate1_3,
.t = 6,
.i_func = &i_func_equals,
};
/* Section 8.2.3.1.5 */
static const struct puncturer punct_292_432 = {
.type = TETRA_RCPC_PUNCT_292_432,
.P = P_rate2_3,
.t = 3,
.i_func = &i_func_292,
};
/* Section 8.2.3.1.6 */
static const struct puncturer punct_148_432 = {
.type = TETRA_RCPC_PUNCT_148_432,
.P = P_rate1_3,
.t = 6,
.i_func = &i_func_148,
};
static const struct puncturer *tetra_puncts[] = {
[TETRA_RCPC_PUNCT_2_3] = &punct_2_3,
[TETRA_RCPC_PUNCT_1_3] = &punct_1_3,
[TETRA_RCPC_PUNCT_292_432] = &punct_292_432,
[TETRA_RCPC_PUNCT_148_432] = &punct_148_432,
};
/* Puncture the mother code (in) and write 'len' symbols to out */
int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out)
{
const struct puncturer *punct;
uint32_t i, j, k;
uint8_t t;
const uint8_t *P;
if (pu >= ARRAY_SIZE(tetra_puncts))
return -EINVAL;
punct = tetra_puncts[pu];
t = punct->t;
P = punct->P;
/* Section 8.2.3.1.2 */
for (j = 1; j <= len; j++) {
i = punct->i_func(j);
k = 8 * ((i-1)/t) + P[i - t*((i-1)/t)];
DEBUGP("j = %u, i = %u, k = %u\n", j, i, k);
out[j-1] = in[k-1];
}
return 0;
}
/* De-Puncture the 'len' type-3 bits (in) and write mother code to out */
int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out)
{
const struct puncturer *punct;
uint32_t i, j, k;
uint8_t t;
const uint8_t *P;
if (pu >= ARRAY_SIZE(tetra_puncts))
return -EINVAL;
punct = tetra_puncts[pu];
t = punct->t;
P = punct->P;
/* Section 8.2.3.1.2 */
for (j = 1; j <= len; j++) {
i = punct->i_func(j);
k = 8 * ((i-1)/t) + P[i - t*((i-1)/t)];
DEBUGP("j = %u, i = %u, k = %u\n", j, i, k);
out[k-1] = in[j-1];
}
return 0;
}
struct punct_test_param {
uint16_t type2_len;
uint16_t type3_len;
enum tetra_rcpc_puncturer punct;
};
static const struct punct_test_param punct_test_params[] = {
{ 80, 120, TETRA_RCPC_PUNCT_2_3 }, /* BSCH */
{ 292, 432, TETRA_RCPC_PUNCT_292_432 }, /* TCH/4.8 */
{ 148, 432, TETRA_RCPC_PUNCT_148_432 }, /* TCH/2.4 */
{ 144, 216, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HD, BNCH, STCH */
{ 112, 168, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HU */
{ 288, 432, TETRA_RCPC_PUNCT_2_3 }, /* SCH/F */
};
static int mother_memcmp(const uint8_t *mother, const uint8_t *depunct, int len)
{
unsigned int i, equal = 0;
for (i = 0; i < len; i++) {
/* ignore any 0xff-initialized part */
if (depunct[i] == 0xff)
continue;
if (depunct[i] != mother[i])
return -1;
equal++;
}
return equal;
}
static int test_one_punct(const struct punct_test_param *ptp)
{
uint8_t *mother_buf;
uint8_t *depunct_buf;
uint8_t *type3_buf;
int i, mother_len;
printf("==> Testing Puncture/Depuncture mode %u (%u/%u)\n",
ptp->punct, ptp->type2_len, ptp->type3_len);
mother_len = ptp->type2_len*4;
mother_buf = malloc(mother_len);
depunct_buf = malloc(ptp->type2_len*4);
type3_buf = malloc(ptp->type3_len);
/* initialize mother buffer with sequence of bytes starting at 0 */
for (i = 0; i < mother_len; i++)
mother_buf[i] = i & 0xff;
/* puncture the mother_buf to type3_buf using rate 2/3 on 60 bits */
get_punctured_rate(ptp->punct, mother_buf, ptp->type3_len, type3_buf);
/* initialize the de-punctured buffer */
memset(depunct_buf, 0xff, mother_len);
/* de-puncture into the depunct_buf (i.e. what happens at the receiver) */
tetra_rcpc_depunct(ptp->punct, type3_buf, ptp->type3_len, depunct_buf);
DEBUGP("MOTH: %s\n", hexdump(mother_buf, mother_len));
DEBUGP("PUNC: %s\n", hexdump(type3_buf, ptp->type3_len));
DEBUGP("DEPU: %s\n", hexdump(depunct_buf, mother_len));
i = mother_memcmp(mother_buf, depunct_buf, mother_len);
if (i < 0) {
fprintf(stderr, "Mother buf != Depunct buf\n");
return i;
} else if (i != ptp->type3_len) {
fprintf(stderr, "Depunct buf only has %u equal symbols, we need %u\n",
i, ptp->type3_len);
return -EINVAL;
}
free(type3_buf);
free(depunct_buf);
free(mother_buf);
return 0;
}
int tetra_punct_test(void)
{
int i, rc;
for (i = 0; i < ARRAY_SIZE(punct_test_params); i++) {
rc = test_one_punct(&punct_test_params[i]);
if (rc < 0)
return rc;
}
return 0;
}

View file

@ -0,0 +1,33 @@
#ifndef TETRA_CONV_ENC_H
#define TETRA_CONV_ENC_H
#include <stdint.h>
struct conv_enc_state {
uint8_t delayed[4];
};
/* in: one-bit-per-byte out: 4bit-per-byte, both 'len' long */
int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out);
int conv_enc_init(struct conv_enc_state *ces);
enum tetra_rcpc_puncturer {
TETRA_RCPC_PUNCT_2_3,
TETRA_RCPC_PUNCT_1_3,
TETRA_RCPC_PUNCT_292_432,
TETRA_RCPC_PUNCT_148_432,
};
/* Puncture the mother code (in) and write 'len' symbols to out */
int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out);
/* De-Puncture the 'len' type-3 bits (in) and write mother code to out */
int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out);
/* Self-test the puncturing/de-puncturing */
int tetra_punct_test(void);
#endif /* TETRA_CONV_ENC_H */

View file

@ -0,0 +1,59 @@
/* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */
/* This converts from type type-3 bits into type-4 bits (and vice versa) */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <osmocore/utils.h>
#include <tetra_common.h>
#include <lower_mac/tetra_interleave.h>
/* Section 8.2.4.1 Block interleaving for phase modulation */
static uint32_t block_interl_func(uint32_t K, uint32_t a, uint32_t i)
{
return (1 + ( (a * i) % K));
}
void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out)
{
int i;
for (i = 1; i <= K; i++) {
uint32_t k = block_interl_func(K, a, i);
DEBUGP("interl: i=%u, k=%u\n", i, k);
out[k-1] = in[i-1];
}
}
void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out)
{
int i;
for (i = 1; i <= K; i++) {
uint32_t k = block_interl_func(K, a, i);
DEBUGP("deinterl: i=%u, k=%u\n", i, k);
out[i-1] = in[k-1];
}
}

View file

@ -0,0 +1,12 @@
#ifndef TETRA_INTERLEAVE_H
#define TETRA_INTERLEAVE_H
/* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */
/* This converts from type type-3 bits into type-4 bits (and vice versa) */
#include <stdint.h>
void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out);
void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out);
#endif /* TETRA_INTERLEAVE_H */

View file

@ -0,0 +1,257 @@
/* TETRA lower MAC layer main routine, between TP-SAP and TMV-SAP */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <osmocore/utils.h>
#include <tetra_common.h>
#include <tetra_tdma.h>
#include <phy/tetra_burst.h>
#include <phy/tetra_burst_sync.h>
#include <lower_mac/crc_simple.h>
#include <lower_mac/tetra_scramb.h>
#include <lower_mac/tetra_interleave.h>
#include <lower_mac/tetra_conv_enc.h>
#include <tetra_prim.h>
#include "tetra_upper_mac.h"
#include <lower_mac/viterbi.h>
struct tetra_blk_param {
const char *name;
uint16_t type345_bits;
uint16_t type2_bits;
uint16_t type1_bits;
uint16_t interleave_a;
uint8_t have_crc16;
};
/* try to aggregate all of the magic numbers somewhere central */
static const struct tetra_blk_param tetra_blk_param[] = {
[TPSAP_T_SB1] = {
.name = "SB1",
.type345_bits = 120,
.type2_bits = 80,
.type1_bits = 60,
.interleave_a = 11,
.have_crc16 = 1,
},
[TPSAP_T_SB2] = {
.name = "SB2",
.type345_bits = 216,
.type2_bits = 144,
.type1_bits = 124,
.interleave_a = 101,
.have_crc16 = 1,
},
[TPSAP_T_NDB] = {
.name = "NDB",
.type345_bits = 216,
.type2_bits = 144,
.type1_bits = 124,
.interleave_a = 101,
.have_crc16 = 1,
},
[TPSAP_T_SCH_HU] = {
.name = "SCH/HU",
.type345_bits = 168,
.type2_bits = 112,
.type1_bits = 92,
.interleave_a = 13,
.have_crc16 = 1,
},
[TPSAP_T_SCH_F] = {
.name = "SCH/F",
.type345_bits = 432,
.type2_bits = 288,
.type1_bits = 268,
.interleave_a = 103,
.have_crc16 = 1,
},
[TPSAP_T_BBK] = {
.name = "BBK",
.type345_bits = 30,
.type2_bits = 30,
.type1_bits = 14,
},
};
struct tetra_cell_data {
uint16_t mcc;
uint16_t mnc;
uint8_t colour_code;
struct tetra_tdma_time time;
uint32_t scramb_init;
};
static struct tetra_cell_data _tcd, *tcd = &_tcd;
int is_bsch(struct tetra_tdma_time *tm)
{
if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+1)%4))
return 1;
return 0;
}
int is_bnch(struct tetra_tdma_time *tm)
{
if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+3)%4))
return 1;
return 0;
}
struct tetra_tmvsap_prim *tmvsap_prim_alloc(uint16_t prim, uint8_t op)
{
struct tetra_tmvsap_prim *ttp;
//ttp = talloc_zero(NULL, struct tetra_tmvsap_prim);
ttp = calloc(1, sizeof(struct tetra_tmvsap_prim));
ttp->oph.sap = TETRA_SAP_TMV;
ttp->oph.primitive = prim;
ttp->oph.operation = op;
return ttp;
}
/* incoming TP-SAP UNITDATA.ind from PHY into lower MAC */
void tp_sap_udata_ind(enum tp_sap_data_type type, const uint8_t *bits, unsigned int len, void *priv)
{
/* various intermediary buffers */
uint8_t type4[512];
uint8_t type3dp[512*4];
uint8_t type3[512];
uint8_t type2[512];
const struct tetra_blk_param *tbp = &tetra_blk_param[type];
const char *time_str;
/* TMV-SAP.UNITDATA.ind primitive which we will send to the upper MAC */
struct tetra_tmvsap_prim *ttp;
struct tmv_unitdata_param *tup;
ttp = tmvsap_prim_alloc(PRIM_TMV_UNITDATA, PRIM_OP_INDICATION);
tup = &ttp->u.unitdata;
/* update the cell time */
memcpy(&tcd->time, &t_phy_state.time, sizeof(tcd->time));
time_str = tetra_tdma_time_dump(&tcd->time);
if (type == TPSAP_T_SB2 && is_bnch(&tcd->time)) {
tup->lchan = TETRA_LC_BNCH;
printf("BNCH FOLLOWS\n");
}
printf("%s %s type5: %s\n", tbp->name, tetra_tdma_time_dump(&tcd->time),
bitdump(bits, tbp->type345_bits));
/* De-scramble, pay special attention to SB1 pre-defined scrambling */
memcpy(type4, bits, tbp->type345_bits);
if (type == TPSAP_T_SB1) {
tetra_scramb_bits(SCRAMB_INIT, type4, tbp->type345_bits);
tup->scrambling_code = SCRAMB_INIT;
} else {
tetra_scramb_bits(tcd->scramb_init, type4, tbp->type345_bits);
tup->scrambling_code = tcd->scramb_init;
}
printf("%s %s type4: %s\n", tbp->name, time_str,
bitdump(type4, tbp->type345_bits));
if (tbp->interleave_a) {
/* Run block deinterleaving: type-3 bits */
block_deinterleave(tbp->type345_bits, tbp->interleave_a, type4, type3);
printf("%s %s type3: %s\n", tbp->name, time_str,
bitdump(type3, tbp->type345_bits));
/* De-puncture */
memset(type3dp, 0xff, sizeof(type3dp));
tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, tbp->type345_bits, type3dp);
printf("%s %s type3dp: %s\n", tbp->name, time_str,
bitdump(type3dp, tbp->type2_bits*4));
viterbi_dec_sb1_wrapper(type3dp, type2, tbp->type2_bits);
printf("%s %s type2: %s\n", tbp->name, time_str,
bitdump(type2, tbp->type2_bits));
}
if (tbp->have_crc16) {
uint16_t crc = crc16_ccitt_bits(type2, tbp->type1_bits+16);
printf("CRC COMP: 0x%04x ", crc);
if (crc == TETRA_CRC_OK) {
printf("OK\n");
tup->crc_ok = 1;
printf("%s %s type1: %s\n", tbp->name, time_str,
bitdump(type2, tbp->type1_bits));
} else
printf("WRONG\n");
}
memcpy(tup->mac_block, type2, tbp->type1_bits);
tup->mac_block_len = tbp->type1_bits;
switch (type) {
case TPSAP_T_SB1:
printf("TMB-SAP SYNC CC %s(0x%02x) ", bitdump(type2+4, 6), bits_to_uint(type2+4, 6));
printf("TN %s(%u) ", bitdump(type2+10, 2), bits_to_uint(type2+10, 2));
printf("FN %s(%2u) ", bitdump(type2+12, 5), bits_to_uint(type2+12, 5));
printf("MN %s(%2u) ", bitdump(type2+17, 6), bits_to_uint(type2+17, 6));
printf("MCC %s(%u) ", bitdump(type2+31, 10), bits_to_uint(type2+31, 10));
printf("MNC %s(%u)\n", bitdump(type2+41, 14), bits_to_uint(type2+41, 14));
/* obtain information from SYNC PDU */
tcd->colour_code = bits_to_uint(type2+4, 6);
tcd->time.tn = bits_to_uint(type2+10, 2);
tcd->time.fn = bits_to_uint(type2+12, 5);
tcd->time.mn = bits_to_uint(type2+17, 6);
tcd->mcc = bits_to_uint(type2+31, 10);
tcd->mnc = bits_to_uint(type2+41, 14);
/* compute the scrambling code for the current cell */
tcd->scramb_init = tetra_scramb_get_init(tcd->mcc, tcd->mnc, tcd->colour_code);
/* update the PHY layer time */
memcpy(&t_phy_state.time, &tcd->time, sizeof(t_phy_state.time));
tup->lchan = TETRA_LC_BSCH;
break;
case TPSAP_T_SB2:
case TPSAP_T_NDB:
/* FIXME: do something */
break;
case TPSAP_T_BBK:
/* FIXME: RM3014-decode */
tup->crc_ok = 1;
memcpy(tup->mac_block, type4, tbp->type1_bits);
printf("%s %s type1: %s\n", tbp->name, time_str, bitdump(tup->mac_block, tbp->type1_bits));
tup->lchan = TETRA_LC_AACH;
break;
case TPSAP_T_SCH_F:
tup->lchan = TETRA_LC_SCH_F;
break;
default:
/* FIXME: do something */
break;
}
/* send Rx time along with the TMV-UNITDATA.ind primitive */
memcpy(&tup->tdma_time, &tcd->time, sizeof(tup->tdma_time));
upper_mac_prim_recv(&ttp->oph, NULL);
}

View file

@ -0,0 +1,96 @@
/* Shortened (30,14) Reed-Muller (RM) code */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <stdio.h>
#include <lower_mac/tetra_rm3014.h>
/* Generator matrix from Section 8.2.3.2 */
static const uint8_t rm_30_14_gen[14][16] = {
{ 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 },
{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0 },
{ 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 },
{ 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 },
{ 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1 },
{ 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1 },
{ 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1 },
{ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 }
};
static uint32_t rm_30_14_rows[14];
static uint32_t shift_bits_together(const uint8_t *bits, int len)
{
uint32_t ret = 0;
int i;
for (i = len-1; i >= 0; i--)
ret |= bits[i] << (len-1-i);
return ret;
}
void tetra_rm3014_init(void)
{
int i;
uint32_t val;
for (i = 0; i < 14; i++) {
/* upper 14 bits identity matrix */
val = (1 << (16+13 - i));
/* lower 16 bits from rm_30_14_gen */
val |= shift_bits_together(rm_30_14_gen[i], 16);
rm_30_14_rows[i] = val;
printf("rm_30_14_rows[%u] = 0x%08x\n", i, val);
}
}
uint32_t tetra_rm3014_compute(const uint16_t in)
{
int i;
uint32_t val = 0;
for (i = 0; i < 14; i++) {
uint32_t bit = (in >> (14-1-i)) & 1;
if (bit)
val ^= rm_30_14_rows[i];
/* we can skip the 'else' as XOR with 0 has no effect */
}
return val;
}
/**
* This is a systematic code. We can remove the control bits
* and then check for an error. Maybe correct it in the future.
*/
int tetra_rm3014_decode(const uint32_t inp, uint16_t *out)
{
*out = inp >> 16;
return 0;
}

View file

@ -0,0 +1,16 @@
#ifndef TETRA_RM3014_H
#define TETRA_RM3014_H
#include <stdint.h>
void tetra_rm3014_init(void);
uint32_t tetra_rm3014_compute(const uint16_t in);
/**
* Decode @param inp to @param out and return if there was
* an error in the input. In the future this should correct
* the error or such.
*/
int tetra_rm3013_decode(const uint32_t inp, uint16_t *out);
#endif

View file

@ -0,0 +1,99 @@
/* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */
/* This converts from type-4 to type-5 bits (and vice versa) */
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <lower_mac/tetra_scramb.h>
/* Tap macro for the standard XOR / Fibonacci form */
#define ST(x, y) ((x) >> (32-y))
/* Tap macro and constant for the Galois form */
#define GL(x) (1<<(x-1))
#define GALOIS_LFSR (GL(32)|GL(26)|GL(23)|GL(22)|GL(16)|GL(12)|GL(11)|GL(10)|GL(8)|GL(7)|GL(5)|GL(4)|GL(2)|GL(1))
#if 1
static uint8_t next_lfsr_bit(uint32_t *lf)
{
uint32_t lfsr = *lf;
uint32_t bit;
/* taps: 32 26 23 22 16 12 11 10 8 7 5 4 2 1 */
bit = (ST(lfsr, 32) ^ ST(lfsr, 26) ^ ST(lfsr, 23) ^ ST(lfsr, 22) ^
ST(lfsr, 16) ^ ST(lfsr, 12) ^ ST(lfsr, 11) ^ ST(lfsr, 10) ^
ST(lfsr, 8) ^ ST(lfsr, 7) ^ ST(lfsr, 5) ^ ST(lfsr, 4) ^
ST(lfsr, 2) ^ ST(lfsr, 1)) & 1;
lfsr = (lfsr >> 1) | (bit << 31);
/* update the caller's LFSR state */
*lf = lfsr;
return bit & 0xff;
}
#else
/* WARNING: this version somehow does not produce the desired result! */
static uint8_t next_lfsr_bit(uint32_t *lf)
{
uint32_t lfsr = *lf;
uint32_t bit = lfsr & 1;
lfsr = (lfsr >> 1) ^ (uint32_t)(0 - ((lfsr & 1u) & GALOIS_LFSR));
*lf = lfsr;
return bit;
}
#endif
int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len)
{
int i;
for (i = 0; i < len; i++)
out[i] = next_lfsr_bit(&lfsr_init);
return 0;
}
/* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */
int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len)
{
int i;
for (i = 0; i < len; i++)
out[i] ^= next_lfsr_bit(&lfsr_init);
return 0;
}
uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour)
{
uint32_t scramb_init;
mcc &= 0x3ff;
mnc &= 0x3fff;
colour &= 0x3f;
scramb_init = colour | (mnc << 6) | (mcc << 20);
scramb_init = (scramb_init << 2) | SCRAMB_INIT;
return scramb_init;
}

View file

@ -0,0 +1,23 @@
#ifndef TETRA_SCRAMB_H
#define TETRA_SCRAMB_H
/* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */
/* This converts from type-4 to type-5 bits (and vice versa) */
#include <stdint.h>
/* Section 8.2.5.2:
* For scrambling of BSCH, all bits e(1) to e(30) shall equal to zero
* p(k) = e(1-k) for k = -29, ... 0
* p(k) = 1 for k = -31, -30
*/
#define SCRAMB_INIT 3
uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour);
int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len);
/* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */
int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len);
#endif /* TETRA_SCRAMB_H */

25
src/lower_mac/viterbi.c Normal file
View file

@ -0,0 +1,25 @@
#include <stdint.h>
#include <string.h>
#include <lower_mac/viterbi_cch.h>
void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count)
{
int8_t vit_inp[864*4];
int i;
for (i = 0; i < sym_count*4; i++) {
switch (in[i]) {
case 0:
vit_inp[i] = 127;
break;
case 0xff:
vit_inp[i] = 0;
break;
default:
vit_inp[i] = -127;
break;
}
}
conv_cch_decode(vit_inp, out, sym_count);
}

6
src/lower_mac/viterbi.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef VITERBI_H
#define VITERBI_H
void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count);
#endif /* VITERBI_H */

163
src/lower_mac/viterbi_cch.c Normal file
View file

@ -0,0 +1,163 @@
/* (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <lower_mac/viterbi.h>
#define CONV_CCH_N 4
#define CONV_CCH_K 5
#define CONV_CCH_N_STATES (1<<(CONV_CCH_K-1))
#define MAX_AE 0x00ffffff
static const uint8_t conv_cch_next_output[CONV_CCH_N_STATES][2] = {
{ 0, 15 }, { 11, 4 }, { 6, 9 }, { 13, 2 },
{ 5, 10 }, { 14, 1 }, { 3, 12 }, { 8, 7 },
{ 15, 0 }, { 4, 11 }, { 9, 6 }, { 2, 13 },
{ 10, 5 }, { 1, 14 }, { 12, 3 }, { 7, 8 },
};
static const uint8_t conv_cch_next_state[CONV_CCH_N_STATES][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
int conv_cch_encode(uint8_t *input, uint8_t *output, int n)
{
uint8_t state;
int i;
state = 0;
for (i=0; i<n; i++) {
int bit = input[i];
uint8_t out = conv_cch_next_output[state][bit];
state = conv_cch_next_state[state][bit];
output[(i<<2) ] = (out >> 3) & 1;
output[(i<<2)+1] = (out >> 2) & 1;
output[(i<<2)+2] = (out >> 1) & 1;
output[(i<<2)+3] = out & 1;
}
return 0;
}
int conv_cch_decode(int8_t *input, uint8_t *output, int n)
{
int i, s, b;
unsigned int ae[CONV_CCH_N_STATES];
unsigned int ae_next[CONV_CCH_N_STATES];
int8_t in_sym[CONV_CCH_N];
int8_t ev_sym[CONV_CCH_N];
int state_history[CONV_CCH_N_STATES][n];
int min_ae;
int min_state;
int cur_state;
/* Initial error (only state 0 is valid) */
ae[0] = 0;
for (i=1; i<CONV_CCH_N_STATES; i++) {
ae[i] = MAX_AE;
}
/* Scan the treillis */
for (i=0; i<n; i++) {
/* Reset next accumulated error */
for (s=0; s<CONV_CCH_N_STATES; s++) {
ae_next[s] = MAX_AE;
}
/* Get input */
in_sym[0] = input[(i<<2) ];
in_sym[1] = input[(i<<2)+1];
in_sym[2] = input[(i<<2)+2];
in_sym[3] = input[(i<<2)+3];
/* Scan all states */
for (s=0; s<CONV_CCH_N_STATES; s++)
{
/* Scan possible input bits */
for (b=0; b<2; b++)
{
int nae;
/* Next output and state */
uint8_t out = conv_cch_next_output[s][b];
uint8_t state = conv_cch_next_state[s][b];
/* Expand */
ev_sym[0] = (out >> 3) & 1 ? -127 : 127;
ev_sym[1] = (out >> 2) & 1 ? -127 : 127;
ev_sym[2] = (out >> 1) & 1 ? -127 : 127;
ev_sym[3] = out & 1 ? -127 : 127;
/* New error for this path */
#define DIFF(x,y) (((x-y)*(x-y)) >> 9)
nae = ae[s] + \
DIFF(ev_sym[0], in_sym[0]) + \
DIFF(ev_sym[1], in_sym[1]) + \
DIFF(ev_sym[2], in_sym[2]) + \
DIFF(ev_sym[3], in_sym[3]);
/* Is it survivor */
if (ae_next[state] > nae) {
ae_next[state] = nae;
state_history[state][i+1] = s;
}
}
}
/* Copy accumulated error */
memcpy(ae, ae_next, sizeof(int) * CONV_CCH_N_STATES);
}
/* Find state with least error */
min_ae = MAX_AE;
min_state = -1;
for (s=0; s<CONV_CCH_N_STATES; s++)
{
if (ae[s] < min_ae) {
min_ae = ae[s];
min_state = s;
}
}
/* Traceback */
cur_state = min_state;
for (i=n-1; i >= 0; i--)
{
min_state = cur_state;
cur_state = state_history[cur_state][i+1];
if (conv_cch_next_state[cur_state][0] == min_state)
output[i] = 0;
else
output[i] = 1;
}
return 0;
}

View file

@ -0,0 +1,7 @@
#ifndef VITERBI_CCH_H
#define VITERBI_CCH_H
int conv_cch_encode(uint8_t *input, uint8_t *output, int n);
int conv_cch_decode(int8_t *input, uint8_t *output, int n);
#endif /* VITERBI_CCH_H */

159
src/lower_mac/viterbi_tch.c Normal file
View file

@ -0,0 +1,159 @@
/* (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <string.h>
#include <lower_mac/viterbi_tch.h>
#define CONV_TCH_N 3
#define CONV_TCH_K 5
#define CONV_TCH_N_STATES (1<<(CONV_TCH_K-1))
#define MAX_AE 0x00ffffff
static const uint8_t conv_tch_next_output[CONV_TCH_N_STATES][2] = {
{ 0, 7 }, { 6, 1 }, { 5, 2 }, { 3, 4 },
{ 6, 1 }, { 0, 7 }, { 3, 4 }, { 5, 2 },
{ 7, 0 }, { 1, 6 }, { 2, 5 }, { 4, 3 },
{ 1, 6 }, { 7, 0 }, { 4, 3 }, { 2, 5 },
};
static const uint8_t conv_tch_next_state[CONV_TCH_N_STATES][2] = {
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
};
int conv_tch_encode(uint8_t *input, uint8_t *output, int n)
{
uint8_t state;
int i;
state = 0;
for (i=0; i<n; i++) {
int bit = input[i];
uint8_t out = conv_tch_next_output[state][bit];
state = conv_tch_next_state[state][bit];
output[(i<<2) ] = (out >> 2) & 1;
output[(i<<2)+1] = (out >> 1) & 1;
output[(i<<2)+2] = out & 1;
}
return 0;
}
int conv_tch_decode(int8_t *input, uint8_t *output, int n)
{
int i, s, b;
unsigned int ae[CONV_TCH_N_STATES];
unsigned int ae_next[CONV_TCH_N_STATES];
int8_t in_sym[CONV_TCH_N];
int8_t ev_sym[CONV_TCH_N];
int state_history[CONV_TCH_N_STATES][n];
int min_ae;
int min_state;
int cur_state;
/* Initial error (only state 0 is valid) */
ae[0] = 0;
for (i=1; i<CONV_TCH_N_STATES; i++) {
ae[i] = MAX_AE;
}
/* Scan the treillis */
for (i=0; i<n; i++) {
/* Reset next accumulated error */
for (s=0; s<CONV_TCH_N_STATES; s++) {
ae_next[s] = MAX_AE;
}
/* Get input */
in_sym[0] = input[(i<<2) ];
in_sym[1] = input[(i<<2)+1];
in_sym[2] = input[(i<<2)+2];
/* Scan all states */
for (s=0; s<CONV_TCH_N_STATES; s++)
{
/* Scan possible input bits */
for (b=0; b<2; b++)
{
int nae;
/* Next output and state */
uint8_t out = conv_tch_next_output[s][b];
uint8_t state = conv_tch_next_state[s][b];
/* Expand */
ev_sym[0] = (out >> 2) & 1 ? -127 : 127;
ev_sym[1] = (out >> 1) & 1 ? -127 : 127;
ev_sym[2] = out & 1 ? -127 : 127;
/* New error for this path */
#define DIFF(x,y) (((x-y)*(x-y)) >> 9)
nae = ae[s] + \
DIFF(ev_sym[0], in_sym[0]) + \
DIFF(ev_sym[1], in_sym[1]) + \
DIFF(ev_sym[2], in_sym[2]);
/* Is it survivor */
if (ae_next[state] > nae) {
ae_next[state] = nae;
state_history[state][i+1] = s;
}
}
}
/* Copy accumulated error */
memcpy(ae, ae_next, sizeof(int) * CONV_TCH_N_STATES);
}
/* Find state with least error */
min_ae = MAX_AE;
min_state = -1;
for (s=0; s<CONV_TCH_N_STATES; s++)
{
if (ae[s] < min_ae) {
min_ae = ae[s];
min_state = s;
}
}
/* Traceback */
cur_state = min_state;
for (i=n-1; i >= 0; i--)
{
min_state = cur_state;
cur_state = state_history[cur_state][i+1];
if (conv_tch_next_state[cur_state][0] == min_state)
output[i] = 0;
else
output[i] = 1;
}
return 0;
}

View file

@ -0,0 +1,7 @@
#ifndef VITERBI_TCH_H
#define VITERBI_TCH_H
int conv_tch_encode(uint8_t *input, uint8_t *output, int n);
int conv_tch_decode(int8_t *input, uint8_t *output, int n);
#endif /* VITERBI_TCH_H */