mirror of https://gerrit.osmocom.org/osmo-tetra
parent
a4c4e5a1ab
commit
7ee08faee0
@ -0,0 +1,8 @@ |
||||
*.o |
||||
*.a |
||||
*.pyc |
||||
.*.swp |
||||
conv_enc_test |
||||
burst_test |
||||
float_to_bits |
||||
crc_test |
@ -0,0 +1,25 @@ |
||||
CFLAGS=-g -O0 -Wall `pkg-config --cflags libosmocore 2> /dev/null` -I.
|
||||
LDFLAGS=`pkg-config --libs libosmocore 2> /dev/null` -losmocore
|
||||
|
||||
all: conv_enc_test crc_test burst_test float_to_bits |
||||
|
||||
%.o: %.c |
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
libosmo-tetra-phy.a: phy/tetra_burst_sync.o phy/tetra_burst.o |
||||
$(AR) r $@ $^
|
||||
|
||||
libosmo-tetra-mac.a: lower_mac/tetra_conv_enc.o tetra_tdma.o lower_mac/tetra_scramb.o lower_mac/tetra_rm3014.o lower_mac/tetra_interleave.o lower_mac/crc_simple.o tetra_common.o lower_mac/viterbi.o lower_mac/viterbi_cch.o lower_mac/viterbi_tch.o lower_mac/tetra_lower_mac.o tetra_upper_mac.o tetra_mac_pdu.o tetra_mle_pdu.o tetra_mm_pdu.o tetra_gsmtap.o |
||||
$(AR) r $@ $^
|
||||
|
||||
float_to_bits: float_to_bits.o |
||||
|
||||
crc_test: crc_test.o tetra_common.o libosmo-tetra-mac.a |
||||
|
||||
burst_test: burst_test.o libosmo-tetra-phy.a libosmo-tetra-mac.a |
||||
|
||||
conv_enc_test: conv_enc_test.o testpdu.o libosmo-tetra-phy.a libosmo-tetra-mac.a |
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||
|
||||
clean: |
||||
@rm -f conv_enc_test crc_test burst_test float_to_bits *.o phy/*.o lower_mac/*.o *.a
|
@ -0,0 +1,74 @@ |
||||
/* Test program for tetra burst synchronizer */ |
||||
|
||||
/* (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 <fcntl.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#include <osmocore/utils.h> |
||||
|
||||
#include "tetra_common.h" |
||||
#include <phy/tetra_burst.h> |
||||
#include <phy/tetra_burst_sync.h> |
||||
#include "tetra_gsmtap.h" |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
int fd; |
||||
struct tetra_rx_state *trs; |
||||
|
||||
if (argc < 2) { |
||||
fprintf(stderr, "Usage: %s <file_with_1_byte_per_bit>\n", argv[0]); |
||||
exit(1); |
||||
} |
||||
|
||||
fd = open(argv[1], O_RDONLY); |
||||
if (fd < 0) { |
||||
perror("open"); |
||||
exit(2); |
||||
} |
||||
|
||||
tetra_gsmtap_init("localhost", 0); |
||||
|
||||
trs = calloc(1, sizeof(*trs)); |
||||
|
||||
while (1) { |
||||
uint8_t buf[64]; |
||||
int len; |
||||
|
||||
len = read(fd, buf, sizeof(buf)); |
||||
if (len < 0) { |
||||
perror("read"); |
||||
exit(1); |
||||
} else if (len == 0) { |
||||
printf("EOF"); |
||||
break; |
||||
} |
||||
tetra_burst_sync_in(trs, buf, len); |
||||
} |
||||
|
||||
free(trs); |
||||
|
||||
exit(0); |
||||
} |
@ -0,0 +1,351 @@ |
||||
/* (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 <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <time.h> |
||||
|
||||
#include <arpa/inet.h> |
||||
|
||||
#include <osmocore/utils.h> |
||||
#include <osmocore/bits.h> |
||||
|
||||
|
||||
#include "tetra_common.h" |
||||
#include <lower_mac/crc_simple.h> |
||||
#include <lower_mac/tetra_conv_enc.h> |
||||
#include <lower_mac/tetra_interleave.h> |
||||
#include <lower_mac/tetra_scramb.h> |
||||
#include <lower_mac/tetra_rm3014.h> |
||||
#include <lower_mac/viterbi.h> |
||||
#include <phy/tetra_burst.h> |
||||
#include "testpdu.h" |
||||
|
||||
|
||||
#define swap16(x) ((x)<<8)|((x)>>8) |
||||
|
||||
static unsigned int num_crc_err; |
||||
|
||||
/* 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) |
||||
{ |
||||
} |
||||
|
||||
static void decode_schf(const uint8_t *bits) |
||||
{ |
||||
uint8_t type4[1024]; |
||||
uint8_t type3dp[1024*4]; |
||||
uint8_t type3[1024]; |
||||
uint8_t type2[1024]; |
||||
|
||||
printf("SCH/f type5: %s\n", bitdump(bits, 432)); |
||||
memcpy(type4, bits, 432); |
||||
tetra_scramb_bits(SCRAMB_INIT, type4, 432); |
||||
printf("SCH/F type4: %s\n", bitdump(type4, 432)); |
||||
/* Run (120,11) block deinterleaving: type-3 bits */ |
||||
block_deinterleave(432, 103, type4, type3); |
||||
printf("SCH/F type3: %s\n", bitdump(type3, 432)); |
||||
/* De-puncture */ |
||||
memset(type3dp, 0xff, sizeof(type3dp)); |
||||
tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 432, type3dp); |
||||
printf("SCH/F type3dp: %s\n", bitdump(type3dp, 288*4)); |
||||
viterbi_dec_sb1_wrapper(type3dp, type2, 288); |
||||
printf("SCH/F type2: %s\n", bitdump(type2, 288)); |
||||
|
||||
{ |
||||
uint16_t crc; |
||||
crc = crc16_ccitt_bits(type2, 288-4); |
||||
printf("CRC COMP: 0x%04x ", crc); |
||||
if (crc == 0x1d0f) |
||||
printf("OK\n"); |
||||
else { |
||||
printf("WRONG\n"); |
||||
num_crc_err++; |
||||
} |
||||
} |
||||
printf("SCH/F type1: %s\n", bitdump(type2, 268)); |
||||
} |
||||
|
||||
/* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ |
||||
int build_ndb_schf() |
||||
{ |
||||
/* input: 268 type-1 bits */ |
||||
uint8_t type2[284]; |
||||
uint8_t master[284*4]; |
||||
uint8_t type3[432]; |
||||
uint8_t type4[432]; |
||||
uint8_t type5[432]; |
||||
uint8_t bb_type5[30]; |
||||
uint8_t burst[255*2]; |
||||
uint16_t crc; |
||||
uint8_t *cur; |
||||
uint32_t bb_rm3014, bb_rm3014_be; |
||||
|
||||
memset(type2, 0, sizeof(type2)); |
||||
cur = type2; |
||||
|
||||
/* Use pdu_sync from testpdu.c */ |
||||
cur += osmo_pbit2ubit(type2, pdu_schf, 268); |
||||
|
||||
crc = ~crc16_ccitt_bits(type2, 268); |
||||
crc = swap16(crc); |
||||
cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); |
||||
|
||||
/* Append 4 tail bits: type-2 bits */ |
||||
cur += 4; |
||||
|
||||
printf("SCH/F type2: %s\n", bitdump(type2, 288)); |
||||
|
||||
/* Run rate 2/3 RCPC code: type-3 bits*/ |
||||
{ |
||||
struct conv_enc_state *ces = calloc(1, sizeof(*ces)); |
||||
conv_enc_init(ces); |
||||
conv_enc_input(ces, type2, 288, master); |
||||
get_punctured_rate(TETRA_RCPC_PUNCT_2_3, master, 432, type3); |
||||
free(ces); |
||||
} |
||||
printf("SCH/F type3: %s\n", bitdump(type3, 432)); |
||||
|
||||
/* Run (432,103) block interleaving: type-4 bits */ |
||||
block_interleave(432, 103, type3, type4); |
||||
printf("SCH/F type4: %s\n", bitdump(type4, 432)); |
||||
|
||||
/* Run scrambling (all-zero): type-5 bits */ |
||||
memcpy(type5, type4, 432); |
||||
tetra_scramb_bits(SCRAMB_INIT, type5, 432); |
||||
printf("SCH/F type5: %s\n", bitdump(type5, 432)); |
||||
|
||||
decode_schf(type5); |
||||
|
||||
/* Use pdu_acc_ass from testpdu.c */ |
||||
/* Run it through (30,14) RM code: type-2=3=4 bits */ |
||||
printf("AACH type-1: %s\n", bitdump(pdu_acc_ass, 2)); |
||||
bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); |
||||
printf("AACH RM3014: 0x0%x\n", bb_rm3014); |
||||
/* convert to big endian */ |
||||
bb_rm3014_be = htonl(bb_rm3014); |
||||
/* shift two bits left as it is only a 30 bit value */ |
||||
bb_rm3014_be <<= 2; |
||||
osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); |
||||
/* Run scrambling (all-zero): type-5 bits */ |
||||
printf("AACH type-5: %s\n", bitdump(bb_type5, 30)); |
||||
|
||||
/* Finally, hand it into the physical layer */ |
||||
build_norm_c_d_burst(burst, type5, bb_type5, type5+216, 0); |
||||
printf("cont norm DL burst: %s\n", bitdump(burst, 255*2)); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
static void decode_sb1(const uint8_t *bits) |
||||
{ |
||||
uint8_t type4[1024]; |
||||
uint8_t type3dp[1024*4]; |
||||
uint8_t type3[1024]; |
||||
uint8_t type2[1024]; |
||||
|
||||
printf("SB1 type5: %s\n", bitdump(bits, 120)); |
||||
memcpy(type4, bits, 120); |
||||
tetra_scramb_bits(SCRAMB_INIT, type4, 120); |
||||
printf("SB1 type4: %s\n", bitdump(type4, 120)); |
||||
/* Run (120,11) block deinterleaving: type-3 bits */ |
||||
block_deinterleave(120, 11, type4, type3); |
||||
printf("SB1 type3: %s\n", bitdump(type3, 120)); |
||||
/* De-puncture */ |
||||
memset(type3dp, 0xff, sizeof(type3dp)); |
||||
tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 120, type3dp); |
||||
printf("SB1 type3dp: %s\n", bitdump(type3dp, 80*4)); |
||||
viterbi_dec_sb1_wrapper(type3dp, type2, 80); |
||||
printf("SB1 type2: %s\n", bitdump(type2, 80)); |
||||
|
||||
{ |
||||
uint16_t crc; |
||||
crc = crc16_ccitt_bits(type2, 76); |
||||
printf("CRC COMP: 0x%04x ", crc); |
||||
if (crc == 0x1d0f) |
||||
printf("OK\n"); |
||||
else { |
||||
printf("WRONG\n"); |
||||
num_crc_err++; |
||||
} |
||||
} |
||||
|
||||
printf("TN %s ", bitdump(type2+10, 2)); |
||||
printf("MCC %s ", bitdump(type2+31, 10)); |
||||
printf("MNC %s\n", bitdump(type2+41, 14)); |
||||
} |
||||
|
||||
/* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ |
||||
int build_sb() |
||||
{ |
||||
uint8_t sb_type2[80]; |
||||
uint8_t sb_master[80*4]; |
||||
uint8_t sb_type3[120]; |
||||
uint8_t sb_type4[120]; |
||||
uint8_t sb_type5[120]; |
||||
|
||||
uint8_t si_type2[140]; |
||||
uint8_t si_master[140*4]; |
||||
uint8_t si_type3[216]; |
||||
uint8_t si_type4[216]; |
||||
uint8_t si_type5[216]; |
||||
|
||||
uint8_t bb_type5[30]; |
||||
uint8_t burst[255*2]; |
||||
uint16_t crc; |
||||
uint8_t *cur; |
||||
uint32_t bb_rm3014, bb_rm3014_be; |
||||
|
||||
memset(sb_type2, 0, sizeof(sb_type2)); |
||||
cur = sb_type2; |
||||
|
||||
/* Use pdu_sync from testpdu.c */ |
||||
cur += osmo_pbit2ubit(sb_type2, pdu_sync, 60); |
||||
|
||||
crc = ~crc16_ccitt_bits(sb_type2, 60); |
||||
crc = swap16(crc); |
||||
cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); |
||||
|
||||
/* Append 4 tail bits: type-2 bits */ |
||||
cur += 4; |
||||
|
||||
printf("SYNC type2: %s\n", bitdump(sb_type2, 80)); |
||||
|
||||
/* Run rate 2/3 RCPC code: type-3 bits*/ |
||||
{ |
||||
struct conv_enc_state *ces = calloc(1, sizeof(*ces)); |
||||
conv_enc_init(ces); |
||||
conv_enc_input(ces, sb_type2, 80, sb_master); |
||||
get_punctured_rate(TETRA_RCPC_PUNCT_2_3, sb_master, 120, sb_type3); |
||||
free(ces); |
||||
} |
||||
printf("SYNC type3: %s\n", bitdump(sb_type3, 120)); |
||||
|
||||
/* Run (120,11) block interleaving: type-4 bits */ |
||||
block_interleave(120, 11, sb_type3, sb_type4); |
||||
printf("SYNC type4: %s\n", bitdump(sb_type4, 120)); |
||||
|
||||
/* Run scrambling (all-zero): type-5 bits */ |
||||
memcpy(sb_type5, sb_type4, 120); |
||||
tetra_scramb_bits(SCRAMB_INIT, sb_type5, 120); |
||||
printf("SYNC type5: %s\n", bitdump(sb_type5, 120)); |
||||
|
||||
decode_sb1(sb_type5); |
||||
|
||||
/* Use pdu_sysinfo from testpdu.c */ |
||||
memset(si_type2, 0, sizeof(si_type2)); |
||||
cur = si_type2; |
||||
cur += osmo_pbit2ubit(si_type2, pdu_sysinfo, 124); |
||||
|
||||
/* Run it through CRC16-CCITT */ |
||||
crc = ~crc16_ccitt_bits(si_type2, 124); |
||||
crc = swap16(crc); |
||||
cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); |
||||
|
||||
/* Append 4 tail bits: type-2 bits */ |
||||
cur += 4; |
||||
|
||||
printf("SI type2: %s\n", bitdump(si_type2, 140)); |
||||
|
||||
/* Run rate 2/3 RCPC code: type-3 bits */ |
||||
{ |
||||
struct conv_enc_state *ces = calloc(1, sizeof(*ces)); |
||||
conv_enc_init(ces); |
||||
conv_enc_input(ces, sb_type2, 144, si_master); |
||||
get_punctured_rate(TETRA_RCPC_PUNCT_2_3, si_master, 216, si_type3); |
||||
free(ces); |
||||
} |
||||
printf("SI type3: %s\n", bitdump(si_type3, 216)); |
||||
|
||||
/* Run (216,101) block interleaving: type-4 bits */ |
||||
block_interleave(216, 101, si_type3, si_type4); |
||||
printf("SI type4: %s\n", bitdump(si_type4, 216)); |
||||
|
||||
/* Run scrambling (all-zero): type-5 bits */ |
||||
memcpy(si_type5, si_type4, 216); |
||||
|
||||
|
||||
/* Use pdu_acc_ass from testpdu.c */ |
||||
/* Run it through (30,14) RM code: type-2=3=4 bits */ |
||||
printf("AACH type-1: %s\n", bitdump(pdu_acc_ass, 2)); |
||||
bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); |
||||
printf("AACH RM3014: 0x0%x\n", bb_rm3014); |
||||
/* convert to big endian */ |
||||
bb_rm3014_be = htonl(bb_rm3014); |
||||
/* shift two bits left as it is only a 30 bit value */ |
||||
bb_rm3014_be <<= 2; |
||||
osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); |
||||
/* Run scrambling (all-zero): type-5 bits */ |
||||
printf("AACH type-5: %s\n", bitdump(bb_type5, 30)); |
||||
|
||||
/* Finally, hand it into the physical layer */ |
||||
build_sync_c_d_burst(burst, sb_type5, bb_type5, si_type5); |
||||
printf("cont sync DL burst: %s\n", bitdump(burst, 255*2)); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
int err, i; |
||||
uint16_t out; |
||||
uint32_t ret; |
||||
|
||||
/* first: run some subsystem tests */ |
||||
ret = tetra_punct_test(); |
||||
if (ret < 0) |
||||
exit(1); |
||||
|
||||
tetra_rm3014_init(); |
||||
#if 0 |
||||
ret = tetra_rm3014_compute(0x1001); |
||||
printf("RM3014: 0x%08x\n", ret); |
||||
|
||||
err = tetra_rm3014_decode(ret, &out); |
||||
printf("RM3014: 0x%x error: %d\n", out, err); |
||||
#endif |
||||
|
||||
/* finally, build some test PDUs and encocde them */ |
||||
testpdu_init(); |
||||
#if 0 |
||||
build_sb(); |
||||
|
||||
build_ndb_schf(); |
||||
#else |
||||
/* iterate over various random PDUs and throw them throguh the viterbi */ |
||||
srand(time(NULL)); |
||||
for (i = 0; i < 100; i++) { |
||||
uint32_t r = rand(); |
||||
osmo_pbit2ubit(pdu_sync, (uint8_t *) &r, 32); |
||||
osmo_pbit2ubit(pdu_sync+32, (uint8_t *)&r, 60-32); |
||||
//build_sb();
|
||||
|
||||
osmo_pbit2ubit(pdu_schf, (uint8_t *) &r, 32); |
||||
osmo_pbit2ubit(pdu_schf+32, (uint8_t *)&r, 60-32); |
||||
build_ndb_schf(); |
||||
} |
||||
#endif |
||||
|
||||
printf("total number of CRC Errors: %u\n", num_crc_err); |
||||
|
||||
exit(0); |
||||
} |
@ -0,0 +1,73 @@ |
||||
/* (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 <stdio.h> |
||||
|
||||
#include "tetra_common.h" |
||||
#include <lower_mac/crc_simple.h> |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
uint8_t input1[] = { 0x01 }; |
||||
uint16_t crc; |
||||
|
||||
crc = crc16_itut_bytes(0x0, input1, 8); |
||||
printf("The CRC is now: %u/0x%x\n", crc, crc); |
||||
|
||||
crc = crc16_itut_bits(0x0, input1, 1); |
||||
printf("The CRC is now: %d/0x%x\n", crc, crc); |
||||
|
||||
crc = crc16_itut_bytes(0xFFFF, input1, 8); |
||||
printf("The CRC is now: %u/0x%x\n", crc, crc); |
||||
|
||||
crc = crc16_itut_bits(0xFFFF, input1, 1); |
||||
printf("The CRC is now: %d/0x%x\n", crc, crc); |
||||
|
||||
/* actual CRC-CCIT usage */ |
||||
uint8_t input2[] = { |
||||
0, 0, 0, 1, 0, 0, 0, 0, |
||||
1, 0, 1, 1, 0, 0, 0, 0, |
||||
1, 0, 1, 1, 1, 1, 1, 0, |
||||
0, 0, 0, 0, 0, 0, 0, 0, |
||||
1, 0, 0, 0, 0, 0, 1, 1, |
||||
0, 0, 0, 0, 0, 1, 1, 1, |
||||
1, 1, 0, 1, 0, 0, 1, 1, |
||||
0, 0, 1, 1, |
||||
|
||||
/* checksum */ |
||||
1, 1, 0, 1, 1, 1, 1, 0, |
||||
1, 1, 1, 1, 0, 0, 0, 1, |
||||
/* tail bits */ |
||||
0, 0, 0, 0 }; |
||||
|
||||
|
||||
crc = ~crc16_itut_bits(0xffff, input2, 60); |
||||
printf("The CRC is now: %d/0x%x\n", crc, crc); |
||||
|
||||
/* swap the bytes */ |
||||
crc = (crc << 8) | (crc >> 8); |
||||
osmo_pbit2ubit(&input2[60], (uint8_t *)&crc, 16); |
||||
|
||||
crc = crc16_itut_bits(0xFFFF, input2, 60 + 16); |
||||
printf("The CRC is now: %d/0x%x\n", crc, crc); |
||||
if (crc == 0x1d0f) |
||||
printf("Decoded successfully.\n"); |
||||
else |
||||
printf("Failed to decode.\n"); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,117 @@ |
||||
/* Convert floating point symbols in the range +3/-3 to hard bits,
|
||||
* in the 1-bit-per-byte format */ |
||||
|
||||
/* (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 <unistd.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
|
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
|
||||
static int process_sym_fl(float offset, float fl) |
||||
{ |
||||
int ret; |
||||
fl += offset; |
||||
|
||||
/* very simplistic scheme */ |
||||
if (fl > 2) |
||||
ret = 3; |
||||
else if (fl > 0) |
||||
ret = 1; |
||||
else if (fl < -2) |
||||
ret = -3; |
||||
else |
||||
ret = -1; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static void sym_int2bits(int sym, uint8_t *ret) |
||||
{ |
||||
switch (sym) { |
||||
case -3: |
||||
ret[0] = 1; |
||||
ret[1] = 1; |
||||
break; |
||||
case 1: |
||||
ret[0] = 0; |
||||
ret[1] = 0; |
||||
break; |
||||
//case -1:
|
||||
case 3: |
||||
ret[0] = 0; |
||||
ret[1] = 1; |
||||
break; |
||||
//case 3:
|
||||
case -1: |
||||
ret[0] = 1; |
||||
ret[1] = 0; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
int fd, fd_out; |
||||
|
||||
if (argc < 3) { |
||||
fprintf(stderr, "Usage: %s <infile> <outfile>\n", argv[0]); |
||||
exit(2); |
||||
} |
||||
|
||||
fd = open(argv[1], O_RDONLY); |
||||
if (fd < 0) { |
||||
perror("open infile"); |
||||
exit(1); |
||||
} |
||||
fd_out = creat(argv[2], 0660); |
||||
if (fd_out < 0) { |
||||
perror("open outfile"); |
||||
exit(1); |
||||
} |
||||
while (1) { |
||||
int rc; |
||||
float fl; |
||||
uint8_t bits[2]; |
||||
rc = read(fd, &fl, sizeof(fl)); |
||||
if (rc < 0) { |
||||
perror("read"); |
||||
exit(1); |
||||
} else if (rc == 0) |
||||
break; |
||||
rc = process_sym_fl(0.3f, fl); |
||||
sym_int2bits(rc, bits); |
||||
//printf("%2d %1u %1u %f\n", rc, bits[0], bits[1], fl);
|
||||
//printf("%1u%1u", bits[0], bits[1]);
|
||||
|
||||
rc = write(fd_out, bits, 2); |
||||
if (rc < 0) { |
||||
perror("write"); |
||||
exit(1); |
||||
} else if (rc == 0) |
||||
break; |
||||
} |
||||
exit(0); |
||||
} |
@ -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); |
||||
} |
@ -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 |
@ -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; |
||||
} |
@ -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 */ |
@ -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]; |
||||
} |
||||
} |
@ -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 */ |
@ -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)); |
||||
|