mirror of
https://github.com/brmlab/osmo-tetra.git
synced 2025-07-01 13:13:41 +02:00
initial import of Osmocom TETRA phy and lower MAC code
This commit is contained in:
parent
a4c4e5a1ab
commit
7ee08faee0
45 changed files with 4217 additions and 0 deletions
106
src/lower_mac/crc_simple.c
Normal file
106
src/lower_mac/crc_simple.c
Normal 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);
|
||||
}
|
48
src/lower_mac/crc_simple.h
Normal file
48
src/lower_mac/crc_simple.h
Normal 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
|
301
src/lower_mac/tetra_conv_enc.c
Normal file
301
src/lower_mac/tetra_conv_enc.c
Normal 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;
|
||||
}
|
33
src/lower_mac/tetra_conv_enc.h
Normal file
33
src/lower_mac/tetra_conv_enc.h
Normal 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 */
|
59
src/lower_mac/tetra_interleave.c
Normal file
59
src/lower_mac/tetra_interleave.c
Normal 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];
|
||||
}
|
||||
}
|
12
src/lower_mac/tetra_interleave.h
Normal file
12
src/lower_mac/tetra_interleave.h
Normal 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 */
|
257
src/lower_mac/tetra_lower_mac.c
Normal file
257
src/lower_mac/tetra_lower_mac.c
Normal 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);
|
||||
}
|
||||
|
||||
|
96
src/lower_mac/tetra_rm3014.c
Normal file
96
src/lower_mac/tetra_rm3014.c
Normal 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;
|
||||
}
|
16
src/lower_mac/tetra_rm3014.h
Normal file
16
src/lower_mac/tetra_rm3014.h
Normal 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
|
99
src/lower_mac/tetra_scramb.c
Normal file
99
src/lower_mac/tetra_scramb.c
Normal 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;
|
||||
}
|
23
src/lower_mac/tetra_scramb.h
Normal file
23
src/lower_mac/tetra_scramb.h
Normal 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
25
src/lower_mac/viterbi.c
Normal 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
6
src/lower_mac/viterbi.h
Normal 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
163
src/lower_mac/viterbi_cch.c
Normal 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;
|
||||
}
|
7
src/lower_mac/viterbi_cch.h
Normal file
7
src/lower_mac/viterbi_cch.h
Normal 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
159
src/lower_mac/viterbi_tch.c
Normal 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;
|
||||
}
|
7
src/lower_mac/viterbi_tch.h
Normal file
7
src/lower_mac/viterbi_tch.h
Normal 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 */
|
Loading…
Add table
Add a link
Reference in a new issue