├── src ├── tuntap.h ├── .gitignore ├── example_keyfile.txt ├── lower_mac │ ├── viterbi.h │ ├── viterbi_cch.h │ ├── viterbi_tch.h │ ├── tetra_rm3014.h │ ├── viterbi.c │ ├── tetra_interleave.h │ ├── tetra_scramb.h │ ├── tetra_conv_enc.h │ ├── crc_simple.h │ ├── viterbi_tch.c │ ├── viterbi_cch.c │ ├── tetra_interleave.c │ ├── crc_simple.c │ ├── tetra_rm3014.c │ ├── tetra_scramb.c │ ├── tch_reordering.c │ └── tetra_conv_enc.c ├── tetra_llc.h ├── tetra_mle.h ├── crypto │ ├── tea2.h │ ├── tea3.h │ ├── tea1.h │ ├── hurdle.h │ ├── taa1.h │ ├── tetra_crypto.h │ ├── tea3.c │ ├── tea2.c │ └── tea1.c ├── testpdu.h ├── receiver1 ├── demod │ ├── .gitignore │ ├── simdemod2.py │ ├── fcdp-tetra_demod.py │ ├── fcdp-tetra_demod_fft.py │ ├── simdemod3.py │ └── osmosdr-tetra_demod_fft.py ├── tetra_gsmtap.h ├── receiver2 ├── tetra_tdma.h ├── phy │ ├── tetra_burst_sync.h │ ├── tetra_burst.h │ └── tetra_burst_sync.c ├── tetra_sndcp_pdu.h ├── tetra_mle_pdu.h ├── tuntap.c ├── tetra_mm_pdu.h ├── tetra_upper_mac.h ├── tetra_prim.h ├── tetra_cmce_pdu.h ├── Makefile ├── tetra_mle.c ├── tetra_mm_pdu.c ├── receiver1udp ├── tetra_sndcp_pdu.c ├── tetra_mle_pdu.c ├── tetra_common.h ├── tetra-rx-tests.sh ├── tetra_gsmtap.c ├── crc_test.c ├── tetra_tdma.c ├── tetra_cmce_pdu.c ├── tetra-rx.c ├── tetra_llc_pdu.h ├── tunctl.c ├── float_to_bits.c ├── tetra_common.c ├── testpdu.c ├── tetra_llc.c ├── tetra_mac_pdu.h ├── tetra_llc_pdu.c └── conv_enc_test.c ├── AUTHORS ├── etsi_codec-patches ├── series ├── fix_64bit.patch ├── download_and_patch.sh ├── filename-case.patch ├── README ├── makefile-cleanups.patch ├── log_stderr.patch └── round_private.patch ├── TODO ├── contrib └── jenkins.sh └── README.md /src/tuntap.h: -------------------------------------------------------------------------------- 1 | #ifndef TUNTAP_H 2 | #define TUNTAP_H 3 | 4 | int tun_alloc(char *dev); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Harald Welte 2 | Sylvain Munaut 3 | Holger Hans Peter Freyther 4 | -------------------------------------------------------------------------------- /etsi_codec-patches/series: -------------------------------------------------------------------------------- 1 | makefile-cleanups.patch 2 | fix_64bit.patch 3 | round_private.patch 4 | filename-case.patch 5 | log_stderr.patch 6 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.pyc 4 | .*.swp 5 | conv_enc_test 6 | tetra-rx 7 | float_to_bits 8 | crc_test 9 | tunctl 10 | tests_data/* 11 | -------------------------------------------------------------------------------- /src/example_keyfile.txt: -------------------------------------------------------------------------------- 1 | network mcc 204 mnc 1337 ksg_type 1 security_class 2 2 | key mcc 204 mnc 1337 addr 00000000 key_type 1 key_num 1234 key 00112233445566778899 3 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi.h: -------------------------------------------------------------------------------- 1 | #ifndef VITERBI_H 2 | #define VITERBI_H 3 | 4 | void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count); 5 | 6 | #endif /* VITERBI_H */ 7 | -------------------------------------------------------------------------------- /src/tetra_llc.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_LLC_H 2 | #define TETRA_LLC_H 3 | 4 | #include "tetra_common.h" 5 | 6 | int rx_tm_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/tetra_mle.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_MLE_H 2 | #define TETRA_MLE_H 3 | 4 | #include "tetra_common.h" 5 | 6 | int rx_tl_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/crypto/tea2.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_TEA2_H 2 | #define HAVE_TEA2_H 3 | 4 | #include 5 | 6 | void tea2(uint32_t dwFrameNumbers, uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut); 7 | 8 | #endif /* HAVE_TEA2_H */ 9 | -------------------------------------------------------------------------------- /src/crypto/tea3.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_TEA3_H 2 | #define HAVE_TEA3_H 3 | 4 | #include 5 | 6 | void tea3(uint32_t dwFrameNumbers, uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut); 7 | 8 | #endif /* HAVE_TEA3_H */ 9 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * implement lower mac processing chain for TCH/F voice frames 2 | * implement STCH / TCH separation and 'reduced/half' voice frame 3 | * implement wireshark dissector using GSMTAP TETRA encapsulation 4 | * implement simple BSCH/BNCH transmitter 5 | -------------------------------------------------------------------------------- /src/crypto/tea1.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_TEA1_H 2 | #define HAVE_TEA1_H 3 | 4 | #include 5 | 6 | 7 | void tea1(uint32_t dwFrameNumbers, const uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut); 8 | 9 | #endif /* HAVE_TEA1_H */ 10 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi_cch.h: -------------------------------------------------------------------------------- 1 | #ifndef VITERBI_CCH_H 2 | #define VITERBI_CCH_H 3 | 4 | int conv_cch_encode(uint8_t *input, uint8_t *output, int n); 5 | int conv_cch_decode(int8_t *input, uint8_t *output, int n); 6 | 7 | #endif /* VITERBI_CCH_H */ 8 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi_tch.h: -------------------------------------------------------------------------------- 1 | #ifndef VITERBI_TCH_H 2 | #define VITERBI_TCH_H 3 | 4 | int conv_tch_encode(uint8_t *input, uint8_t *output, int n); 5 | int conv_tch_decode(int8_t *input, uint8_t *output, int n); 6 | 7 | #endif /* VITERBI_TCH_H */ 8 | -------------------------------------------------------------------------------- /src/testpdu.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | extern uint8_t pdu_sync[8]; /* 60 bits */ 5 | extern uint8_t pdu_sysinfo[16]; /* 124 bits */ 6 | extern uint8_t pdu_acc_ass[2]; 7 | extern uint8_t pdu_schf[268]; 8 | 9 | void testpdu_init(); 10 | 11 | -------------------------------------------------------------------------------- /src/receiver1: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is an example how to use simdemod.py --sq5bpf 3 | export TETRA_HACK_PORT=7379 4 | export TETRA_HACK_RXID=$1 5 | FIFO=/tmp/fifo$1 6 | mkfifo $FIFO 7 | ulimit -c unlimited 8 | demod/python/simdemod2.py -o /dev/stdout -i $FIFO | ./float_to_bits /dev/stdin /dev/stdout | ./tetra-rx /dev/stdin 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_rm3014.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_RM3014_H 2 | #define TETRA_RM3014_H 3 | 4 | #include 5 | 6 | void tetra_rm3014_init(void); 7 | uint32_t tetra_rm3014_compute(const uint16_t in); 8 | 9 | /** 10 | * Decode @param inp to @param out and return if there was 11 | * an error in the input. In the future this should correct 12 | * the error or such. 13 | */ 14 | int tetra_rm3013_decode(const uint32_t inp, uint16_t *out); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/demod/.gitignore: -------------------------------------------------------------------------------- 1 | COPYING 2 | INSTALL 3 | Makefile 4 | Makefile.in 5 | aclocal.m4 6 | config.guess 7 | config.h 8 | config.h.in 9 | config.h.in~ 10 | config.log 11 | config.status 12 | config.sub 13 | config/libtool.m4 14 | config/ltoptions.m4 15 | config/ltsugar.m4 16 | config/ltversion.m4 17 | config/lt~obsolete.m4 18 | configure 19 | install-sh 20 | libtool 21 | ltmain.sh 22 | missing 23 | py-compile 24 | py_run_tests 25 | stamp-h1 26 | autom4te.cache 27 | *.sw? 28 | -------------------------------------------------------------------------------- /src/tetra_gsmtap.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_GSMTAP_H 2 | #define TETRA_GSMTAP_H 3 | #include "tetra_common.h" 4 | 5 | struct msgb *tetra_gsmtap_makemsg(struct tetra_tdma_time *tm, enum tetra_log_chan lchan, uint8_t ts, uint8_t ss, 6 | int8_t signal_dbm, uint8_t snr, const uint8_t *bitdata, unsigned int bitlen, 7 | struct tetra_mac_state *tms); 8 | 9 | int tetra_gsmtap_sendmsg(struct msgb *msg); 10 | 11 | int tetra_gsmtap_init(const char *host, uint16_t port); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/receiver2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is an example how to use simdemod.py --sq5bpf 3 | # this is receiver 1, only with the port changed, it will be used in the example documentation 4 | export TETRA_HACK_PORT=7380 #i might as well have made this a command line parameter, oh well :) 5 | export TETRA_HACK_RXID=$1 6 | FIFO=/tmp/fifo$1 7 | mkfifo $FIFO 8 | ulimit -c unlimited 9 | demod/python/simdemod2.py -o /dev/stdout -i $FIFO | ./float_to_bits /dev/stdin /dev/stdout | ./tetra-rx /dev/stdin 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count) 7 | { 8 | int8_t vit_inp[864*4] = {0}; 9 | int i; 10 | 11 | for (i = 0; i < sym_count*4; i++) { 12 | switch (in[i]) { 13 | case 0: 14 | vit_inp[i] = 127; 15 | break; 16 | case 0xff: 17 | vit_inp[i] = 0; 18 | break; 19 | default: 20 | vit_inp[i] = -127; 21 | break; 22 | } 23 | } 24 | conv_cch_decode(vit_inp, out, sym_count); 25 | } 26 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_interleave.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_INTERLEAVE_H 2 | #define TETRA_INTERLEAVE_H 3 | /* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ 4 | 5 | /* This converts from type type-3 bits into type-4 bits (and vice versa) */ 6 | 7 | #include 8 | 9 | void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out); 10 | void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out); 11 | 12 | void matrix_interleave(uint32_t lines, uint32_t columns, 13 | const uint8_t *in, uint8_t *out); 14 | void matrix_deinterleave(uint32_t lines, uint32_t columns, 15 | const uint8_t *in, uint8_t *out); 16 | 17 | #endif /* TETRA_INTERLEAVE_H */ 18 | -------------------------------------------------------------------------------- /src/crypto/hurdle.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_HURDLE_H 2 | #define HAVE_HURDLE_H 3 | 4 | #include 5 | 6 | struct hurdle_ctx { 7 | uint8_t abRoundKeys[256]; 8 | }; 9 | 10 | #define HURDLE_ENCRYPT 0 11 | #define HURDLE_DECRYPT 1 12 | 13 | void hurdle_set_key(uint8_t *k, struct hurdle_ctx *lpContextOut); 14 | void HURDLE_encrypt(uint8_t abOutput[8], const uint8_t abInput[8], struct hurdle_ctx *lpKey, uint8_t eEncryptMode); 15 | void HURDLE_enc_cbc(uint8_t abCiphertext[16], const uint8_t abPlaintext[16], uint8_t abKey[16]); 16 | void HURDLE_dec_cts(uint8_t abPlaintext[15], const uint8_t abCiphertext[15], uint8_t abKey[16]); 17 | 18 | extern const uint8_t g_abHurdleSbox[256]; 19 | extern const uint8_t g_abHurdleInvSbox[256]; 20 | 21 | #endif /* HAVE_HURDLE_H */ 22 | -------------------------------------------------------------------------------- /src/tetra_tdma.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_TDMA_H 2 | #define TETRA_TDMA_H 3 | 4 | #include 5 | 6 | struct tetra_tdma_time { 7 | uint16_t hn; /* hyperframe number (1 ... 65535) */ 8 | uint32_t sn; /* symbol number (1 ... 255) */ 9 | uint32_t tn; /* timeslot number (1 .. 4) */ 10 | uint32_t fn; /* frame number (1 .. 18) */ 11 | uint32_t mn; /* multiframe number (1 .. 60) */ 12 | }; 13 | 14 | void tetra_tdma_time_add_sym(struct tetra_tdma_time *tm, uint32_t sym_count); 15 | void tetra_tdma_time_add_tn(struct tetra_tdma_time *tm, uint32_t tn_count); 16 | void tetra_tdma_time_add_fn(struct tetra_tdma_time *tm, uint32_t fn_count); 17 | char *tetra_tdma_time_dump(const struct tetra_tdma_time *tm); 18 | 19 | uint32_t tetra_tdma_time2fn(struct tetra_tdma_time *tm); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_scramb.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_SCRAMB_H 2 | #define TETRA_SCRAMB_H 3 | /* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */ 4 | 5 | /* This converts from type-4 to type-5 bits (and vice versa) */ 6 | 7 | #include 8 | 9 | /* Section 8.2.5.2: 10 | * For scrambling of BSCH, all bits e(1) to e(30) shall equal to zero 11 | * p(k) = e(1-k) for k = -29, ... 0 12 | * p(k) = 1 for k = -31, -30 13 | */ 14 | #define SCRAMB_INIT 3 15 | 16 | uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour); 17 | 18 | int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len); 19 | 20 | /* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */ 21 | int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len); 22 | 23 | #endif /* TETRA_SCRAMB_H */ 24 | -------------------------------------------------------------------------------- /src/phy/tetra_burst_sync.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_BURST_SYNC_H 2 | #define TETRA_BURST_SYNC_H 3 | 4 | #include 5 | 6 | enum rx_state { 7 | RX_S_UNLOCKED, /* we're completely unlocked */ 8 | RX_S_KNOW_FSTART, /* we know the next frame start */ 9 | RX_S_LOCKED, /* fully locked */ 10 | }; 11 | 12 | struct tetra_rx_state { 13 | enum rx_state state; 14 | unsigned int bits_in_buf; /* how many bits are currently in bitbuf */ 15 | uint8_t bitbuf[4096]; 16 | unsigned int bitbuf_start_bitnum; /* bit number at first element in bitbuf */ 17 | unsigned int next_frame_start_bitnum; /* frame start expected at this bitnum */ 18 | 19 | void *burst_cb_priv; 20 | }; 21 | 22 | 23 | /* input a raw bitstream into the tetra burst synchronizaer */ 24 | int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len); 25 | 26 | #endif /* TETRA_BURST_SYNC_H */ 27 | -------------------------------------------------------------------------------- /src/tetra_sndcp_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_SNDCP_PDU_H 2 | #define TETRA_SNDCP_PDU_H 3 | 4 | #include 5 | 6 | /* 28.115 */ 7 | enum sndcp_pdu_type { 8 | SNDCP_PDU_T_ACT_PDP_ACCEPT = 0x0, 9 | SNDCP_PDU_T_DEACT_PDP_ACC = 0x1, 10 | SNDCP_PDU_T_DEACT_PDP_DEMAND = 0x2, 11 | SNDCP_PDU_T_ACT_PDP_REJECT = 0x3, 12 | SNDCP_PDU_T_UNITDATA = 0x4, 13 | SNDCP_PDU_T_DATA = 0x5, 14 | SNDCP_PDU_T_DATA_TX_REQ = 0x6, 15 | SNDCP_PDU_T_DATA_TX_RESP = 0x7, 16 | SNDCP_PDU_T_END_OF_DATA = 0x8, 17 | SNDCP_PDU_T_RECONNECT = 0x9, 18 | SNDCP_PDU_T_PAGE_REQUEST = 0xa, 19 | SNDCP_PDU_T_NOT_SUPPORTED = 0xb, 20 | SNDCP_PDU_T_DATA_PRIORITY = 0xc, 21 | SNDCP_PDU_T_MODIFY = 0xd, 22 | }; 23 | 24 | #define SNDCP_PDU_T_ACT_PDP_DEMAND SNDCP_PDU_T_ACT_PDP_ACCEPT 25 | #define SNDCP_PDU_T_PAGE_RESPONSE SNDCP_PDU_T_PAGE_REQUEST 26 | 27 | #endif /* TETRA_SNDCP_PDU_H */ 28 | 29 | const char *tetra_get_sndcp_pdut_name(uint8_t pdut, int uplink); 30 | -------------------------------------------------------------------------------- /contrib/jenkins.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org 3 | 4 | if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then 5 | echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" 6 | exit 2 7 | fi 8 | 9 | set -ex 10 | 11 | base="$PWD" 12 | deps="$base/deps" 13 | inst="$deps/install" 14 | export deps inst 15 | 16 | osmo-clean-workspace.sh 17 | 18 | mkdir "$deps" || true 19 | 20 | osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false 21 | 22 | verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") 23 | 24 | export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" 25 | export LD_LIBRARY_PATH="$inst/lib" 26 | 27 | set +x 28 | echo 29 | echo 30 | echo 31 | echo " =============================== osmo-tetra ===============================" 32 | echo 33 | set -x 34 | 35 | cd "$base" 36 | cd src 37 | $MAKE clean || true 38 | $MAKE 39 | 40 | osmo-clean-workspace.sh 41 | -------------------------------------------------------------------------------- /src/tetra_mle_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_MLE_PDU_H 2 | #define TETRA_MLE_PDU_H 3 | 4 | #include 5 | 6 | /* 18.5.20 */ 7 | enum tetra_mle_pdu_type_d { 8 | TMLE_PDUT_D_NEW_CELL = 0, 9 | TMLE_PDUT_D_PREPARE_FAIL = 1, 10 | TMLE_PDUT_D_NWRK_BROADCAST = 2, 11 | TMLE_PDUT_D_NWRK_BROADCAST_EXT = 3, 12 | TMLE_PDUT_D_RESTORE_ACK = 4, 13 | TMLE_PDUT_D_RESTORE_FAIL = 5, 14 | TMLE_PDUT_D_CHANNEL_RESPONSE = 6 15 | }; 16 | enum tetra_mle_pdu_type_u { 17 | TMLE_PDUT_U_PREPARE = 0, 18 | TMLE_PDUT_U_SECTOR_ADVICE = 2, 19 | TMLE_PDUT_U_CHANNEL_ADVICE = 3, 20 | TMLE_PDUT_U_RESTORE = 4, 21 | TMLE_PDUT_U_CHANNEL_REQUEST = 6, 22 | }; 23 | const char *tetra_get_mle_pdut_name(unsigned int pdut, int uplink); 24 | 25 | /* 18.5.21 */ 26 | enum tetra_mle_pdisc { 27 | TMLE_PDISC_MM = 1, 28 | TMLE_PDISC_CMCE = 2, 29 | TMLE_PDISC_SNDCP = 4, 30 | TMLE_PDISC_MLE = 5, 31 | TMLE_PDISC_MGMT = 6, 32 | TMLE_PDISC_TEST = 7, 33 | }; 34 | const char *tetra_get_mle_pdisc_name(uint8_t pdisc); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /etsi_codec-patches/fix_64bit.patch: -------------------------------------------------------------------------------- 1 | diff -Nruw codec.orig/amr-code/source.h codec/amr-code/source.h 2 | --- codec.orig/amr-code/source.h 2016-12-18 16:17:46.238404730 +0100 3 | +++ codec/amr-code/source.h 2016-12-18 16:18:21.358545740 +0100 4 | @@ -19,10 +19,11 @@ 5 | 6 | #ifndef TYPEDEF_H 7 | #define TYPEDEF_H 8 | +#include 9 | 10 | 11 | -typedef short Word16; 12 | -typedef long Word32; 13 | +typedef int16_t Word16; 14 | +typedef int32_t Word32; 15 | typedef int Flag; 16 | 17 | #endif 18 | diff -Nruw codec.orig/c-code/source.h codec/c-code/source.h 19 | --- codec.orig/c-code/source.h 2016-12-18 16:17:46.242404746 +0100 20 | +++ codec/c-code/source.h 2016-12-18 16:18:54.494678813 +0100 21 | @@ -19,10 +19,11 @@ 22 | 23 | #ifndef TYPEDEF_H 24 | #define TYPEDEF_H 25 | +#include 26 | 27 | 28 | -typedef short Word16; 29 | -typedef long Word32; 30 | +typedef int16_t Word16; 31 | +typedef int32_t Word32; 32 | typedef int Flag; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /etsi_codec-patches/download_and_patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | URL=http://www.etsi.org/deliver/etsi_en/300300_300399/30039502/01.03.01_60/en_30039502v010301p0.zip 4 | MD5_EXP=a8115fe68ef8f8cc466f4192572a1e3e 5 | LOCAL_FILE=etsi_tetra_codec.zip 6 | 7 | PATCHDIR=`pwd` 8 | CODECDIR=`pwd`/../codec 9 | 10 | echo Deleting $CODECDIR ... 11 | [ -e $CODECDIR ] && rm -rf $CODECDIR 12 | 13 | echo Creating $CODECDIR ... 14 | mkdir $CODECDIR 15 | 16 | if [ ! -f $LOCAL_FILE ]; then 17 | echo Downloading $URL ... 18 | wget -O $LOCAL_FILE $URL 19 | else 20 | echo Skipping download, file $LOCAL_FILE exists 21 | fi 22 | MD5=`md5sum $LOCAL_FILE | awk '{ print $1 }'` 23 | 24 | echo Checking MD5SUM ... 25 | if [ $MD5 != $MD5_EXP ]; then 26 | print "MD5sum of ETSI reference codec file doesn't match" 27 | exit 1 28 | fi 29 | 30 | echo Unpacking ZIP ... 31 | cd $CODECDIR 32 | unzip -L $PATCHDIR/etsi_tetra_codec.zip 33 | 34 | echo Applying Patches ... 35 | for p in `cat "$PATCHDIR/series"`; do 36 | echo "=> Applying patch '$p'..." 37 | patch -p1 < "$PATCHDIR/$p" 38 | done 39 | 40 | echo Done! 41 | -------------------------------------------------------------------------------- /src/tuntap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | int tun_alloc(char *dev) 14 | { 15 | struct ifreq ifr; 16 | int fd, err; 17 | 18 | fd = open("/dev/net/tun", O_RDWR); 19 | if (fd < 0) 20 | return fd; 21 | // return tun_alloc_old(dev); 22 | 23 | memset(&ifr, 0, sizeof(ifr)); 24 | 25 | /* Flags: IFF_TUN - TUN device (no Ethernet headers) 26 | * IFF_TAP - TAP device 27 | * 28 | * IFF_NO_PI - Do not provide packet information 29 | */ 30 | ifr.ifr_flags = IFF_TUN|IFF_NO_PI; 31 | if( *dev ) 32 | snprintf(ifr.ifr_name, IFNAMSIZ, "%s", dev); 33 | 34 | if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ 35 | close(fd); 36 | return err; 37 | } 38 | #if 0 39 | strcpy(dev, ifr.ifr_name); 40 | #endif 41 | return fd; 42 | } 43 | -------------------------------------------------------------------------------- /src/tetra_mm_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_MM_PDU_H 2 | #define TETRA_MM_PDU_H 3 | 4 | /* 16.10.39 PDU Type */ 5 | enum tetra_mm_pdu_type_d { 6 | TMM_PDU_T_D_OTAR = 0x0, 7 | TMM_PDU_T_D_AUTH = 0x1, 8 | TMM_PDU_T_D_CK_CHG_DEM = 0x2, 9 | TMM_PDU_T_D_DISABLE = 0x3, 10 | TMM_PDU_T_D_ENABLE = 0x4, 11 | TMM_PDU_T_D_LOC_UPD_ACC = 0x5, 12 | TMM_PDU_T_D_LOC_UPD_CMD = 0x6, 13 | TMM_PDU_T_D_LOC_UPD_REJ = 0x7, 14 | /* RES */ 15 | TMM_PDU_T_D_LOC_UPD_PROC = 0x9, 16 | TMM_PDU_T_D_ATT_DET_GRP = 0xa, 17 | TMM_PDU_T_D_ATT_DET_GRP_ACK = 0xb, 18 | TMM_PDU_T_D_MM_STATUS = 0xc, 19 | /* RES */ 20 | /* RES */ 21 | TMM_PDU_T_D_MM_PDU_NOTSUPP = 0xf 22 | }; 23 | 24 | /* 16.10.35a Location update accept type */ 25 | enum tetra_mm_loc_upd_acc_type { 26 | TMM_LUPD_ACC_T_ROAMING = 0, 27 | TMM_LUPD_ACC_T_TEMPORARY = 1, 28 | TMM_LUPD_ACC_T_PERIODIC = 2, 29 | TMM_LUPD_ACC_T_ITSI_ATT = 3, 30 | TMM_LUPD_ACC_T_CALL_RESTORE = 4, 31 | TMM_LUPD_ACC_T_MIGRATING = 5, 32 | TMM_LUPD_ACC_T_DEMAND = 6, 33 | TMM_LUPD_ACC_T_DISABLED = 7 34 | }; 35 | 36 | const char *tetra_get_mm_pdut_name(uint8_t pdut, int uplink); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/tetra_upper_mac.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_UPPER_MAC_H 2 | #define TETRA_UPPER_MAC_H 3 | 4 | #include "tetra_prim.h" 5 | 6 | #define REASSEMBLE_FRAGMENTS 1 /* Set to 0 to disable reassembly functionality */ 7 | #define FRAGSLOT_NR_SLOTS 5 /* Slot 0 is unused */ 8 | 9 | #define N203 6 /* Fragslot max age, see N.203 in the tetra docs, must be 4 multiframes or greater */ 10 | #define FRAGSLOT_MSGB_SIZE 8192 11 | struct fragslot { 12 | bool active; /* Set to 1 when fragslot holds a partially constructed message */ 13 | uint32_t age; /* Maintains the number of multiframes since the last fragment */ 14 | int num_frags; /* Maintains the number of fragments appended in the msgb */ 15 | int length; /* Maintains the number of bits appended in the msgb */ 16 | bool encryption; /* Set to true if the fragments were received encrypted */ 17 | struct tetra_key *key; /* Holds pointer to the key to be used for this slot */ 18 | struct msgb *msgb; /* Message buffer in which fragments are appended */ 19 | }; 20 | 21 | void upper_mac_init_fragslots(); 22 | int upper_mac_prim_recv(struct osmo_prim_hdr *op, void *priv); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_conv_enc.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_CONV_ENC_H 2 | #define TETRA_CONV_ENC_H 3 | 4 | #include 5 | 6 | struct conv_enc_state { 7 | uint8_t delayed[4]; 8 | }; 9 | 10 | 11 | /* in: one-bit-per-byte out: 4bit-per-byte, both 'len' long */ 12 | int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out); 13 | 14 | int conv_enc_init(struct conv_enc_state *ces); 15 | 16 | enum tetra_rcpc_puncturer { 17 | TETRA_RCPC_PUNCT_2_3, 18 | TETRA_RCPC_PUNCT_1_3, 19 | TETRA_RCPC_PUNCT_292_432, 20 | TETRA_RCPC_PUNCT_148_432, 21 | TETRA_RCPC_PUNCT_112_168, 22 | TETRA_RCPC_PUNCT_72_162, 23 | TETRA_RCPC_PUNCT_38_80, 24 | }; 25 | 26 | /* Puncture the mother code (in) and write 'len' symbols to out */ 27 | int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out); 28 | 29 | 30 | /* De-Puncture the 'len' type-3 bits (in) and write mother code to out */ 31 | int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out); 32 | 33 | /* Self-test the puncturing/de-puncturing */ 34 | int tetra_punct_test(void); 35 | 36 | #endif /* TETRA_CONV_ENC_H */ 37 | -------------------------------------------------------------------------------- /src/phy/tetra_burst.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_BURST_H 2 | #define TETRA_BURST_H 3 | 4 | #include 5 | 6 | #define BLK_1 1 7 | #define BLK_2 2 8 | 9 | enum tp_sap_data_type { 10 | TPSAP_T_SB1, 11 | TPSAP_T_SB2, 12 | TPSAP_T_NDB, 13 | TPSAP_T_BBK, 14 | TPSAP_T_SCH_HU, 15 | TPSAP_T_SCH_F, 16 | }; 17 | 18 | extern void tp_sap_udata_ind(enum tp_sap_data_type type, int blk_num, const uint8_t *bits, unsigned int len, void *priv); 19 | 20 | /* 9.4.4.2.6 Synchronization continuous downlink burst */ 21 | int build_sync_c_d_burst(uint8_t *buf, const uint8_t *sb, const uint8_t *bb, const uint8_t *bkn); 22 | 23 | /* 9.4.4.2.5 Normal continuous downlink burst */ 24 | int build_norm_c_d_burst(uint8_t *buf, const uint8_t *bkn1, const uint8_t *bb, const uint8_t *bkn2, int two_log_chan); 25 | 26 | enum tetra_train_seq { 27 | TETRA_TRAIN_NORM_1, 28 | TETRA_TRAIN_NORM_2, 29 | TETRA_TRAIN_NORM_3, 30 | TETRA_TRAIN_SYNC, 31 | TETRA_TRAIN_EXT, 32 | }; 33 | 34 | /* find a TETRA training sequence in the burst buffer indicated */ 35 | int tetra_find_train_seq(const uint8_t *in, unsigned int end_of_in, 36 | uint32_t mask_of_train_seq, unsigned int *offset); 37 | 38 | #endif /* TETRA_BURST_H */ 39 | -------------------------------------------------------------------------------- /etsi_codec-patches/filename-case.patch: -------------------------------------------------------------------------------- 1 | Index: etsi/amr-code/init_params.c 2 | =================================================================== 3 | --- etsi.orig/amr-code/init_params.c 2011-05-29 12:11:52.000000000 +0200 4 | +++ etsi/amr-code/init_params.c 2011-05-29 12:11:21.000000000 +0200 5 | @@ -26,11 +26,11 @@ 6 | #include "const.tab" /* contains constants for channel coding/decoding */ 7 | #include "arrays.h" /* contains constants for channel coding/decoding */ 8 | 9 | -#include "const_TETRA.tab" /* contains constants for channel coding/decoding */ 10 | -#include "arrays_TETRA.tab" /* contains arrays for channel coding/decoding */ 11 | +#include "const_tetra.tab" /* contains constants for channel coding/decoding */ 12 | +#include "arrays_tetra.tab" /* contains arrays for channel coding/decoding */ 13 | 14 | -#include "const_AMR475.tab" /* contains constants for channel coding/decoding */ 15 | -#include "arrays_AMR475.tab" /* contains arrays for channel coding/decoding */ 16 | +#include "const_amr475.tab" /* contains constants for channel coding/decoding */ 17 | +#include "arrays_amr475.tab" /* contains arrays for channel coding/decoding */ 18 | 19 | #define ALLOW_NEG(x) (((x) < 0) ? (((-x)%2 == 1) ? (-x)/2 - N1_2 + 1 : (x)/2 + 1) : (x)) 20 | 21 | -------------------------------------------------------------------------------- /src/tetra_prim.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_PRIM_H 2 | #define TETRA_PRIM_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "tetra_common.h" 9 | 10 | enum tetra_saps { 11 | TETRA_SAP_TP, /* between PHY and lower MAC */ 12 | TETRA_SAP_TMV, /* between lower and upper MAC */ 13 | TETRA_SAP_TMA, 14 | TETRA_SAP_TMB, 15 | TETRA_SAP_TMD, 16 | }; 17 | 18 | /* Table 23.1 */ 19 | enum tmv_sap_prim { 20 | PRIM_TMV_UNITDATA, 21 | PRIM_TMV_CONFIGURE, 22 | }; 23 | 24 | /* Table 23.2 */ 25 | struct tmv_unitdata_param { 26 | uint32_t mac_block_len; /* length of mac block */ 27 | enum tetra_log_chan lchan; /* to which lchan do we belong? */ 28 | int crc_ok; /* was the CRC verified OK? */ 29 | uint32_t scrambling_code; /* which scrambling code was used */ 30 | struct tetra_tdma_time tdma_time;/* TDMA timestamp */ 31 | int blk_num; /* Indicates whether BLK1 or BLK2 in the downlink burst */ 32 | //uint8_t mac_block[412]; /* maximum num of bits in a non-QAM chan */ 33 | }; 34 | 35 | /* Table 23.3 */ 36 | struct tmv_configure_param { 37 | /* FIXME */ 38 | uint32_t scrambling_rx; 39 | }; 40 | 41 | struct tetra_tmvsap_prim { 42 | struct osmo_prim_hdr oph; 43 | union { 44 | struct tmv_unitdata_param unitdata; 45 | struct tmv_configure_param configure; 46 | } u; 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/tetra_cmce_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_CMCE_PDU_H 2 | #define TETRA_CMCE_PDU_H 3 | 4 | #include 5 | 6 | /* 14.8.28 */ 7 | enum tetra_cmce_pdu_type_d { 8 | TCMCE_PDU_T_D_ALERT = 0x00, 9 | TCMCE_PDU_T_D_CALL_PROCEEDING = 0x01, 10 | TCMCE_PDU_T_D_CONNECT = 0x02, 11 | TCMCE_PDU_T_D_CONNECT_ACK = 0x03, 12 | TCMCE_PDU_T_D_DISCONNECT = 0x04, 13 | TCMCE_PDU_T_D_INFO = 0x05, 14 | TCMCE_PDU_T_D_RELEASE = 0x06, 15 | TCMCE_PDU_T_D_SETUP = 0x07, 16 | TCMCE_PDU_T_D_STATUS = 0x08, 17 | TCMCE_PDU_T_D_TX_CEASED = 0x09, 18 | TCMCE_PDU_T_D_TX_CONTINUE = 0x0a, 19 | TCMCE_PDU_T_D_TX_GRANTED = 0x0b, 20 | TCMCE_PDU_T_D_TX_WAIT = 0x0c, 21 | TCMCE_PDU_T_D_TX_INTERRUPT = 0x0d, 22 | TCMCE_PDU_T_D_CALL_RESTORE = 0x0e, 23 | TCMCE_PDU_T_D_SDS_DATA = 0x0f, 24 | TCMCE_PDU_T_D_FACILITY = 0x10, 25 | }; 26 | 27 | enum tetra_cmce_pdu_type_u { 28 | TCMCE_PDU_T_U_ALERT = 0x00, 29 | /* reserved */ 30 | TCMCE_PDU_T_U_CONNECT = 0x02, 31 | /* reserved */ 32 | TCMCE_PDU_T_U_DISCONNECT = 0x04, 33 | TCMCE_PDU_T_U_INFO = 0x05, 34 | TCMCE_PDU_T_U_RELEASE = 0x06, 35 | TCMCE_PDU_T_U_SETUP = 0x07, 36 | TCMCE_PDU_T_U_STATUS = 0x08, 37 | TCMCE_PDU_T_U_TX_CEASED = 0x09, 38 | TCMCE_PDU_T_U_TX_DEMAND = 0x0a, 39 | /*reserved*/ 40 | TCMCE_PDU_T_U_CALL_RESTORE = 0x0e, 41 | TCMCE_PDU_T_U_SDS_DATA = 0x0f, 42 | TCMCE_PDU_T_U_FACILITY = 0x10, 43 | /*reserved*/ 44 | }; 45 | 46 | const char *tetra_get_cmce_pdut_name(uint16_t pdut, int uplink); 47 | 48 | #endif /* TETRA_CMCE_PDU_H */ 49 | -------------------------------------------------------------------------------- /etsi_codec-patches/README: -------------------------------------------------------------------------------- 1 | This directory contains a series of patches against the TETRA codec reference 2 | source code as published by the ETSI at in the file named 3 | en_30039502v010301p0.zip 4 | 5 | You can proceed two ways, a or b: 6 | 7 | a) Using the download_and_patch.sh script 8 | 9 | simply execute ./download_and_patch.sh which will download the 10 | reference codec from the ETSI website and patch it accordingly. 11 | 12 | 13 | b) Manually 14 | 15 | Modified instructions as of 20141118 (differs from the osmocom description, 16 | the ETSI site looks different now): 17 | 18 | In order to obtain the codec, download it directly from 19 | http://www.etsi.org/deliver/etsi_en/300300_300399/30039502/01.03.01_60/en_30039502v010301p0.zip 20 | or use the following procedure: 21 | 22 | * go to http://pda.etsi.org/ 23 | * enter "en 300 395-2" as search term 24 | * execute the search, there should a result called "REN/TETRA-05059 " 25 | * click on the 'Download icon' link (zip-vice symbol) 26 | * you should now have a file called en_30039502v010301p0.zip 27 | 28 | Please also see https://osmocom.org/projects/tetra/wiki/Speech_Codec 29 | 30 | The en_30039502v010301p0.zip file has 361169 bytes, and has the md5sum: 31 | a8115fe68ef8f8cc466f4192572a1e3e 32 | 33 | Due to the use of uppercase file names in the zip file, you should use the "-L" 34 | flag to the unzip program, which ensures all files are completely lower-case. 35 | 36 | Please apply all of the patches found in this directory to the 37 | resulting source code. 38 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g -O3 -Wall `pkg-config --cflags libosmocore 2> /dev/null` -I. 2 | LDLIBS=`pkg-config --libs libosmocore 2> /dev/null` -losmocore 3 | 4 | all: conv_enc_test crc_test tetra-rx float_to_bits tunctl 5 | 6 | debug: CFLAGS := -lasan $(CFLAGS) -fsanitize=address -fno-omit-frame-pointer -g -Og 7 | debug: LDLIBS := -lasan $(LDLIBS) 8 | debug: all 9 | 10 | %.o: %.c 11 | $(CC) $(CFLAGS) -c $^ -o $@ 12 | 13 | libosmo-tetra-phy.a: phy/tetra_burst_sync.o phy/tetra_burst.o 14 | $(AR) r $@ $^ 15 | 16 | libosmo-tetra-mac.a: lower_mac/tetra_conv_enc.o lower_mac/tch_reordering.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_llc_pdu.o tetra_llc.o tetra_mle_pdu.o tetra_mle.o tetra_mm_pdu.o tetra_cmce_pdu.o tetra_sndcp_pdu.o tetra_gsmtap.o tuntap.o 17 | $(AR) r $@ $^ 18 | 19 | libosmo-tetra-crypto.a: crypto/tea1.o crypto/tea2.o crypto/tea3.o crypto/hurdle.o crypto/taa1.o crypto/tetra_crypto.o 20 | $(AR) r $@ $^ 21 | 22 | float_to_bits: float_to_bits.o 23 | 24 | crc_test: crc_test.o tetra_common.o libosmo-tetra-mac.a 25 | 26 | tetra-rx: tetra-rx.o libosmo-tetra-phy.a libosmo-tetra-mac.a libosmo-tetra-crypto.a 27 | 28 | conv_enc_test: conv_enc_test.o testpdu.o libosmo-tetra-phy.a libosmo-tetra-mac.a 29 | 30 | tunctl: tunctl.o 31 | 32 | clean: 33 | @rm -f tunctl float_to_bits crc_test tetra-rx conv_enc_test *.o phy/*.o lower_mac/*.o crypto/*.o *.a 34 | -------------------------------------------------------------------------------- /src/tetra_mle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "tetra_mle_pdu.h" 11 | #include "tetra_mle.h" 12 | #include "tetra_mm_pdu.h" 13 | #include "tetra_cmce_pdu.h" 14 | #include "tetra_sndcp_pdu.h" 15 | #include "tetra_mle_pdu.h" 16 | 17 | 18 | 19 | /* Receive TL-SDU (LLC SDU == MLE PDU) */ 20 | int rx_tl_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len) 21 | { 22 | uint8_t *bits = msg->l3h; 23 | uint8_t mle_pdisc = bits_to_uint(bits, 3); 24 | 25 | printf("TL-SDU(%s): %s ", tetra_get_mle_pdisc_name(mle_pdisc), 26 | osmo_ubit_dump(bits, len)); 27 | switch (mle_pdisc) { 28 | case TMLE_PDISC_MM: 29 | printf("%s\n", tetra_get_mm_pdut_name(bits_to_uint(bits+3, 4), 0)); 30 | break; 31 | case TMLE_PDISC_CMCE: 32 | printf("%s\n", tetra_get_cmce_pdut_name(bits_to_uint(bits+3, 5), 0)); 33 | break; 34 | case TMLE_PDISC_SNDCP: 35 | printf("%s ", tetra_get_sndcp_pdut_name(bits_to_uint(bits+3, 4), 0)); 36 | printf(" NSAPI=%u PCOMP=%u, DCOMP=%u", 37 | bits_to_uint(bits+3+4, 4), 38 | bits_to_uint(bits+3+4+4, 4), 39 | bits_to_uint(bits+3+4+4+4, 4)); 40 | printf(" V%u, IHL=%u", 41 | bits_to_uint(bits+3+4+4+4+4, 4), 42 | 4*bits_to_uint(bits+3+4+4+4+4+4, 4)); 43 | printf(" Proto=%u\n", 44 | bits_to_uint(bits+3+4+4+4+4+4+4+64, 8)); 45 | break; 46 | case TMLE_PDISC_MLE: 47 | printf("%s\n", tetra_get_mle_pdut_name(bits_to_uint(bits+3, 3), 0)); 48 | break; 49 | default: 50 | break; 51 | } 52 | return len; 53 | } 54 | -------------------------------------------------------------------------------- /src/lower_mac/crc_simple.h: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Holger Hans Peter Freyther 2 | * All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | 19 | #ifndef CRC_16 20 | #define CRC_16 21 | 22 | #include 23 | 24 | /** 25 | * Code to generate a CRC16-ITU-T as of the X.25 specification and 26 | * compatible with Linux's implementations. At least the polynom is 27 | * coming from the X.25 spec. 28 | */ 29 | 30 | /** 31 | * This is working of bits packed together. The high bits will be 32 | * accessed first. If you only pass one bit the array needs to contain 33 | * a 0xF0 instead of a 0x01. This interface is compatible with the 34 | * normal CRC interface (besides the length). 35 | */ 36 | uint16_t crc16_itut_bytes(uint16_t crc, 37 | const uint8_t *input, const int number_bits); 38 | 39 | /** 40 | * Each byte contains one bit. Calculate the CRC16-ITU-T for it. 41 | */ 42 | uint16_t crc16_itut_bits(uint16_t crc, 43 | const uint8_t *input, const int number_bits); 44 | 45 | 46 | uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /etsi_codec-patches/makefile-cleanups.patch: -------------------------------------------------------------------------------- 1 | Index: etsi/amr-code/makefile 2 | =================================================================== 3 | --- etsi.orig/amr-code/makefile 2011-05-29 11:38:48.000000000 +0200 4 | +++ etsi/amr-code/makefile 2011-05-29 11:38:50.000000000 +0200 5 | @@ -21,7 +21,7 @@ 6 | 7 | CC=gcc 8 | 9 | -CFLAGS = -g -I. 10 | +CFLAGS = -g -I. -Wall -O3 11 | 12 | SRCS1 = ccoder.c ccod_tet.c init_params.c sub_cc.c \ 13 | tetra_op.c 14 | @@ -33,10 +33,10 @@ 15 | 16 | all: ccoder cdecoder 17 | 18 | -ccoder: 19 | +ccoder: $(SRCS1) 20 | $(CC) $(SRCS1) $(CFLAGS) -o ccoder 21 | 22 | -cdecoder: 23 | +cdecoder: $(SRCS2) 24 | $(CC) $(SRCS2) $(CFLAGS) -o cdecoder 25 | 26 | clean: 27 | Index: etsi/c-code/makefile 28 | =================================================================== 29 | --- etsi.orig/c-code/makefile 2011-05-29 11:38:59.000000000 +0200 30 | +++ etsi/c-code/makefile 2011-05-29 11:39:31.000000000 +0200 31 | @@ -21,9 +21,9 @@ 32 | 33 | # macro definitions 34 | 35 | -CC=acc 36 | +CC=gcc 37 | 38 | -CFLAGS = -I. 39 | +CFLAGS = -I. -Wall -O3 40 | 41 | SRCS1 = scoder.c scod_tet.c sub_sc_d.c \ 42 | sub_dsp.c fbas_tet.c fexp_tet.c \ 43 | @@ -43,18 +43,18 @@ 44 | 45 | all: scoder ccoder sdecoder cdecoder 46 | 47 | -scoder: 48 | +scoder: $(SRCS1) 49 | $(CC) $(SRCS1) $(CFLAGS) -o scoder 50 | 51 | -ccoder: 52 | +ccoder: $(SRCS2) 53 | $(CC) $(SRCS2) $(CFLAGS) -o ccoder 54 | 55 | -sdecoder: 56 | +sdecoder: $(SRCS3) 57 | $(CC) $(SRCS3) $(CFLAGS) -o sdecoder 58 | 59 | -cdecoder: 60 | +cdecoder: $(SRCS4) 61 | $(CC) $(SRCS4) $(CFLAGS) -o cdecoder 62 | 63 | clean: 64 | - rm -f core *.o 65 | + rm -f core *.o cdecoder sdecoder ccoder scoder 66 | 67 | -------------------------------------------------------------------------------- /src/tetra_mm_pdu.c: -------------------------------------------------------------------------------- 1 | /* Implementation of TETRA MM PDU parsing */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "tetra_mm_pdu.h" 25 | 26 | static const struct value_string mm_pdut_d_names[] = { 27 | { TMM_PDU_T_D_OTAR, "D-OTAR" }, 28 | { TMM_PDU_T_D_AUTH, "D-AUTHENTICATION" }, 29 | { TMM_PDU_T_D_CK_CHG_DEM, "D-CK CHANGE DEMAND" }, 30 | { TMM_PDU_T_D_DISABLE, "D-DISABLE" }, 31 | { TMM_PDU_T_D_ENABLE, "D-ENABLE" }, 32 | { TMM_PDU_T_D_LOC_UPD_ACC, "D-LOCATION UPDATE ACCEPT" }, 33 | { TMM_PDU_T_D_LOC_UPD_CMD, "D-LOCATION UPDATE COMMAND" }, 34 | { TMM_PDU_T_D_LOC_UPD_REJ, "D-LOCATION UPDATE REJECT" }, 35 | { TMM_PDU_T_D_LOC_UPD_PROC, "D-LOCATION UPDATE PROCEEDING" }, 36 | { TMM_PDU_T_D_ATT_DET_GRP, "D-ATTACH/DETACH GROUP ID" }, 37 | { TMM_PDU_T_D_ATT_DET_GRP_ACK, "D-ATTACH/DETACH GROUP ID ACK" }, 38 | { TMM_PDU_T_D_MM_STATUS, "D-MM STATUS" }, 39 | { TMM_PDU_T_D_MM_PDU_NOTSUPP, "MM PDU/FUNCTION NOT SUPPORTED" }, 40 | { 0, NULL } 41 | }; 42 | const char *tetra_get_mm_pdut_name(uint8_t pdut, int uplink) 43 | { 44 | /* FIXME: uplink */ 45 | return get_value_string(mm_pdut_d_names, pdut); 46 | } 47 | -------------------------------------------------------------------------------- /src/receiver1udp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # receiver1udp (c) 2023 Jacek Lipkowski SQ5BPF 4 | # 5 | # This is an example how to get I/Q data via udp packets, 6 | # feed them to a qpsk demodulator, and feed the result to tetra-rx 7 | # 8 | # Originally this was developed to work with the osmo-tetra-sq5bpf tree 9 | # but will work with original osmo-tetra too 10 | # 11 | # 12 | # To use with an rtl-sdr dongle: 13 | # 14 | # - run the demod/telive_1ch_simple_gr310_udp.py (you can also use the telive_1ch_simple_gr310_udp.grc in gnuradio-companion) 15 | # 16 | # - run ./receiver1udp 17 | # 18 | # - enter the frequency in the receiver window, set the ppm (frequency correction) and the gain 19 | # 20 | 21 | 22 | if [ "$1" ]; then 23 | RXID=$1 24 | else 25 | RXID=1 26 | fi 27 | 28 | 29 | # you can also set GR_VERSION yourself outside of this script --sq5bpf 30 | [ "$GR_VERSION" ] || GR_VERSION=`gnuradio-config-info -v|cut -d . -f 1-2|tr -d v` 31 | case $GR_VERSION in 32 | 3.6) 33 | GR_DIR=gnuradio-3.6 34 | PYT=2 35 | ;; 36 | 3.7) 37 | GR_DIR=gnuradio-3.7 38 | PYT=2 39 | ;; 40 | 3.10) 41 | GR_DIR=gnuradio-3.10 42 | PYT=3 43 | ;; 44 | *) 45 | echo "Unsupported gnuradio version $GR_VERSION" 46 | exit 1 47 | ;; 48 | esac 49 | 50 | UDP_PORT=42001 51 | 52 | 53 | ulimit -c unlimited 54 | 55 | #tetra-rx args: -a turns on pseudo-afc , -i uses an internal float_t_bits 56 | # -r turns on fragment reassembly, -s tries to dump unknown SDS protocols as text 57 | # 58 | #if you have problems with the receiver, then try to remove -a 59 | 60 | case "$PYT" in 61 | 2) 62 | socat -b 4096 UDP-RECV:${UDP_PORT} STDOUT | demod/simdemod2.py -o /dev/stdout -i /dev/stdin | ./float_to_bits -a /dev/stdin /dev/stdout | ./tetra-rx /dev/stdin 63 | ;; 64 | 3) 65 | socat -b 4096 UDP-RECV:${UDP_PORT} STDOUT | demod/simdemod3.py | ./tetra-rx /dev/stdin 66 | ;; 67 | *) 68 | echo "Dont know how to handle pythin version [$PYT]" 69 | exit 1 70 | esac 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/tetra_sndcp_pdu.c: -------------------------------------------------------------------------------- 1 | /* Implementation of TETRA SNDCP PDU parsing */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "tetra_sndcp_pdu.h" 25 | 26 | static const struct value_string sndcp_pdut_names[] = { 27 | { SNDCP_PDU_T_ACT_PDP_ACCEPT, "SN-ACTIVATE PDP ACCEPT" }, 28 | { SNDCP_PDU_T_DEACT_PDP_ACC, "SN-DEACTIVATE PDP ACCEPT" }, 29 | { SNDCP_PDU_T_DEACT_PDP_DEMAND, "SN-DEACTIVATE PDP DEMAND" }, 30 | { SNDCP_PDU_T_ACT_PDP_REJECT, "SN-ACTIVATE PDP REJECT" }, 31 | { SNDCP_PDU_T_UNITDATA, "SN-UNITDATA" }, 32 | { SNDCP_PDU_T_DATA, "SN-DATA" }, 33 | { SNDCP_PDU_T_DATA_TX_REQ, "SN-DATA TX REQUEST" }, 34 | { SNDCP_PDU_T_DATA_TX_RESP, "SN-DATA TX RESPONSE" }, 35 | { SNDCP_PDU_T_END_OF_DATA, "SN-END OF DATA" }, 36 | { SNDCP_PDU_T_RECONNECT, "SN-RECONNECT" }, 37 | { SNDCP_PDU_T_PAGE_REQUEST, "SN-PAGE REQUEST" }, 38 | { SNDCP_PDU_T_NOT_SUPPORTED, "SN-NOT SUPPORTED" }, 39 | { SNDCP_PDU_T_DATA_PRIORITY, "SN-DATA PRIORITY" }, 40 | { SNDCP_PDU_T_MODIFY, "SN-MODIFY" }, 41 | { 0, NULL } 42 | }; 43 | 44 | const char *tetra_get_sndcp_pdut_name(uint8_t pdut, int uplink) 45 | { 46 | /* FIXME: uplink */ 47 | return get_value_string(sndcp_pdut_names, pdut); 48 | } 49 | -------------------------------------------------------------------------------- /src/tetra_mle_pdu.c: -------------------------------------------------------------------------------- 1 | /* Implementation of TETRA MLE PDU parsing */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "tetra_mle_pdu.h" 25 | 26 | static const struct value_string mle_pdisc_names[] = { 27 | { TMLE_PDISC_MM, "MM" }, 28 | { TMLE_PDISC_CMCE, "CMCE" }, 29 | { TMLE_PDISC_SNDCP, "SNDCP" }, 30 | { TMLE_PDISC_MLE, "MLE" }, 31 | { TMLE_PDISC_MGMT, "MGMT" }, 32 | { TMLE_PDISC_TEST, "TEST" }, 33 | { 0, NULL } 34 | }; 35 | const char *tetra_get_mle_pdisc_name(uint8_t pdisc) 36 | { 37 | return get_value_string(mle_pdisc_names, pdisc); 38 | } 39 | 40 | static const struct value_string mle_pdut_d_names[] = { 41 | { TMLE_PDUT_D_NEW_CELL, "D-NEW CELL" }, 42 | { TMLE_PDUT_D_PREPARE_FAIL, "D-PREPARE FAIL" }, 43 | { TMLE_PDUT_D_NWRK_BROADCAST, "D-NWRK BROADCAST" }, 44 | { TMLE_PDUT_D_NWRK_BROADCAST_EXT, "D-NWRK BROADCAST EXT" }, 45 | { TMLE_PDUT_D_RESTORE_ACK, "D-RESTORE ACK" }, 46 | { TMLE_PDUT_D_RESTORE_FAIL, "D-RESTORE FAIL" }, 47 | { TMLE_PDUT_D_CHANNEL_RESPONSE, "D-CHANNEL RESPONSE" }, 48 | { 0, NULL } 49 | }; 50 | const char *tetra_get_mle_pdut_name(unsigned int pdut, int uplink) 51 | { 52 | /* FIXME: uplink */ 53 | return get_value_string(mle_pdut_d_names, pdut); 54 | } 55 | -------------------------------------------------------------------------------- /src/tetra_common.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_COMMON_H 2 | #define TETRA_COMMON_H 3 | 4 | #include 5 | #include "tetra_mac_pdu.h" 6 | #include 7 | #include 8 | 9 | #include "tetra_common.h" 10 | #include "tetra_mac_pdu.h" 11 | 12 | #ifdef DEBUG 13 | #define DEBUGP(x, args...) printf(x, ## args) 14 | #else 15 | #define DEBUGP(x, args...) do { } while (0) 16 | #endif 17 | 18 | #define TETRA_SYM_PER_TS 255 19 | #define TETRA_BITS_PER_TS (TETRA_SYM_PER_TS*2) 20 | 21 | /* Chapter 22.2.x */ 22 | enum tetra_log_chan { 23 | TETRA_LC_UNKNOWN, 24 | /* TMA SAP */ 25 | TETRA_LC_SCH_F, 26 | TETRA_LC_SCH_HD, 27 | TETRA_LC_SCH_HU, 28 | TETRA_LC_STCH, 29 | TETRA_LC_SCH_P8_F, 30 | TETRA_LC_SCH_P8_HD, 31 | TETRA_LC_SCH_P8_HU, 32 | 33 | TETRA_LC_AACH, 34 | TETRA_LC_TCH, 35 | TETRA_LC_BSCH, 36 | TETRA_LC_BNCH, 37 | 38 | /* FIXME: QAM */ 39 | }; 40 | 41 | uint32_t bits_to_uint(const uint8_t *bits, unsigned int len); 42 | 43 | #include "tetra_tdma.h" 44 | struct tetra_phy_state { 45 | struct tetra_tdma_time time; 46 | }; 47 | extern struct tetra_phy_state t_phy_state; 48 | 49 | struct tetra_mac_state { 50 | struct llist_head voice_channels; 51 | struct { 52 | int is_traffic; 53 | bool blk1_stolen; 54 | bool blk2_stolen; 55 | } cur_burst; 56 | struct tetra_si_decoded last_sid; 57 | 58 | struct tetra_crypto_state *tcs; /* contains all state relevant to encryption */ 59 | 60 | char *dumpdir; /* Where to save traffic channel dump */ 61 | int ssi; /* SSI */ 62 | int tsn; /* Timeslon number */ 63 | int usage_marker; /* Usage marker (if addressed)*/ 64 | int addr_type; 65 | }; 66 | 67 | void tetra_mac_state_init(struct tetra_mac_state *tms); 68 | 69 | #define TETRA_CRC_OK 0x1d0f 70 | 71 | uint32_t tetra_dl_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset); 72 | uint32_t tetra_ul_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset, 73 | uint8_t duplex, uint8_t reverse); 74 | 75 | const char *tetra_get_lchan_name(enum tetra_log_chan lchan); 76 | const char *tetra_get_sap_name(uint8_t sap); 77 | #endif 78 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi_tch.c: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Sylvain Munaut 2 | * 3 | * All Rights Reserved 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | 27 | 28 | /* 29 | * G1 = 1 + D + D2 + D3 + D4 30 | * G2 = 1 + D + D3 + D4 31 | * G3 = 1 + D2 + D4 32 | */ 33 | 34 | static const uint8_t conv_tch_next_output[][2] = { 35 | { 0, 7 }, { 6, 1 }, { 5, 2 }, { 3, 4 }, 36 | { 6, 1 }, { 0, 7 }, { 3, 4 }, { 5, 2 }, 37 | { 7, 0 }, { 1, 6 }, { 2, 5 }, { 4, 3 }, 38 | { 1, 6 }, { 7, 0 }, { 4, 3 }, { 2, 5 }, 39 | }; 40 | 41 | static const uint8_t conv_tch_next_state[][2] = { 42 | { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, 43 | { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, 44 | { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, 45 | { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, 46 | }; 47 | 48 | static const struct osmo_conv_code conv_tch = { 49 | .N = 4, 50 | .K = 5, 51 | .next_output = conv_tch_next_output, 52 | .next_state = conv_tch_next_state, 53 | }; 54 | 55 | 56 | int conv_tch_decode(int8_t *input, uint8_t *output, int n) 57 | { 58 | struct osmo_conv_code code; 59 | 60 | memcpy(&code, &conv_tch, sizeof(struct osmo_conv_code)); 61 | code.len = n; 62 | 63 | return osmo_conv_decode(&code, input, output); 64 | } 65 | -------------------------------------------------------------------------------- /src/lower_mac/viterbi_cch.c: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Sylvain Munaut 2 | * 3 | * All Rights Reserved 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | 27 | 28 | /* 29 | * G1 = 1 + D + D4 30 | * G2 = 1 + D2 + D3 + D4 31 | * G3 = 1 + D + D2 + D4 32 | * G4 = 1 + D + D3 + D4 33 | */ 34 | 35 | static const uint8_t conv_cch_next_output[][2] = { 36 | { 0, 15 }, { 11, 4 }, { 6, 9 }, { 13, 2 }, 37 | { 5, 10 }, { 14, 1 }, { 3, 12 }, { 8, 7 }, 38 | { 15, 0 }, { 4, 11 }, { 9, 6 }, { 2, 13 }, 39 | { 10, 5 }, { 1, 14 }, { 12, 3 }, { 7, 8 }, 40 | }; 41 | 42 | static const uint8_t conv_cch_next_state[][2] = { 43 | { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, 44 | { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, 45 | { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, 46 | { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, 47 | }; 48 | 49 | 50 | static const struct osmo_conv_code conv_cch = { 51 | .N = 4, 52 | .K = 5, 53 | .next_output = conv_cch_next_output, 54 | .next_state = conv_cch_next_state, 55 | }; 56 | 57 | 58 | int conv_cch_decode(int8_t *input, uint8_t *output, int n) 59 | { 60 | struct osmo_conv_code code; 61 | 62 | memcpy(&code, &conv_cch, sizeof(struct osmo_conv_code)); 63 | code.len = n; 64 | 65 | return osmo_conv_decode(&code, input, output); 66 | } 67 | -------------------------------------------------------------------------------- /src/tetra-rx-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 1 ]; then 4 | echo "$0" 5 | echo "Runs tetra-rx on bit files provided as args, prints the number of correct frames" 6 | echo " and the time it took and compares it to previous runs." 7 | echo "" 8 | echo "Extra options:" 9 | echo " -n (default: git head id) (no spaces and \"-\" please)" 10 | echo " -o overwrite previous measurement" 11 | echo " -t parameters for tetra-rx" 12 | echo "" 13 | echo "Examples:" 14 | echo "$0 path/to/file.bits" 15 | echo "$0 -n \"something changed\" path/to/file.bits path/to/file2.bits" 16 | exit 1 17 | fi 18 | 19 | if ! command -v bc > /dev/null; then 20 | echo "Please install \"bc\"" 21 | fi 22 | 23 | TESTS_DIR="tests_data/" 24 | mkdir -p "$TESTS_DIR" 25 | 26 | n=`git rev-parse HEAD` 27 | o=0 28 | t=" " 29 | while getopts ":n:ot:" opt; do 30 | case $opt in 31 | n) 32 | n="$OPTARG" 33 | ;; 34 | o) 35 | o=1 36 | ;; 37 | t) 38 | t="$OPTARG" 39 | ;; 40 | \?) 41 | echo "Unknown option $OPTARG" >&2 42 | exit 1 43 | ;; 44 | :) 45 | echo "-$OPTARG requires argument" >&2 46 | exit 1 47 | ;; 48 | esac 49 | done 50 | 51 | shift $(( $OPTIND - 1 )) 52 | 53 | tmpdir=`mktemp -d /tmp/tetraXXX` 54 | 55 | for f in $@; do 56 | corrects=`"time" -o "$tmpdir/time" ./tetra-rx "$f" $t 2>/dev/null | grep -E "^CRC COMP: 0x.+ OK" | wc -l` 57 | tt=`grep user "$tmpdir/time" | head -n 1 | cut -d u -f 1` 58 | echo "$f: $corrects frames, $tt s" 59 | hash=`sha256sum "$f" | cut -c 1-20` 60 | fnb="$TESTS_DIR/rx-$hash-" 61 | for meas in "$fnb"*; do 62 | if ! [ -s "$meas" ]; then 63 | continue 64 | fi 65 | tag=`echo "$meas" | rev | cut -d - -f 1 | rev` 66 | if [ "$tag" = "$n" ]; then 67 | continue 68 | fi 69 | pf=`cat "$meas" | cut -d " " -f 1` 70 | pt=`cat "$meas" | cut -d " " -f 2` 71 | deltaf=`echo "scale=7; $corrects/$pf" | bc -l` 72 | deltat=`echo "scale=3; $tt/$pt" | bc -l` 73 | echo "... ${deltaf}x frames than $tag" 74 | echo "... ${deltat}x time than $tag" 75 | done 76 | fn="$fnb$n" 77 | if [ "$o" -eq 1 -o ! -s "$fn" ]; then 78 | echo "$corrects $tt" > "$fn" 79 | fi 80 | done 81 | 82 | rm -r "$tmpdir" 83 | -------------------------------------------------------------------------------- /src/tetra_gsmtap.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "tetra_common.h" 15 | #include "tetra_tdma.h" 16 | 17 | static struct gsmtap_inst *g_gti = NULL; 18 | 19 | static const uint8_t lchan2gsmtap[] = { 20 | [TETRA_LC_SCH_F] = GSMTAP_TETRA_SCH_F, 21 | [TETRA_LC_SCH_HD] = GSMTAP_TETRA_SCH_HD, 22 | [TETRA_LC_SCH_HU] = GSMTAP_TETRA_SCH_HU, 23 | [TETRA_LC_STCH] = GSMTAP_TETRA_STCH, 24 | [TETRA_LC_AACH] = GSMTAP_TETRA_AACH, 25 | [TETRA_LC_TCH] = GSMTAP_TETRA_TCH_F, 26 | [TETRA_LC_BSCH] = GSMTAP_TETRA_BSCH, 27 | [TETRA_LC_BNCH] = GSMTAP_TETRA_BNCH, 28 | }; 29 | 30 | 31 | struct msgb *tetra_gsmtap_makemsg(struct tetra_tdma_time *tm, enum tetra_log_chan lchan, uint8_t ts, uint8_t ss, 32 | int8_t signal_dbm, uint8_t snr, const ubit_t *bitdata, unsigned int bitlen, 33 | struct tetra_mac_state *tms) 34 | { 35 | struct msgb *msg; 36 | struct gsmtap_hdr *gh; 37 | uint32_t fn = tetra_tdma_time2fn(tm); 38 | unsigned int packed_len = osmo_pbit_bytesize(bitlen); 39 | uint8_t *dst; 40 | 41 | msg = msgb_alloc(sizeof(*gh) + packed_len, "tetra_gsmtap_tx"); 42 | if (!msg) 43 | return NULL; 44 | 45 | gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); 46 | gh->version = GSMTAP_VERSION; 47 | gh->hdr_len = sizeof(*gh)/4; 48 | gh->type = GSMTAP_TYPE_TETRA_I1; 49 | gh->timeslot = ts; 50 | tms->tsn = ts; 51 | gh->sub_slot = ss; 52 | gh->snr_db = snr; 53 | gh->signal_dbm = signal_dbm; 54 | gh->frame_number = htonl(fn); 55 | gh->sub_type = lchan2gsmtap[lchan]; 56 | gh->antenna_nr = 0; 57 | 58 | /* convert from 1bit-per-byte to compressed bits!!! */ 59 | dst = msgb_put(msg, packed_len); 60 | osmo_ubit2pbit(dst, bitdata, bitlen); 61 | 62 | return msg; 63 | } 64 | 65 | int tetra_gsmtap_sendmsg(struct msgb *msg) 66 | { 67 | if (g_gti) { 68 | return gsmtap_sendmsg(g_gti, msg); 69 | } else { 70 | return 0; 71 | } 72 | } 73 | 74 | int tetra_gsmtap_init(const char *host, uint16_t port) 75 | { 76 | g_gti = gsmtap_source_init(host, port, 0); 77 | if (!g_gti) 78 | return -EINVAL; 79 | gsmtap_source_add_sink(g_gti); 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /src/crc_test.c: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Holger Hans Peter Freyther 2 | * All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | #include 19 | 20 | #include 21 | 22 | #include "tetra_common.h" 23 | #include 24 | 25 | int main(int argc, char **argv) 26 | { 27 | uint8_t input1[] = { 0x01 }; 28 | uint16_t crc; 29 | 30 | crc = crc16_itut_bytes(0x0, input1, 8); 31 | printf("The CRC is now: %u/0x%x\n", crc, crc); 32 | 33 | crc = crc16_itut_bits(0x0, input1, 1); 34 | printf("The CRC is now: %d/0x%x\n", crc, crc); 35 | 36 | crc = crc16_itut_bytes(0xFFFF, input1, 8); 37 | printf("The CRC is now: %u/0x%x\n", crc, crc); 38 | 39 | crc = crc16_itut_bits(0xFFFF, input1, 1); 40 | printf("The CRC is now: %d/0x%x\n", crc, crc); 41 | 42 | /* actual CRC-CCIT usage */ 43 | uint8_t input2[] = { 44 | 0, 0, 0, 1, 0, 0, 0, 0, 45 | 1, 0, 1, 1, 0, 0, 0, 0, 46 | 1, 0, 1, 1, 1, 1, 1, 0, 47 | 0, 0, 0, 0, 0, 0, 0, 0, 48 | 1, 0, 0, 0, 0, 0, 1, 1, 49 | 0, 0, 0, 0, 0, 1, 1, 1, 50 | 1, 1, 0, 1, 0, 0, 1, 1, 51 | 0, 0, 1, 1, 52 | 53 | /* checksum */ 54 | 1, 1, 0, 1, 1, 1, 1, 0, 55 | 1, 1, 1, 1, 0, 0, 0, 1, 56 | /* tail bits */ 57 | 0, 0, 0, 0 }; 58 | 59 | 60 | crc = ~crc16_itut_bits(0xffff, input2, 60); 61 | printf("The CRC is now: %d/0x%x\n", crc, crc); 62 | 63 | /* swap the bytes */ 64 | crc = (crc << 8) | (crc >> 8); 65 | osmo_pbit2ubit(&input2[60], (uint8_t *)&crc, 16); 66 | 67 | crc = crc16_itut_bits(0xFFFF, input2, 60 + 16); 68 | printf("The CRC is now: %d/0x%x\n", crc, crc); 69 | if (crc == 0x1d0f) 70 | printf("Decoded successfully.\n"); 71 | else 72 | printf("Failed to decode.\n"); 73 | 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_interleave.c: -------------------------------------------------------------------------------- 1 | /* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ 2 | 3 | /* This converts from type type-3 bits into type-4 bits (and vice versa) */ 4 | 5 | /* (C) 2011 by Harald Welte 6 | * All Rights Reserved 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | 35 | /* Section 8.2.4.1 Block interleaving for phase modulation */ 36 | static uint32_t block_interl_func(uint32_t K, uint32_t a, uint32_t i) 37 | { 38 | return (1 + ( (a * i) % K)); 39 | } 40 | 41 | void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out) 42 | { 43 | int i; 44 | for (i = 1; i <= K; i++) { 45 | uint32_t k = block_interl_func(K, a, i); 46 | DEBUGP("interl: i=%u, k=%u\n", i, k); 47 | out[k-1] = in[i-1]; 48 | } 49 | } 50 | 51 | void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out) 52 | { 53 | int i; 54 | for (i = 1; i <= K; i++) { 55 | uint32_t k = block_interl_func(K, a, i); 56 | DEBUGP("deinterl: i=%u, k=%u\n", i, k); 57 | out[i-1] = in[k-1]; 58 | } 59 | } 60 | 61 | /* EN 300 395-2 Section 5.5.3 Matrix interleaving (voice */ 62 | void matrix_interleave(uint32_t lines, uint32_t columns, 63 | const uint8_t *in, uint8_t *out) 64 | { 65 | int i, j; 66 | 67 | for (i = 0; i < columns; i++) { 68 | for (j = 0; j < lines; j++) 69 | out[i*lines + columns] = in[j*columns + lines]; 70 | } 71 | } 72 | 73 | void matrix_deinterleave(uint32_t lines, uint32_t columns, 74 | const uint8_t *in, uint8_t *out) 75 | { 76 | int i, j; 77 | 78 | for (i = 0; i < columns; i++) { 79 | for (j = 0; j < lines; j++) 80 | out[j*columns + lines] = in[i*lines + columns]; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/tetra_tdma.c: -------------------------------------------------------------------------------- 1 | /* TETRA TDMA time functions, see Section 7.3 of EN 300 392-2 */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | 25 | #include "tetra_tdma.h" 26 | 27 | static void normalize_mn(struct tetra_tdma_time *tm) 28 | { 29 | if (tm->mn > 60) 30 | tm->mn = (tm->mn%60); 31 | } 32 | 33 | static void normalize_fn(struct tetra_tdma_time *tm) 34 | { 35 | uint32_t mn_delta; 36 | 37 | if (tm->fn > 18) { 38 | mn_delta = tm->fn/18; 39 | tm->fn = (tm->fn%18); 40 | tm->mn += mn_delta; 41 | } 42 | normalize_mn(tm); 43 | } 44 | 45 | static void normalize_tn(struct tetra_tdma_time *tm) 46 | { 47 | uint32_t fn_delta; 48 | 49 | if (tm->tn > 4) { 50 | fn_delta = tm->tn/4; 51 | tm->tn = (tm->tn%4); 52 | tm->fn += fn_delta; 53 | } 54 | normalize_fn(tm); 55 | } 56 | 57 | static void normalize_sn(struct tetra_tdma_time *tm) 58 | { 59 | uint32_t tn_delta; 60 | 61 | if (tm->sn > 255) { 62 | tn_delta = (tm->sn/255); 63 | tm->sn = (tm->sn % 255) + 1; 64 | tm->tn += tn_delta; 65 | } 66 | normalize_tn(tm); 67 | } 68 | 69 | void tetra_tdma_time_add_sym(struct tetra_tdma_time *tm, uint32_t sym_count) 70 | { 71 | tm->sn += sym_count; 72 | normalize_sn(tm); 73 | } 74 | 75 | void tetra_tdma_time_add_tn(struct tetra_tdma_time *tm, uint32_t tn_count) 76 | { 77 | tm->tn += tn_count; 78 | normalize_tn(tm); 79 | } 80 | 81 | void tetra_tdma_time_add_fn(struct tetra_tdma_time *tm, uint32_t fn_count) 82 | { 83 | tm->fn += fn_count; 84 | normalize_fn(tm); 85 | } 86 | 87 | char *tetra_tdma_time_dump(const struct tetra_tdma_time *tm) 88 | { 89 | static char buf[256]; 90 | 91 | snprintf(buf, sizeof(buf), "%02u/%02u/%u/%03u", tm->mn, tm->fn, tm->tn, tm->sn); 92 | 93 | return buf; 94 | } 95 | 96 | uint32_t tetra_tdma_time2fn(struct tetra_tdma_time *tm) 97 | { 98 | return (((tm->hn * 60) + tm->mn) * 18) + tm->fn; 99 | } 100 | -------------------------------------------------------------------------------- /src/demod/simdemod2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # osmosdr-tetra_demod_fft.py Copyright 2012 Dimitri Stolnikov 4 | 5 | # simdemod.py (c) 2014 Jacek Lipkowski 6 | # this is a modified osmosdr-tetra_demod_fft.py with all of the gui stuff 7 | # removed. is is intended to be fed from a receiver program via a pipe 8 | # 9 | # mkfifo /tmp/fifo1 10 | # demod/python/simdemod.py -o /dev/stdout -i /tmp/fifo1 | ./float_to_bits /dev/stdin /dev/stdout | ./tetra-rx /dev/stdin 11 | # 12 | 13 | import sys 14 | import math 15 | from gnuradio import gr, gru, eng_notation, blocks 16 | from gnuradio.eng_option import eng_option 17 | from optparse import OptionParser 18 | import osmosdr 19 | 20 | try: 21 | import cqpsk 22 | except: 23 | from tetra_demod import cqpsk 24 | 25 | # applies frequency translation, resampling and demodulation 26 | 27 | class top_block(gr.top_block): 28 | def __init__(self): 29 | gr.top_block.__init__(self, "Top Block") 30 | 31 | options = get_options() 32 | self.input_file=options.input_file 33 | self.gr_file_source_0 = blocks.file_source(gr.sizeof_gr_complex*1, self.input_file, True) 34 | 35 | symbol_rate = 18000 36 | sps = 2 # output rate will be 36,000 37 | out_sample_rate = symbol_rate * sps 38 | 39 | options.low_pass = options.low_pass / 2.0 40 | 41 | self.demod = cqpsk.cqpsk_demod( 42 | samples_per_symbol = sps, 43 | excess_bw=0.35, 44 | costas_alpha=0.03, 45 | gain_mu=0.05, 46 | mu=0.05, 47 | omega_relative_limit=0.05, 48 | log=options.log, 49 | verbose=options.verbose) 50 | 51 | self.output = gr.file_sink(gr.sizeof_float, options.output_file) 52 | 53 | self.connect(self.gr_file_source_0, self.demod, self.output) 54 | 55 | 56 | 57 | 58 | 59 | 60 | def get_options(): 61 | parser = OptionParser(option_class=eng_option) 62 | 63 | # demodulator related settings 64 | parser.add_option("-l", "--log", action="store_true", default=False, help="dump debug .dat files") 65 | parser.add_option("-i", "--input-file", type="string", default="in.float", help="specify the bit input file") 66 | parser.add_option("-o", "--output-file", type="string", default="out.float", help="specify the bit output file") 67 | parser.add_option("-v", "--verbose", action="store_true", default=False, help="dump demodulation data") 68 | parser.add_option("-L", "--low-pass", type="eng_float", default=25e3, help="low pass cut-off", metavar="Hz") 69 | 70 | (options, args) = parser.parse_args() 71 | if len(args) != 0: 72 | parser.print_help() 73 | raise SystemExit, 1 74 | 75 | return (options) 76 | 77 | if __name__ == '__main__': 78 | tb = top_block() 79 | tb.run() 80 | #tb.run(True) 81 | -------------------------------------------------------------------------------- /src/tetra_cmce_pdu.c: -------------------------------------------------------------------------------- 1 | /* Implementation of TETRA CMCE PDU parsing */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "tetra_cmce_pdu.h" 25 | 26 | static const struct value_string cmce_pdut_d_names[] = { 27 | { TCMCE_PDU_T_D_ALERT, "D-ALERT" }, 28 | { TCMCE_PDU_T_D_CALL_PROCEEDING, "D-CALL PROCEEDING" }, 29 | { TCMCE_PDU_T_D_CONNECT, "D-CONNECT" }, 30 | { TCMCE_PDU_T_D_CONNECT_ACK, "D-CONNECT ACK" }, 31 | { TCMCE_PDU_T_D_DISCONNECT, "D-DISCONNECT" }, 32 | { TCMCE_PDU_T_D_INFO, "D-INFO" }, 33 | { TCMCE_PDU_T_D_RELEASE, "D-RELEASE" }, 34 | { TCMCE_PDU_T_D_SETUP, "D-SETUP" }, 35 | { TCMCE_PDU_T_D_STATUS, "D-STATUS" }, 36 | { TCMCE_PDU_T_D_TX_CEASED, "D-TX CEASED" }, 37 | { TCMCE_PDU_T_D_TX_CONTINUE, "D-TX CONTINUE" }, 38 | { TCMCE_PDU_T_D_TX_GRANTED, "D-TX GRANTED" }, 39 | { TCMCE_PDU_T_D_TX_WAIT, "D-TX WAIT" }, 40 | { TCMCE_PDU_T_D_TX_INTERRUPT, "D-TX INTERRUPT" }, 41 | { TCMCE_PDU_T_D_CALL_RESTORE, "D-TX CALL RESTORE" }, 42 | { TCMCE_PDU_T_D_SDS_DATA, "D-SDS DATA" }, 43 | { TCMCE_PDU_T_D_FACILITY, "D-FACILITY" }, 44 | { 0, NULL } 45 | }; 46 | 47 | static const struct value_string cmce_pdut_u_names[] = { 48 | { TCMCE_PDU_T_U_ALERT, "U-ALERT" }, 49 | { TCMCE_PDU_T_U_CONNECT, "U-CONNECT" }, 50 | { TCMCE_PDU_T_U_DISCONNECT, "U-DISCONNECT" }, 51 | { TCMCE_PDU_T_U_INFO, "U-INFO" }, 52 | { TCMCE_PDU_T_U_RELEASE, "U-RELEASE" }, 53 | { TCMCE_PDU_T_U_SETUP, "U-SETUP" }, 54 | { TCMCE_PDU_T_U_STATUS, "U-STATUS" }, 55 | { TCMCE_PDU_T_U_TX_CEASED, "U-TX CEASED" }, 56 | { TCMCE_PDU_T_U_TX_DEMAND, "U-TX DEMAND" }, 57 | { TCMCE_PDU_T_U_CALL_RESTORE, "U-TX CALL RESTORE" }, 58 | { TCMCE_PDU_T_U_SDS_DATA, "U-SDS DATA" }, 59 | { TCMCE_PDU_T_U_FACILITY, "U-FACILITY" }, 60 | { 0, NULL } 61 | }; 62 | 63 | const char *tetra_get_cmce_pdut_name(uint16_t pdut, int uplink) 64 | { 65 | if (uplink == 0) 66 | return get_value_string(cmce_pdut_d_names, pdut); 67 | else 68 | return get_value_string(cmce_pdut_u_names, pdut); 69 | } 70 | -------------------------------------------------------------------------------- /src/tetra-rx.c: -------------------------------------------------------------------------------- 1 | /* Test program for tetra burst synchronizer */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include "tetra_common.h" 33 | #include "crypto/tetra_crypto.h" 34 | #include 35 | #include 36 | #include "tetra_gsmtap.h" 37 | 38 | void *tetra_tall_ctx; 39 | 40 | int main(int argc, char **argv) 41 | { 42 | int fd; 43 | int opt; 44 | struct tetra_rx_state *trs; 45 | struct tetra_mac_state *tms; 46 | 47 | /* Initialize tetra mac state and crypto state */ 48 | tms = talloc_zero(tetra_tall_ctx, struct tetra_mac_state); 49 | tetra_mac_state_init(tms); 50 | tms->tcs = talloc_zero(NULL, struct tetra_crypto_state); 51 | tetra_crypto_state_init(tms->tcs); 52 | 53 | trs = talloc_zero(tetra_tall_ctx, struct tetra_rx_state); 54 | trs->burst_cb_priv = tms; 55 | 56 | while ((opt = getopt(argc, argv, "d:k:")) != -1) { 57 | switch (opt) { 58 | case 'd': 59 | tms->dumpdir = strdup(optarg); 60 | break; 61 | case 'k': 62 | load_keystore(optarg); 63 | break; 64 | default: 65 | fprintf(stderr, "Unknown option %c\n", opt); 66 | } 67 | } 68 | 69 | if (argc <= optind) { 70 | fprintf(stderr, "Usage: %s [-d DUMPDIR] \n", argv[0]); 71 | exit(1); 72 | } 73 | 74 | fd = open(argv[optind], O_RDONLY); 75 | if (fd < 0) { 76 | perror("open"); 77 | exit(2); 78 | } 79 | 80 | tetra_gsmtap_init("localhost", 0); 81 | 82 | while (1) { 83 | uint8_t buf[64]; 84 | int len; 85 | 86 | len = read(fd, buf, sizeof(buf)); 87 | if (len < 0) { 88 | perror("read"); 89 | exit(1); 90 | } else if (len == 0) { 91 | printf("EOF"); 92 | break; 93 | } 94 | tetra_burst_sync_in(trs, buf, len); 95 | } 96 | 97 | free(tms->dumpdir); 98 | talloc_free(trs); 99 | talloc_free(tms->tcs); 100 | talloc_free(tms); 101 | 102 | exit(0); 103 | } 104 | -------------------------------------------------------------------------------- /src/lower_mac/crc_simple.c: -------------------------------------------------------------------------------- 1 | /* CRC16 (most likely the ITU variant) working on plain bits as a trial 2 | * to find out if simple padding to the right is going to be enough 3 | */ 4 | /* (C) 2011 by Holger Hans Peter Freyther 5 | * All Rights Reserved 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | /** 26 | * X.25 rec 2.2.7.4 Frame Check Sequence. This should be 27 | * CRC ITU-T from the kernel or such. 28 | */ 29 | #define GEN_POLY 0x1021 30 | 31 | uint16_t get_nth_bit(const uint8_t *input, int _bit) 32 | { 33 | uint16_t val; 34 | int byte = _bit / 8; 35 | int bit = 7 - _bit % 8; 36 | 37 | val = (input[byte] & (1 << bit)) >> bit; 38 | return val; 39 | } 40 | 41 | /** 42 | * This is mostly from http://en.wikipedia.org/wiki/Computation_of_CRC 43 | * Code fragment 2. Due some stupidity it took longer to implement than 44 | * it should have taken. 45 | */ 46 | uint16_t crc16_itut_bytes(uint16_t crc, const uint8_t *input, int number_bits) 47 | { 48 | int i; 49 | 50 | for (i = 0; i < number_bits; ++i) { 51 | uint16_t bit = get_nth_bit(input, i); 52 | 53 | crc ^= bit << 15; 54 | if ((crc & 0x8000)) { 55 | crc <<= 1; 56 | crc ^= GEN_POLY; 57 | } else { 58 | crc <<= 1; 59 | } 60 | } 61 | 62 | return crc; 63 | } 64 | 65 | uint16_t crc16_itut_bits(uint16_t crc, const uint8_t *input, int number_bits) 66 | { 67 | int i; 68 | 69 | for (i = 0; i < number_bits; ++i) { 70 | uint16_t bit = input[i] & 0x1; 71 | 72 | crc ^= bit << 15; 73 | if ((crc & 0x8000)) { 74 | crc <<= 1; 75 | crc ^= GEN_POLY; 76 | } else { 77 | crc <<= 1; 78 | } 79 | } 80 | 81 | return crc; 82 | } 83 | 84 | uint16_t crc16_itut_poly(uint16_t crc, uint32_t poly, const uint8_t *input, int number_bits) 85 | { 86 | int i; 87 | 88 | for (i = 0; i < number_bits; ++i) { 89 | uint16_t bit = input[i] & 0x1; 90 | 91 | crc ^= bit << 15; 92 | if ((crc & 0x8000)) { 93 | crc <<= 1; 94 | crc ^= poly; 95 | } else { 96 | crc <<= 1; 97 | } 98 | } 99 | 100 | return crc; 101 | } 102 | 103 | uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len) 104 | { 105 | return crc16_itut_bits(0xffff, bits, len); 106 | } 107 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_rm3014.c: -------------------------------------------------------------------------------- 1 | /* Shortened (30,14) Reed-Muller (RM) code */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | /* Generator matrix from Section 8.2.3.2 */ 27 | 28 | static const uint8_t rm_30_14_gen[14][16] = { 29 | { 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 }, 30 | { 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, 31 | { 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, 32 | { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, 33 | { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0 }, 34 | { 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 }, 35 | { 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0 }, 36 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 }, 37 | { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 }, 38 | { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1 }, 39 | { 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1 }, 40 | { 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1 }, 41 | { 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1 }, 42 | { 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 } 43 | }; 44 | 45 | static uint32_t rm_30_14_rows[14]; 46 | 47 | 48 | static uint32_t shift_bits_together(const uint8_t *bits, int len) 49 | { 50 | uint32_t ret = 0; 51 | int i; 52 | 53 | for (i = len-1; i >= 0; i--) 54 | ret |= bits[i] << (len-1-i); 55 | 56 | return ret; 57 | } 58 | 59 | void tetra_rm3014_init(void) 60 | { 61 | int i; 62 | uint32_t val; 63 | 64 | for (i = 0; i < 14; i++) { 65 | /* upper 14 bits identity matrix */ 66 | val = (1 << (16+13 - i)); 67 | /* lower 16 bits from rm_30_14_gen */ 68 | val |= shift_bits_together(rm_30_14_gen[i], 16); 69 | rm_30_14_rows[i] = val; 70 | printf("rm_30_14_rows[%u] = 0x%08x\n", i, val); 71 | } 72 | } 73 | 74 | uint32_t tetra_rm3014_compute(const uint16_t in) 75 | { 76 | int i; 77 | uint32_t val = 0; 78 | 79 | for (i = 0; i < 14; i++) { 80 | uint32_t bit = (in >> (14-1-i)) & 1; 81 | if (bit) 82 | val ^= rm_30_14_rows[i]; 83 | /* we can skip the 'else' as XOR with 0 has no effect */ 84 | } 85 | return val; 86 | } 87 | 88 | /** 89 | * This is a systematic code. We can remove the control bits 90 | * and then check for an error. Maybe correct it in the future. 91 | */ 92 | int tetra_rm3014_decode(const uint32_t inp, uint16_t *out) 93 | { 94 | *out = inp >> 16; 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/tetra_llc_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_LLC_PDU_H 2 | #define TETRA_LLC_PDU_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | /* Table 21.1 */ 9 | enum tetra_llc_pdu_t { 10 | TLLC_PDUT_BL_ADATA = 0, 11 | TLLC_PDUT_BL_DATA = 1, 12 | TLLC_PDUT_BL_UDATA = 2, 13 | TLLC_PDUT_BL_ACK = 3, 14 | TLLC_PDUT_BL_ADATA_FCS = 4, 15 | TLLC_PDUT_BL_DATA_FCS = 5, 16 | TLLC_PDUT_BL_UDATA_FCS = 6, 17 | TLLC_PDUT_BL_ACK_FCS = 7, 18 | TLLC_PDUT_AL_SETUP = 8, 19 | TLLC_PDUT_AL_DATA_FINAL = 9, 20 | TLLC_PDUT_AL_UDATA_UFINAL = 10, 21 | TLLC_PDUT_AL_ACK_RNR = 11, 22 | TLLC_PDUT_AL_RECONNECT = 12, 23 | TLLC_PDUT_SUPPL = 13, 24 | TLLC_PDUT_L2SIG = 14, 25 | TLLD_PDUT_AL_DISC = 15 26 | }; 27 | const char *tetra_get_llc_pdut_name(uint8_t pdut); 28 | 29 | /* Table 21.2 */ 30 | enum tetra_llc_l2sig_pdu_t { 31 | TLLC_PDUT_L2_DATA_PRIO = 0, 32 | TLLC_PDUT_L2_SCHEDULE_SYNC = 1, 33 | TLLC_PDUT_L2_LINK_FB_CTRL = 2, 34 | TLLC_PDUT_L2_LINK_FB_INFO = 3, 35 | TLLC_PDUT_L2_LINK_FB_INFO_RD_PRIO = 4 36 | }; 37 | 38 | /* Table 21.3 */ 39 | enum tetra_llc_suppl_pdu_t { 40 | TLLC_PDUT_SUPPL_ALX_DATA_FINAL = 0, 41 | TLLC_PDUT_SUPPL_ALX_UDATA_UFINAL = 1, 42 | TLLC_PDUT_SUPPL_ALX_ACK_RNR = 2 43 | }; 44 | 45 | /* Decoded */ 46 | 47 | enum tllc_pdut_dec { 48 | TLLC_PDUT_DEC_UNKNOWN, 49 | TLLC_PDUT_DEC_BL_ADATA, 50 | TLLC_PDUT_DEC_BL_DATA, 51 | TLLC_PDUT_DEC_BL_UDATA, 52 | TLLC_PDUT_DEC_BL_ACK, 53 | TLLC_PDUT_DEC_AL_SETUP, 54 | TLLC_PDUT_DEC_AL_DATA, 55 | TLLC_PDUT_DEC_AL_FINAL, 56 | TLLC_PDUT_DEC_AL_UDATA, 57 | TLLC_PDUT_DEC_AL_UFINAL, 58 | TLLC_PDUT_DEC_AL_ACK, 59 | TLLC_PDUT_DEC_AL_RNR, 60 | TLLC_PDUT_DEC_AL_RECONNECT, 61 | TLLC_PDUT_DEC_AL_DISC, 62 | TLLC_PDUT_DEC_ALX_DATA, 63 | TLLC_PDUT_DEC_ALX_FINAL, 64 | TLLC_PDUT_DEC_ALX_UDATA, 65 | TLLC_PDUT_DEC_ALX_UFINAL, 66 | TLLC_PDUT_DEC_ALX_ACK, 67 | TLLC_PDUT_DEC_ALX_RNR, 68 | }; 69 | const char *tetra_get_llc_pdut_dec_name(enum tllc_pdut_dec pdut); 70 | 71 | /* decoded/parsed PDU with easier encoding */ 72 | struct tetra_llc_pdu { 73 | enum tllc_pdut_dec pdu_type; 74 | uint8_t nr; /* N(R) PDU number (receive) */ 75 | uint8_t ns; /* N(S) PDU number (sent) */ 76 | uint8_t ss; /* S(S) Segment (sent) */ 77 | 78 | bool have_fcs; /* 1 if LLC PDU defines FCS is present */ 79 | uint32_t fcs; /* FCS value extracted from pdu */ 80 | bool fcs_invalid; /* 1 if extracted FCS does not match computed FCS */ 81 | 82 | uint8_t *tl_sdu; /* pointer to bitbuf */ 83 | uint32_t tl_sdu_len; /* in bits */ 84 | }; 85 | 86 | /* parse a received LLC PDU and parse it into 'lpp' */ 87 | int tetra_llc_pdu_parse(struct tetra_llc_pdu *lpp, uint8_t *buf, int len); 88 | 89 | /* TETRA LLC state */ 90 | struct tllc_state { 91 | struct llist_head list; 92 | 93 | struct { 94 | struct llist_head defrag_list; 95 | } rx; 96 | }; 97 | 98 | /* entry in the defragmentation queue */ 99 | struct tllc_defrag_q_e { 100 | struct llist_head list; 101 | unsigned int ns; /* current de-fragmenting */ 102 | unsigned int last_ss; /* last received S(S) */ 103 | 104 | struct msgb *tl_sdu; 105 | }; 106 | #endif /* TETRA_LLC_PDU_H */ 107 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_scramb.c: -------------------------------------------------------------------------------- 1 | /* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */ 2 | 3 | /* This converts from type-4 to type-5 bits (and vice versa) */ 4 | 5 | /* (C) 2011 by Harald Welte 6 | * All Rights Reserved 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | /* Tap macro for the standard XOR / Fibonacci form */ 27 | #define ST(x, y) ((x) >> (32-y)) 28 | 29 | /* Tap macro and constant for the Galois form */ 30 | #define GL(x) (1<<(x-1)) 31 | #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)) 32 | 33 | #if 1 34 | static uint8_t next_lfsr_bit(uint32_t *lf) 35 | { 36 | uint32_t lfsr = *lf; 37 | uint32_t bit; 38 | 39 | /* taps: 32 26 23 22 16 12 11 10 8 7 5 4 2 1 */ 40 | bit = (ST(lfsr, 32) ^ ST(lfsr, 26) ^ ST(lfsr, 23) ^ ST(lfsr, 22) ^ 41 | ST(lfsr, 16) ^ ST(lfsr, 12) ^ ST(lfsr, 11) ^ ST(lfsr, 10) ^ 42 | ST(lfsr, 8) ^ ST(lfsr, 7) ^ ST(lfsr, 5) ^ ST(lfsr, 4) ^ 43 | ST(lfsr, 2) ^ ST(lfsr, 1)) & 1; 44 | lfsr = (lfsr >> 1) | (bit << 31); 45 | 46 | /* update the caller's LFSR state */ 47 | *lf = lfsr; 48 | 49 | return bit & 0xff; 50 | } 51 | #else 52 | /* WARNING: this version somehow does not produce the desired result! */ 53 | static uint8_t next_lfsr_bit(uint32_t *lf) 54 | { 55 | uint32_t lfsr = *lf; 56 | uint32_t bit = lfsr & 1; 57 | 58 | lfsr = (lfsr >> 1) ^ (uint32_t)(0 - ((lfsr & 1u) & GALOIS_LFSR)); 59 | 60 | *lf = lfsr; 61 | 62 | return bit; 63 | } 64 | #endif 65 | 66 | int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len) 67 | { 68 | int i; 69 | 70 | for (i = 0; i < len; i++) 71 | out[i] = next_lfsr_bit(&lfsr_init); 72 | 73 | return 0; 74 | } 75 | 76 | /* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */ 77 | int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len) 78 | { 79 | int i; 80 | 81 | for (i = 0; i < len; i++) 82 | out[i] ^= next_lfsr_bit(&lfsr_init); 83 | 84 | return 0; 85 | } 86 | 87 | uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour) 88 | { 89 | uint32_t scramb_init; 90 | 91 | mcc &= 0x3ff; 92 | mnc &= 0x3fff; 93 | colour &= 0x3f; 94 | 95 | scramb_init = colour | (mnc << 6) | (mcc << 20); 96 | scramb_init = (scramb_init << 2) | SCRAMB_INIT; 97 | 98 | return scramb_init; 99 | } 100 | -------------------------------------------------------------------------------- /src/crypto/taa1.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_TAA1_H 2 | #define HAVE_TAA1_H 3 | 4 | #include 5 | 6 | #if __BYTE_ORDER == __LITTLE_ENDIAN 7 | #define be32(x) __builtin_bswap32(x) 8 | #define be16(x) __builtin_bswap16(x) 9 | #else 10 | #define be32(x) (x) 11 | #define be16(x) (x) 12 | #endif 13 | 14 | 15 | /* 16 | * transformation functions used by TAxx primitives 17 | */ 18 | void transform_80_to_120(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 19 | void transform_80_to_128(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 20 | void transform_80_to_120_alt(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 21 | void transform_80_to_128_alt(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 22 | void transform_88_to_120(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 23 | void transform_120_to_88(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 24 | void transform_120_to_80_alt(const uint8_t *lpBuffer, uint8_t *lpBufferOut); 25 | void transform_identity(const uint8_t *lpInput, uint8_t *lpOutput); 26 | void transform_identity_inverse(const uint8_t *lpInput, uint8_t *lpOutput); 27 | 28 | /* 29 | * TAxx primitives for authentication, key derivation and sealing functionality 30 | * (see ETSI EN 300 392-7) 31 | */ 32 | void ta11_ta41(uint8_t *lpKeyK, uint8_t *lpChallengeRs, uint8_t *lpKsOut); 33 | void ta12_ta22(uint8_t *lpKeyKs, uint8_t *lpRand, uint8_t *lpResOut, uint8_t *lpDckOut); 34 | void ta21(uint8_t *lpKeyK, uint8_t *lpChallengeRs, uint8_t *lpKspOut); 35 | void ta31(uint8_t *lpUnsealedCck, uint8_t *lpCckId, uint8_t *lpDck, uint8_t *lpSealedCckOut); 36 | void ta32(uint8_t *lpSealedCck, uint8_t *lpCckId, uint8_t *lpDck, uint8_t *lpUnsealedCckOut, uint8_t *lpMfOut); 37 | void ta51(uint8_t *lpUnsealed, uint8_t *lpVn, uint8_t *lpKey, uint8_t *lpKeyN, uint8_t *lpSealedOut); 38 | void ta52(uint8_t *lpSealed, uint8_t *lpKey, uint8_t *lpVn, uint8_t *lpUnsealedOut, uint8_t *lpMfOut, uint8_t *lpKeyNOut); 39 | void ta61(const uint8_t *lpKey, const uint8_t *lpIdentity, uint8_t *lpEncIdentityOut); 40 | void ta61_inv(uint8_t *lpKey, uint8_t *lpIdentity, uint8_t *lpDecIdentityOut); 41 | void ta71(uint8_t *lpGck, uint8_t *lpCck, uint8_t *lpMgckOut); 42 | void ta81(uint8_t *lpUnsealedGck, uint8_t *lpGckVn, uint8_t *lpGckN, uint8_t *lpKey, uint8_t *lpSealedGckOut); 43 | void ta82(uint8_t *lpSealedGck, uint8_t *lpGckVn, uint8_t *lpKey, uint8_t *lpUnsealedGckOut, uint8_t *lpMfOut, uint8_t *lpGckNOut); 44 | void ta91(uint8_t *lpUnsealedGsko, uint8_t *lpGskoVn, uint8_t *lpKey, uint8_t *lpSealedGskoOut); 45 | void ta92(uint8_t *lpSealedGsko, uint8_t *lpGskoVn, uint8_t *lpKey, uint8_t *lpUnsealedGskoOut, uint8_t *lpMfOut); 46 | 47 | /* 48 | * TA61 internals 49 | */ 50 | void ta61_compute_c(const uint8_t *lpKey, uint8_t *lpIntermediateKeyOut); 51 | void ta61_inner(const uint8_t *lpIntermediateKey, const uint8_t *lpIdentity, uint8_t *lpEncIdentityOut); 52 | void ta61_inner_inv(uint8_t *lpIntermediateKey, uint8_t *lpIdentity, uint8_t *lpDecIdentityOut); 53 | 54 | /* 55 | * TBxx non-cryptographic primitives also used for authentication and key derivation 56 | */ 57 | void tb4(uint8_t *lpDck1, uint8_t *lpDck2, uint8_t *lpDckOut); 58 | void tb5(uint8_t *lpCn, uint8_t *lpLa, uint8_t *lpCc, uint8_t *lpCk, uint8_t *lpEckOut); 59 | void tb6(uint8_t *lpSck, uint8_t *lpCn, uint8_t *lpSsi, uint8_t *lpEckOut); 60 | void tb7(uint8_t *lpGsko, uint8_t *lpEgskoOut); 61 | 62 | #endif /* HAVE_TAA1_H */ 63 | -------------------------------------------------------------------------------- /src/demod/fcdp-tetra_demod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import math 5 | from gnuradio import gr, gru, audio, eng_notation, blocks, filter 6 | from gnuradio import audio 7 | from gnuradio.eng_option import eng_option 8 | from optparse import OptionParser 9 | 10 | try: 11 | import cqpsk 12 | except: 13 | from tetra_demod import cqpsk 14 | 15 | # applies frequency translation, resampling (interpolation/decimation) and cqpsk demodulation 16 | 17 | class my_top_block(gr.top_block): 18 | def __init__(self, options): 19 | gr.top_block.__init__(self) 20 | 21 | sample_rate = int(options.sample_rate) 22 | 23 | self.asrc = audio.source(sample_rate, options.audio_device, True) 24 | 25 | self.f2c = blocks.float_to_complex(1) 26 | 27 | self.connect((self.asrc, 1), (self.f2c, 1)) 28 | self.connect((self.asrc, 0), (self.f2c, 0)) 29 | 30 | symbol_rate = 18000 31 | sps = 2 32 | # output rate will be 36,000 33 | ntaps = 11 * sps 34 | new_sample_rate = symbol_rate * sps 35 | 36 | channel_taps = filter.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, filter.firdes.WIN_HANN) 37 | 38 | FILTER = filter.freq_xlating_fir_filter_ccf(1, channel_taps, options.calibration, sample_rate) 39 | 40 | sys.stderr.write("sample rate: %d\n" %(sample_rate)) 41 | 42 | DEMOD = cqpsk.cqpsk_demod( samples_per_symbol = sps, 43 | excess_bw=0.35, 44 | costas_alpha=0.03, 45 | gain_mu=0.05, 46 | mu=0.05, 47 | omega_relative_limit=0.05, 48 | log=options.log, 49 | verbose=options.verbose) 50 | 51 | OUT = blocks.file_sink(gr.sizeof_float, options.output_file) 52 | 53 | r = float(sample_rate) / float(new_sample_rate) 54 | 55 | INTERPOLATOR = filter.fractional_interpolator_cc(0, r) 56 | 57 | self.connect(self.f2c, FILTER, INTERPOLATOR, DEMOD, OUT) 58 | 59 | def get_options(): 60 | parser = OptionParser(option_class=eng_option) 61 | 62 | parser.add_option("-r", "--sample-rate", type="eng_float", default=96000, 63 | help="set sample rate to RATE (96000)") 64 | parser.add_option("-D", "--audio-device", type="string", default="", 65 | help="pcm input device name. E.g., hw:0,0 or /dev/dsp") 66 | 67 | # demodulator related settings 68 | parser.add_option("-c", "--calibration", type="int", default=0, help="freq offset") 69 | parser.add_option("-l", "--log", action="store_true", default=False, help="dump debug .dat files") 70 | parser.add_option("-L", "--low-pass", type="eng_float", default=25e3, help="low pass cut-off", metavar="Hz") 71 | parser.add_option("-o", "--output-file", type="string", default="out.float", help="specify the bit output file") 72 | parser.add_option("-v", "--verbose", action="store_true", default=False, help="dump demodulation data") 73 | 74 | (options, args) = parser.parse_args() 75 | if len(args) != 0: 76 | parser.print_help() 77 | raise SystemExit, 1 78 | 79 | return (options) 80 | 81 | if __name__ == "__main__": 82 | (options) = get_options() 83 | tb = my_top_block(options) 84 | try: 85 | tb.run() 86 | except KeyboardInterrupt: 87 | tb.stop() 88 | -------------------------------------------------------------------------------- /src/demod/fcdp-tetra_demod_fft.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import math 5 | from gnuradio import gr, gru, audio, eng_notation, blocks, filter 6 | from gnuradio import audio 7 | from gnuradio.eng_option import eng_option 8 | from gnuradio.wxgui import stdgui2, fftsink2 9 | from optparse import OptionParser 10 | 11 | try: 12 | import cqpsk 13 | except: 14 | from tetra_demod import cqpsk 15 | 16 | # applies frequency translation, resampling (interpolation/decimation) and cqpsk demodulation 17 | 18 | class my_top_block(stdgui2.std_top_block): 19 | def __init__(self, frame, panel, vbox, argv): 20 | stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) 21 | 22 | self.frame = frame 23 | self.panel = panel 24 | 25 | options = get_options() 26 | 27 | sample_rate = int(options.sample_rate) 28 | 29 | self.asrc = audio.source(sample_rate, options.audio_device, True) 30 | 31 | self.f2c = blocks.float_to_complex(1) 32 | 33 | self.connect((self.asrc, 1), (self.f2c, 1)) 34 | self.connect((self.asrc, 0), (self.f2c, 0)) 35 | 36 | symbol_rate = 18000 37 | sps = 2 38 | # output rate will be 36,000 39 | ntaps = 11 * sps 40 | new_sample_rate = symbol_rate * sps 41 | 42 | channel_taps = filter.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, filter.firdes.WIN_HANN) 43 | 44 | FILTER = filter.freq_xlating_fir_filter_ccf(1, channel_taps, options.calibration, sample_rate) 45 | 46 | sys.stderr.write("sample rate: %d\n" %(sample_rate)) 47 | 48 | DEMOD = cqpsk.cqpsk_demod( samples_per_symbol = sps, 49 | excess_bw=0.35, 50 | costas_alpha=0.03, 51 | gain_mu=0.05, 52 | mu=0.05, 53 | omega_relative_limit=0.05, 54 | log=options.log, 55 | verbose=options.verbose) 56 | 57 | OUT = blocks.file_sink(gr.sizeof_float, options.output_file) 58 | 59 | r = float(sample_rate) / float(new_sample_rate) 60 | 61 | INTERPOLATOR = filter.fractional_interpolator_cc(0, r) 62 | 63 | self.connect(self.f2c, FILTER, INTERPOLATOR, DEMOD, OUT) 64 | 65 | self.scope = fftsink2.fft_sink_c(panel, fft_size=512, 66 | sample_rate=sample_rate, 67 | ref_scale=2.0, 68 | ref_level=-30, y_divs=10, 69 | fft_rate=10, 70 | average=True, 71 | avg_alpha=0.2) 72 | self.connect(self.f2c, self.scope) 73 | 74 | def get_options(): 75 | parser = OptionParser(option_class=eng_option) 76 | 77 | parser.add_option("-r", "--sample-rate", type="eng_float", default=96000, 78 | help="set sample rate to RATE (96000)") 79 | parser.add_option("-D", "--audio-device", type="string", default="", 80 | help="pcm input device name. E.g., hw:0,0 or /dev/dsp") 81 | 82 | # demodulator related settings 83 | parser.add_option("-c", "--calibration", type="int", default=0, help="freq offset") 84 | parser.add_option("-l", "--log", action="store_true", default=False, help="dump debug .dat files") 85 | parser.add_option("-L", "--low-pass", type="eng_float", default=25e3, help="low pass cut-off", metavar="Hz") 86 | parser.add_option("-o", "--output-file", type="string", default="out.float", help="specify the bit output file") 87 | parser.add_option("-v", "--verbose", action="store_true", default=False, help="dump demodulation data") 88 | (options, args) = parser.parse_args() 89 | if len(args) != 0: 90 | parser.print_help() 91 | raise SystemExit, 1 92 | 93 | return (options) 94 | 95 | if __name__ == "__main__": 96 | app = stdgui2.stdapp(my_top_block, "OsmoTETRA FCDP", nstatus=1) 97 | app.MainLoop() 98 | -------------------------------------------------------------------------------- /src/tunctl.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2002 Jeff Dike 2 | * Licensed under the GPL 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /* TUNSETGROUP appeared in 2.6.23 */ 18 | #ifndef TUNSETGROUP 19 | #define TUNSETGROUP _IOW('T', 206, int) 20 | #endif 21 | 22 | static void Usage(char *name) 23 | { 24 | fprintf(stderr, "Create: %s [-b] [-u owner] [-g group] [-t device-name] " 25 | "[-f tun-clone-device] [ -U ]\n", name); 26 | fprintf(stderr, "Delete: %s -d device-name [-f tun-clone-device]\n\n", 27 | name); 28 | fprintf(stderr, "The default tun clone device is /dev/net/tun - some systems" 29 | " use\n/dev/misc/net/tun instead\n\n"); 30 | fprintf(stderr, "-b will result in brief output (just the device name)\n"); 31 | exit(1); 32 | } 33 | 34 | int main(int argc, char **argv) 35 | { 36 | struct ifreq ifr; 37 | struct passwd *pw; 38 | struct group *gr; 39 | uid_t owner = -1; 40 | gid_t group = -1; 41 | int tap_fd, opt, delete = 0, brief = 0; 42 | char *tun = "", *file = "/dev/net/tun", *name = argv[0], *end; 43 | unsigned int iff_type = IFF_TAP; 44 | 45 | while((opt = getopt(argc, argv, "bd:f:t:u:g:U")) > 0){ 46 | switch(opt) { 47 | case 'b': 48 | brief = 1; 49 | break; 50 | case 'd': 51 | delete = 1; 52 | tun = optarg; 53 | break; 54 | case 'f': 55 | file = optarg; 56 | break; 57 | case 'u': 58 | pw = getpwnam(optarg); 59 | if(pw != NULL){ 60 | owner = pw->pw_uid; 61 | break; 62 | } 63 | owner = strtol(optarg, &end, 0); 64 | if(*end != '\0'){ 65 | fprintf(stderr, "'%s' is neither a username nor a numeric uid.\n", 66 | optarg); 67 | Usage(name); 68 | } 69 | break; 70 | case 'g': 71 | gr = getgrnam(optarg); 72 | if(gr != NULL){ 73 | group = gr->gr_gid; 74 | break; 75 | } 76 | group = strtol(optarg, &end, 0); 77 | if(*end != '\0'){ 78 | fprintf(stderr, "'%s' is neither a groupname nor a numeric group.\n", 79 | optarg); 80 | Usage(name); 81 | } 82 | break; 83 | 84 | case 't': 85 | tun = optarg; 86 | break; 87 | case 'U': 88 | iff_type = IFF_TUN; 89 | break; 90 | case 'h': 91 | default: 92 | Usage(name); 93 | } 94 | } 95 | 96 | argv += optind; 97 | argc -= optind; 98 | 99 | if(argc > 0) 100 | Usage(name); 101 | 102 | if((tap_fd = open(file, O_RDWR)) < 0){ 103 | fprintf(stderr, "Failed to open '%s' : ", file); 104 | perror(""); 105 | exit(1); 106 | } 107 | 108 | memset(&ifr, 0, sizeof(ifr)); 109 | 110 | ifr.ifr_flags = iff_type | IFF_NO_PI; 111 | strncpy(ifr.ifr_name, tun, sizeof(ifr.ifr_name) - 1); 112 | if(ioctl(tap_fd, TUNSETIFF, (void *) &ifr) < 0){ 113 | perror("TUNSETIFF"); 114 | exit(1); 115 | } 116 | 117 | if(delete){ 118 | if(ioctl(tap_fd, TUNSETPERSIST, 0) < 0){ 119 | perror("disabling TUNSETPERSIST"); 120 | exit(1); 121 | } 122 | printf("Set '%s' nonpersistent\n", ifr.ifr_name); 123 | } 124 | else { 125 | /* emulate behaviour prior to TUNSETGROUP */ 126 | if(owner == -1 && group == -1) { 127 | owner = geteuid(); 128 | } 129 | 130 | if(owner != -1) { 131 | if(ioctl(tap_fd, TUNSETOWNER, owner) < 0){ 132 | perror("TUNSETOWNER"); 133 | exit(1); 134 | } 135 | } 136 | if(group != -1) { 137 | if(ioctl(tap_fd, TUNSETGROUP, group) < 0){ 138 | perror("TUNSETGROUP"); 139 | exit(1); 140 | } 141 | } 142 | 143 | if(ioctl(tap_fd, TUNSETPERSIST, 1) < 0){ 144 | perror("enabling TUNSETPERSIST"); 145 | exit(1); 146 | } 147 | 148 | if(brief) 149 | printf("%s\n", ifr.ifr_name); 150 | else { 151 | printf("Set '%s' persistent and owned by", ifr.ifr_name); 152 | if(owner != -1) 153 | printf(" uid %d", owner); 154 | if(group != -1) 155 | printf(" gid %d", group); 156 | printf("\n"); 157 | } 158 | } 159 | return(0); 160 | } 161 | -------------------------------------------------------------------------------- /src/lower_mac/tch_reordering.c: -------------------------------------------------------------------------------- 1 | /* Bit re-ordering for TETRA ACELP speech codec */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * 5 | * All Rights Reserved 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | 26 | /* EN 300 395-2 V1.3.1 Table 4 */ 27 | 28 | #define NUM_ACELP_CLASS0_BITS 51 29 | /* 51 Unprotected Class0 bits */ 30 | static const uint8_t class0_positions[NUM_ACELP_CLASS0_BITS] = { 31 | 35, 36, 37, 32 | 38, 39, 40, 33 | 41, 42, 33, 34 | 47, 48, 35 | 56, 36 | 61, 62, 63, 37 | 65, 66, 67, 38 | 68, 69, 70, 39 | 74, 75, 40 | 83, 41 | 88, 42 | 89, 90, 91, 43 | 92, 93, 94, 44 | 95, 96, 97, 45 | 101, 102, 46 | 110, 47 | 115, 48 | 116, 117, 118, 49 | 119, 120, 121, 50 | 122, 123, 124, 51 | 128, 129, 52 | 137 53 | }; 54 | 55 | #define NUM_ACELP_CLASS1_BITS 56 56 | static const uint8_t class1_positions[NUM_ACELP_CLASS1_BITS] = { 57 | 58, 85, 112, 58 | 54, 81, 108, 135, 59 | 50, 77, 60 | 104, 131, 61 | 45, 72, 99, 126, 62 | 55, 82, 109, 136, 63 | 5, 13, 34, 64 | 8, 16, 17, 65 | 22, 23, 24, 66 | 25, 26, 67 | 6, 14, 7, 15, 68 | 60, 87 ,114, 69 | 46, 70 | 73, 100, 127, 71 | 44, 71, 98, 125, 72 | 33, 49, 73 | 76, 103, 130, 74 | 59, 86, 113, 75 | 57, 84, 111 76 | }; 77 | 78 | #define NUM_ACELP_CLASS2_BITS 30 79 | static const uint8_t class2_positions[NUM_ACELP_CLASS2_BITS] = { 80 | 18, 19, 20, 21, 81 | 31, 32, 82 | 53, 80, 107, 134, 83 | 1, 2, 3, 4, 84 | 9, 10, 11, 12, 85 | 27, 28, 29, 30, 86 | 52, 79, 106, 133, 87 | 51, 78, 105, 132 88 | }; 89 | 90 | #define NUM_ACELP_BITS (NUM_ACELP_CLASS0_BITS+NUM_ACELP_CLASS1_BITS+NUM_ACELP_CLASS2_BITS) 91 | 92 | /* reorder the 432-bit incoming (decoded) type2 bits of a full-rate speech 93 | * frame in order to generate two consecutive 216-bit ACELP codec frames */ 94 | void tetra_acelp_type2_to_codec(const uint8_t *in, uint8_t *out) 95 | { 96 | int bit, frame; 97 | const uint8_t *in_cur = in; 98 | 99 | for (bit = 0; bit < NUM_ACELP_CLASS0_BITS; bit++) { 100 | for (frame = 0; frame < 2; frame++) 101 | out[frame*NUM_ACELP_BITS + class0_positions[bit] - 1] = in_cur[2*bit + frame]; 102 | } 103 | in_cur += 2*NUM_ACELP_CLASS0_BITS; 104 | 105 | for (bit = 0; bit < NUM_ACELP_CLASS1_BITS; bit++) { 106 | for (frame = 0; frame < 2; frame++) 107 | out[frame*NUM_ACELP_BITS + class1_positions[bit] - 1] = in_cur[2*bit + frame]; 108 | } 109 | in_cur += 2*NUM_ACELP_CLASS1_BITS; 110 | 111 | for (bit = 0; bit < NUM_ACELP_CLASS2_BITS; bit++) { 112 | for (frame = 0; frame < 2; frame++) 113 | out[frame*NUM_ACELP_BITS + class2_positions[bit] - 1] = in_cur[2*bit + frame]; 114 | } 115 | 116 | /* FIXME: same for STCH use */ 117 | } 118 | 119 | void tetra_acelp_codec_to_acelp(const uint8_t *in, uint8_t *out) 120 | { 121 | int bit, frame; 122 | uint8_t *out_cur = out; 123 | 124 | for (bit = 0; bit < NUM_ACELP_CLASS0_BITS; bit++) { 125 | for (frame = 0; frame < 2; frame++) 126 | out_cur[2*bit + frame] = in[frame*NUM_ACELP_BITS + class0_positions[bit] - 1]; 127 | } 128 | out_cur += 2*NUM_ACELP_CLASS0_BITS; 129 | 130 | for (bit = 0; bit < NUM_ACELP_CLASS1_BITS; bit++) { 131 | for (frame = 0; frame < 2; frame++) 132 | out_cur[2*bit + frame] = in[frame*NUM_ACELP_BITS + class1_positions[bit] - 1]; 133 | } 134 | out_cur += 2*NUM_ACELP_CLASS1_BITS; 135 | 136 | for (bit = 0; bit < NUM_ACELP_CLASS2_BITS; bit++) { 137 | for (frame = 0; frame < 2; frame++) 138 | out_cur[2*bit + frame] = in[frame*NUM_ACELP_BITS + class2_positions[bit] - 1]; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/float_to_bits.c: -------------------------------------------------------------------------------- 1 | /* Convert floating point symbols in the range +3/-3 to hard bits, 2 | * in the 1-bit-per-byte format */ 3 | 4 | /* (C) 2011 by Harald Welte 5 | * All Rights Reserved 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation; either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | static int process_sym_fl(float fl) 34 | { 35 | int ret; 36 | 37 | /* very simplistic scheme */ 38 | if (fl > 2) 39 | ret = 3; 40 | else if (fl > 0) 41 | ret = 1; 42 | else if (fl < -2) 43 | ret = -3; 44 | else 45 | ret = -1; 46 | 47 | return ret; 48 | } 49 | 50 | static void sym_int2bits(int sym, uint8_t *ret) 51 | { 52 | switch (sym) { 53 | case -3: 54 | ret[0] = 1; 55 | ret[1] = 1; 56 | break; 57 | case 1: 58 | ret[0] = 0; 59 | ret[1] = 0; 60 | break; 61 | //case -1: 62 | case 3: 63 | ret[0] = 0; 64 | ret[1] = 1; 65 | break; 66 | //case 3: 67 | case -1: 68 | ret[0] = 1; 69 | ret[1] = 0; 70 | break; 71 | } 72 | } 73 | 74 | // size of IO buffers (number of elements) 75 | #define BUF_SIZE 1024 76 | #define MAXVAL 5.0 77 | 78 | 79 | int main(int argc, char **argv) 80 | { 81 | int fd, fd_out, opt; 82 | int opt_verbose = 0; 83 | int opt_afc = 0; 84 | float filter = 0; 85 | float filter_val = 0.0001; 86 | float filter_goal = 0; 87 | int sym; 88 | 89 | while ((opt = getopt(argc, argv, "vaf:F:")) != -1) { 90 | switch (opt) { 91 | case 'v': 92 | opt_verbose = 1; 93 | break; 94 | case 'a': 95 | opt_afc = 1; 96 | break; 97 | case 'f': 98 | filter_val = atof(optarg); 99 | break; 100 | case 'F': 101 | filter_goal = atof(optarg); 102 | break; 103 | 104 | default: 105 | exit(2); 106 | } 107 | } 108 | 109 | if (argc <= optind+1) { 110 | fprintf(stderr, "Usage: %s [-v] [-a] [-f filter_val] [-F filter_goal] \n", argv[0]); 111 | fprintf(stderr, "-v verbose, print bits to stdout\n"); 112 | fprintf(stderr, "-a turn on pseudo-afc (automatic frequency correction)\n"); 113 | fprintf(stderr, "-f pseudo-afc averaging filter constant (default 0.0001)\n"); 114 | fprintf(stderr, "-F pseudo-afc filter goal (default: 0)\n"); 115 | exit(2); 116 | } 117 | 118 | fd = open(argv[optind], O_RDONLY); 119 | if (fd < 0) { 120 | perror("open infile"); 121 | exit(1); 122 | } 123 | fd_out = creat(argv[optind+1], 0660); 124 | if (fd_out < 0) { 125 | perror("open outfile"); 126 | exit(1); 127 | } 128 | while (1) { 129 | int rc; 130 | float fl[BUF_SIZE]; 131 | uint8_t bits[2*BUF_SIZE]; 132 | rc = read(fd, fl, sizeof(*fl) * BUF_SIZE); 133 | if (rc < 0) { 134 | perror("read"); 135 | exit(1); 136 | } else if (rc == 0) { 137 | break; 138 | } 139 | rc /= sizeof(*fl); 140 | int i; 141 | for (i = 0; i < rc; ++i) { 142 | if (opt_afc) { 143 | if ((fl[i] > -MAXVAL) && (fl[i] < MAXVAL)) { 144 | filter = filter * (1.0 - filter_val) + (fl[i] - filter_goal) * filter_val; 145 | } 146 | sym = process_sym_fl(fl[i]-filter); 147 | } else { 148 | sym = process_sym_fl(fl[i]); 149 | } 150 | 151 | sym_int2bits(sym, bits + i*2); 152 | //printf("%2d %1u %1u %f\n", rc, bits[0], bits[1], fl); 153 | if (opt_verbose) { 154 | printf("%1u%1u", bits[2*i + 0], bits[2*i + 1]); 155 | } 156 | } 157 | 158 | rc = write(fd_out, bits, rc * 2); 159 | if (rc < 0) { 160 | perror("write"); 161 | exit(1); 162 | } else if (rc == 0) 163 | break; 164 | } 165 | exit(0); 166 | } 167 | -------------------------------------------------------------------------------- /src/tetra_common.c: -------------------------------------------------------------------------------- 1 | /* Common routines for the TETRA PHY/MAC implementation */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "tetra_common.h" 29 | #include "tetra_prim.h" 30 | 31 | uint32_t bits_to_uint(const uint8_t *bits, unsigned int len) 32 | { 33 | uint32_t ret = 0; 34 | 35 | while (len--) 36 | ret = (ret << 1) | (*bits++ & 1); 37 | 38 | return ret; 39 | } 40 | 41 | static inline uint32_t tetra_band_base_hz(uint8_t band) 42 | { 43 | return (band * 100000000); 44 | } 45 | 46 | static const int16_t tetra_carrier_offset[4] = { 47 | [0] = 0, 48 | [1] = 6250, 49 | [2] = -6250, 50 | [3] = 12500, 51 | }; 52 | 53 | uint32_t tetra_dl_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset) 54 | { 55 | uint32_t freq = tetra_band_base_hz(band); 56 | freq += carrier * 25000; 57 | freq += tetra_carrier_offset[offset&3]; 58 | return freq; 59 | } 60 | 61 | /* TS 100 392-15, Table 2 */ 62 | static const int16_t tetra_duplex_spacing[8][16] = { /* values are in kHz */ 63 | [0] = { -1, 1600, 10000, 10000, 10000, 10000, 10000, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 64 | [1] = { -1, 4500, -1, 36000, 7000, -1, -1, -1, 45000, 45000, -1, -1, -1, -1, -1, -1 }, 65 | [2] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 66 | [3] = { -1, -1, -1, 8000, 8000, -1, -1, -1, 18000, 18000, -1, -1, -1, -1, -1, -1 }, 67 | [4] = { -1, -1, -1, 18000, 5000, -1, 30000, 30000, -1, 39000, -1, -1, -1, -1, -1, -1 }, 68 | [5] = { -1, -1, -1, -1, 9500, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 69 | [6] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 70 | [7] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, 71 | }; 72 | 73 | uint32_t tetra_ul_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset, 74 | uint8_t duplex, uint8_t reverse) 75 | { 76 | uint32_t freq = tetra_dl_carrier_hz(band, carrier, offset); 77 | 78 | uint32_t duplex_spacing = tetra_duplex_spacing[duplex & 7][band & 15]; 79 | 80 | if (duplex_spacing < 0) /* reserved for future standardization */ 81 | return 0; 82 | 83 | duplex_spacing *= 1000; // make Hz 84 | 85 | if (reverse) 86 | freq += duplex_spacing; 87 | else 88 | freq -= duplex_spacing; 89 | 90 | return freq; 91 | } 92 | 93 | static const struct value_string tetra_sap_names[] = { 94 | { TETRA_SAP_TP, "TP-SAP" }, 95 | { TETRA_SAP_TMV, "TMV-SAP" }, 96 | { TETRA_SAP_TMA, "TMA-SAP" }, 97 | { TETRA_SAP_TMB, "TMB-SAP" }, 98 | { TETRA_SAP_TMD, "TMD-SAP" }, 99 | { 0, NULL } 100 | }; 101 | 102 | static const struct value_string tetra_lchan_names[] = { 103 | { TETRA_LC_UNKNOWN, "UNKNOWN" }, 104 | { TETRA_LC_SCH_F, "SCH/F" }, 105 | { TETRA_LC_SCH_HD, "SCH/HD" }, 106 | { TETRA_LC_SCH_HU, "SCH/HU" }, 107 | { TETRA_LC_STCH, "STCH" }, 108 | { TETRA_LC_SCH_P8_F, "SCH-P8/F" }, 109 | { TETRA_LC_SCH_P8_HD, "SCH-P8/HD" }, 110 | { TETRA_LC_SCH_P8_HU, "SCH-P8/HU" }, 111 | { TETRA_LC_AACH, "AACH" }, 112 | { TETRA_LC_TCH, "TCH" }, 113 | { TETRA_LC_BSCH, "BSCH" }, 114 | { TETRA_LC_BNCH, "BNCH" }, 115 | { 0, NULL } 116 | }; 117 | 118 | const char *tetra_get_lchan_name(enum tetra_log_chan lchan) 119 | { 120 | return get_value_string(tetra_lchan_names, lchan); 121 | } 122 | 123 | const char *tetra_get_sap_name(uint8_t sap) 124 | { 125 | return get_value_string(tetra_sap_names, sap); 126 | } 127 | 128 | void tetra_mac_state_init(struct tetra_mac_state *tms) 129 | { 130 | INIT_LLIST_HEAD(&tms->voice_channels); 131 | } 132 | -------------------------------------------------------------------------------- /src/testpdu.c: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Harald Welte 2 | * All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "testpdu.h" 27 | 28 | uint8_t pdu_sync[8]; /* 60 bits */ 29 | uint8_t pdu_sysinfo[16]; /* 124 bits */ 30 | uint8_t pdu_acc_ass[2]; 31 | uint8_t pdu_schf[268]; 32 | 33 | void testpdu_init() 34 | { 35 | struct bitvec bv; 36 | 37 | memset(&bv, 0, sizeof(bv)); 38 | bv.data = pdu_sync; 39 | bv.data_len = sizeof(pdu_sync); 40 | 41 | //bitvec_set_uint(&bv, 0, 4); /* alignment */ 42 | /* According to Table 21.73: SYNC PDU Contents */ 43 | bitvec_set_uint(&bv, 0, 4); /* System Code: ETS 300 392-2 ed. 1 */ 44 | bitvec_set_uint(&bv, 0, 6); /* Colour Code: Predefined Scrambling */ 45 | bitvec_set_uint(&bv, 0, 2); /* Timeslot number: TN 1 */ 46 | bitvec_set_uint(&bv, 1, 5); /* Frame number: FN 1 */ 47 | bitvec_set_uint(&bv, 1, 6); /* Multiframe number: MN 1 */ 48 | bitvec_set_uint(&bv, 0, 2); /* Sharing mode: continuous transmission */ 49 | bitvec_set_uint(&bv, 0, 3); /* TS reserved frames: 1 frame per 2 mfrm */ 50 | bitvec_set_bit(&bv, 0); /* No DTX */ 51 | bitvec_set_bit(&bv, 0); /* No Frame 18 extension */ 52 | bitvec_set_bit(&bv, 0); /* Reserved */ 53 | /* As defined in Table 18.4.2.1: D-MLE-SYNC */ 54 | bitvec_set_uint(&bv, 262, 10); /* MCC */ 55 | bitvec_set_uint(&bv, 42, 14); /* MNC */ 56 | bitvec_set_uint(&bv, 0, 2); /* Neighbor cell boradcast: not supported */ 57 | bitvec_set_uint(&bv, 0, 2); /* Cell service level: unknown */ 58 | bitvec_set_bit(&bv, 0); /* Late entry information */ 59 | printf("SYNC PDU: %s\n", osmo_hexdump(pdu_sync, sizeof(pdu_sync))); 60 | 61 | memset(&bv, 0, sizeof(bv)); 62 | bv.data = pdu_sysinfo; 63 | bv.data_len = sizeof(pdu_sysinfo); 64 | 65 | //bitvec_set_uint(&bv, 0, 4); /* alignment */ 66 | /* According to Table 21.4.4.1: SYSINFO PDU contents */ 67 | bitvec_set_uint(&bv, 2, 2); /* MAC PDU type: Broadcast */ 68 | bitvec_set_uint(&bv, 0, 2); /* SYSINVO PDU */ 69 | bitvec_set_uint(&bv, ((392775-300000)/25), 12); /* Main carier */ 70 | bitvec_set_uint(&bv, 3, 4); /* Frequency band: 390/400 */ 71 | bitvec_set_uint(&bv, 0, 2); /* Offset: No offset */ 72 | bitvec_set_uint(&bv, 0, 3); /* Duplex Spacing: */ 73 | bitvec_set_bit(&bv, 0); /* Normal operation */ 74 | bitvec_set_uint(&bv, 0, 2); /* Number of CSCH: none */ 75 | bitvec_set_uint(&bv, 1, 3); /* MS_TXPWR_MAX_CELL: 15 dBm */ 76 | bitvec_set_uint(&bv, 0, 4); /* RXLEV_ACCESS_MIN: -125dBm */ 77 | bitvec_set_uint(&bv, 0, 4); /* ACCESS_PARAMETER: -53 dBm */ 78 | bitvec_set_uint(&bv, 0, 4); /* RADIO_DOWNLINK_TIMEOUT: Disable */ 79 | bitvec_set_bit(&bv, 0); /* Hyperframe number follows */ 80 | bitvec_set_uint(&bv, 0, 16); /* Hyperframe number */ 81 | bitvec_set_uint(&bv, 0, 2); /* Optional field: Even multiframe */ 82 | bitvec_set_uint(&bv, 0, 20); /* TS_COMMON_FRAMES for even mframe */ 83 | /* TM-SDU (42 bit), Section 18.4.2.2, Table 18.15 */ 84 | bitvec_set_uint(&bv, 0, 14); /* Location Area (18.5.9) */ 85 | bitvec_set_uint(&bv, 0xFFFF, 16); /* Subscriber Class (18.5.22) */ 86 | bitvec_set_uint(&bv, 0, 12); /* BS service details (18.5.2) */ 87 | printf("SYSINFO PDU: %s\n", osmo_hexdump(pdu_sysinfo, sizeof(pdu_sysinfo))); 88 | 89 | memset(&bv, 0, sizeof(bv)); 90 | bv.data = pdu_acc_ass; 91 | bv.data_len = sizeof(pdu_acc_ass); 92 | 93 | bitvec_set_uint(&bv, 0, 2); /* alignment */ 94 | /* According to Table 21.27: ACCESS-ASSIGN PDU */ 95 | bitvec_set_uint(&bv, 0, 2); /* DL/UL: common only */ 96 | bitvec_set_uint(&bv, 0, 6); 97 | bitvec_set_uint(&bv, 0, 6); 98 | printf("ACCESS-ASSIGN PDU: %s\n", osmo_hexdump(pdu_acc_ass, sizeof(pdu_acc_ass))); 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TETRA MAC/PHY layer experimentation code 2 | ======================================== 3 | 4 | (C) 2010-2016 by Harald Welte and contributors 5 | 6 | This code aims to implement the sending and receiving part of the 7 | TETRA MAC/PHY layer. 8 | 9 | If you read the ETSI EN 300 392-2 (TETRA V+D Air Interface), you will 10 | find this code implementing the parts between the MAC-blocks (called 11 | type-1 bits) and the bits that go to the DQPSK-modulator (type-5 bits). 12 | 13 | It is most useful to look at Figure 8.5, 8.6, 9.3 and 19.12 in conjunction 14 | with this program. 15 | 16 | You will need 17 | [libosmocore](https://osmocom.org/projects/libosmocore/wiki/Libosmocore) 18 | to build this softwar 19 | 20 | Homepage 21 | -------- 22 | 23 | The official homepage of the project is 24 | https://osmocom.org/projects/tetra/wiki/OsmocomTETRA 25 | 26 | GIT Repository 27 | -------------- 28 | 29 | You can clone from the official osmo-tetra.git repository using 30 | 31 | git clone https://gitea.osmocom.org/tetra/osmo-tetra 32 | 33 | There is a web interface at 34 | 35 | Mailing List 36 | ------------ 37 | 38 | Discussions related to osmo-tetra are happening on the 39 | tetra@lists.osmocom.org mailing list, please see 40 | https://lists.osmocom.org/mailman/listinfo/tetra for subscription 41 | options and the list archive. 42 | 43 | Please observe the [Osmocom Mailing List 44 | Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules) 45 | when posting. 46 | 47 | Contributing 48 | ------------ 49 | 50 | Our coding standards are described at 51 | https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards 52 | 53 | We us a gerrit based patch submission/review process for managing 54 | contributions. Please see 55 | https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for 56 | more details 57 | 58 | The current patch queue for osmo-tetra can be seen at 59 | https://gerrit.osmocom.org/#/q/project:osmo-tetra+status:open 60 | 61 | 62 | Demodulator 63 | =========== 64 | 65 | src/demod/python/cpsk.py 66 | * contains a gnuradio based pi4/DQPSK demodulator, courtesy of KA1RBI 67 | 68 | src/demod/python/osmosdr-tetra_demod_fft.py 69 | * call demodulator on any source supported by gr-osmosdr 70 | (uhd, fcd, hackrf, blaerf, etc.) 71 | 72 | src/demod/python/simdemod2.py 73 | * call demodulator on a 'cfile' containing complex baseband samples 74 | 75 | src/demod/python/{uhd,fcdp}-tetra_demod.py 76 | * use demodulator directly with UHd or FCDP hadware (no gr-osmosdr) 77 | 78 | The output of the demodulator is a file containing one float value for each symbol, 79 | containing the phase shift (in units of pi/4) relative to the previous symbol. 80 | 81 | You can use the "float_to_bits" program to convert the float values to unpacked 82 | bits, i.e. 1-bit-per-byte 83 | 84 | 85 | PHY/MAC layer 86 | ============= 87 | 88 | library code 89 | ------------ 90 | 91 | Specifically, it implements: 92 | lower_mac/crc_simple.[ch] 93 | * CRC16-CCITT (currently defunct/broken as we need it for 94 | non-octet-aligned bitfields) 95 | lower_mac/tetra_conv_enc.[ch] 96 | * 16-state Rate-Compatible Punctured Convolutional (RCPC) coder 97 | lower_mac/tetra_interleave.[ch] 98 | * Block interleaving (over a single block only) 99 | lower_mac/tetra_rm3014.[ch] 100 | * (30, 14) Reed-Muller code for the ACCH (broadcast block of 101 | each downlink burst) 102 | lower_mac/tetra_scramb.[ch] 103 | * Scrambling 104 | lower_mac/viterbi*.[ch] 105 | * Convolutional decoder for signalling and voice channels 106 | phy/tetra_burst.[ch] 107 | * Routines to encode continuous normal and sync bursts 108 | phy/tetra_burst_sync.[ch] 109 | 110 | 111 | Receiver Program 112 | ---------------- 113 | 114 | The main receiver program 'tetra-rx' expects an input file containing a 115 | stream of unpacked bits, i.e. 1-bit-per-byte. 116 | 117 | 118 | Transmitter Program 119 | ------------------- 120 | 121 | The main program conv_enc_test.c generates a single continuous downlinc sync 122 | burst (SB), contining: 123 | * a SYNC-PDU as block 1 124 | * a ACCESS-ASSIGN PDU as broadcast block 125 | * a SYSINFO-PDU as block 2 126 | 127 | Scrambling is set to 0 (no scrambling) for all elements of the burst. 128 | 129 | It does not actually modulate and/or transmit yet. 130 | 131 | 132 | Quick example 133 | ============= 134 | 135 | # assuming you have generated a file samples.cfile at a sample rate of 136 | # 195.312kHz (100MHz/512 == USRP2 at decimation 512) 137 | src/demod/python/tetra-demod.py -i /tmp/samples.cfile -o /tmp/out.float -s 195312 -c 0 138 | src/float_to_bits /tmp/out.float /tmp/out.bits 139 | src/tetra-rx /tmp/out.bits 140 | 141 | -------------------------------------------------------------------------------- /src/crypto/tetra_crypto.h: -------------------------------------------------------------------------------- 1 | /* Cryptography related helper, key management and wrapper functions */ 2 | /* 3 | * Copyright (C) 2023 Midnight Blue B.V. 4 | * 5 | * Author: Wouter Bokslag 6 | * 7 | * SPDX-License-Identifier: AGPL-3.0+ 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * See the COPYING file in the main directory for details. 22 | */ 23 | 24 | 25 | #ifndef TETRA_CRYPTO_H 26 | #define TETRA_CRYPTO_H 27 | 28 | #include 29 | #include 30 | 31 | #include "../tetra_prim.h" 32 | #include "../tetra_tdma.h" 33 | #include "../tetra_mac_pdu.h" 34 | 35 | #define TCDB_ALLOC_BLOCK_SIZE 16 36 | 37 | enum tetra_key_type { 38 | KEYTYPE_UNDEFINED = 0, 39 | 40 | /* Keytypes usable as ECK after applying TB5 */ 41 | KEYTYPE_CCK_SCK = 1, /* SCK in class 2, CCK in class 3 networks */ 42 | KEYTYPE_DCK = 2, /* FIXME: add support for DCK */ 43 | KEYTYPE_MGCK = 4, /* FIXME: add support for MGCK */ 44 | KEYTYPE_GCK = 8, /* FIXME: add support. Used with SCK/CCK to derive MGCK */ 45 | }; 46 | 47 | enum tetra_ksg_type { 48 | UNKNOWN = 0, 49 | KSG_TEA1 = 1, /* KSG number value 0 */ 50 | KSG_TEA2 = 2, /* KSG number value 1 */ 51 | KSG_TEA3 = 3, /* KSG number value 2 */ 52 | KSG_TEA4 = 4, /* KSG number value 3 */ 53 | KSG_TEA5 = 5, /* KSG number value 4 */ 54 | KSG_TEA6 = 6, /* KSG number value 5 */ 55 | KSG_TEA7 = 7, /* KSG number value 6 */ 56 | KSG_PROPRIETARY = 8, /* KSG number values 9-15 */ 57 | }; 58 | 59 | enum tetra_security_class { 60 | NETWORK_CLASS_UNDEFINED = 0, 61 | NETWORK_CLASS_1 = 1, 62 | NETWORK_CLASS_2 = 2, 63 | NETWORK_CLASS_3 = 3, 64 | }; 65 | 66 | struct tetra_netinfo { 67 | uint32_t mcc; 68 | uint32_t mnc; 69 | enum tetra_ksg_type ksg_type; /* KSG used in this network */ 70 | enum tetra_security_class security_class; /* Security class this network employs */ 71 | }; 72 | 73 | struct tetra_key { 74 | uint32_t index; /* position in key list */ 75 | uint32_t mnc; 76 | uint32_t mcc; 77 | enum tetra_key_type key_type; 78 | uint32_t key_num; /* key_vn or whatever key numbering system is relevant for this key type */ 79 | uint32_t addr; /* ISSI, GSSI, or whatever address is relevant for this key type */ 80 | uint8_t key[16]; /* Currently keys are 80 bits, but we may want to support 128-bit keys as well */ 81 | struct tetra_netinfo *network_info; /* Network with which the key is associated */ 82 | }; 83 | 84 | struct tetra_crypto_database { 85 | uint32_t num_keys; 86 | struct tetra_key *keys; 87 | uint32_t num_nets; 88 | struct tetra_netinfo *nets; 89 | }; 90 | extern struct tetra_crypto_database *tcdb; 91 | 92 | struct tetra_crypto_state { 93 | int mnc; /* Network info for selecting keys */ 94 | int mcc; /* Network info for selecting keys */ 95 | int cck_id; /* CCK or SCK id used on network (from SYSINFO) */ 96 | int hn; /* Hyperframe number for IV (from SYSINFO) */ 97 | int la; /* location area for TB5 */ 98 | int cn; /* carrier number for TB5. WARNING: only set correctly if tuned to main control channel */ 99 | int cc; /* colour code for TB5 */ 100 | struct tetra_netinfo *network; /* pointer to network info struct loaded from file */ 101 | struct tetra_key *cck; /* pointer to CCK or SCK for this network and version (from SYSINFO) */ 102 | }; 103 | 104 | const char *tetra_get_key_type_name(enum tetra_key_type); 105 | const char *tetra_get_ksg_type_name(enum tetra_ksg_type); 106 | const char *tetra_get_security_class_name(uint8_t pdut); 107 | 108 | /* Key loading / unloading */ 109 | void tetra_crypto_state_init(struct tetra_crypto_state *tcs); 110 | int load_keystore(char *filename); 111 | 112 | /* Keystream generation and decryption functions */ 113 | uint32_t tea_build_iv(struct tetra_tdma_time *tm, uint16_t hn, uint8_t dir); 114 | bool decrypt_identity(struct tetra_crypto_state *tcs, struct tetra_addr *addr); 115 | bool decrypt_mac_element(struct tetra_crypto_state *tcs, struct tetra_tmvsap_prim *tmvp, struct tetra_key *key, int l1_len, int tmpdu_offset); 116 | bool decrypt_voice_timeslot(struct tetra_crypto_state *tcs, struct tetra_tdma_time *tdma_time, int16_t *type1_bits); 117 | 118 | /* Key selection and crypto state management */ 119 | struct tetra_netinfo *get_network_info(int mcc, int mnc); 120 | struct tetra_key *get_ksg_key(struct tetra_crypto_state *tcs, int addr); 121 | void update_current_network(struct tetra_crypto_state *tcs, int mcc, int mnc); 122 | void update_current_cck(struct tetra_crypto_state *tcs); 123 | 124 | char *dump_network_info(struct tetra_netinfo *network); 125 | char *dump_key(struct tetra_key *k); 126 | 127 | #endif /* TETRA_CRYPTO_H */ 128 | -------------------------------------------------------------------------------- /src/tetra_llc.c: -------------------------------------------------------------------------------- 1 | /* TETRA LLC Layer */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "tetra_llc_pdu.h" 31 | #include "tetra_mle.h" 32 | #include "tuntap.h" 33 | 34 | static int tun_fd = -1; 35 | 36 | static struct tllc_state g_llcs = { 37 | .rx.defrag_list = LLIST_HEAD_INIT(g_llcs.rx.defrag_list), 38 | }; 39 | 40 | static struct tllc_defrag_q_e * 41 | get_dqe_for_ns(struct tllc_state *llcs, uint8_t ns, int alloc_if_missing) 42 | { 43 | struct tllc_defrag_q_e *dqe; 44 | llist_for_each_entry(dqe, &llcs->rx.defrag_list, list) { 45 | if (dqe->ns == ns) 46 | return dqe; 47 | } 48 | if (alloc_if_missing) { 49 | dqe = talloc_zero(NULL, struct tllc_defrag_q_e); 50 | dqe->ns = ns; 51 | dqe->tl_sdu = msgb_alloc(4096, "LLC defrag"); 52 | llist_add(&dqe->list, &llcs->rx.defrag_list); 53 | } else 54 | dqe = NULL; 55 | 56 | return dqe; 57 | } 58 | 59 | static int tllc_defrag_in(struct tllc_state *llcs, 60 | struct tetra_llc_pdu *lpp, 61 | struct msgb *msg, unsigned int len) 62 | { 63 | struct tllc_defrag_q_e *dqe; 64 | 65 | dqe = get_dqe_for_ns(llcs, lpp->ns, 1); 66 | 67 | /* check if this is the first segment, or the next 68 | * expected segment */ 69 | if (!dqe->last_ss || 70 | (dqe->last_ss == lpp->ss - 1)) { 71 | /* FIXME: append */ 72 | printf("<> ", lpp->ss); 73 | dqe->last_ss = lpp->ss; 74 | memcpy(msgb_put(dqe->tl_sdu, len), msg->l3h, len); 75 | } else 76 | printf("<> ", dqe->last_ss, lpp->ss); 77 | 78 | return 0; 79 | } 80 | 81 | static int tllc_defrag_out(struct tetra_mac_state *tms, struct tllc_state *llcs, struct tetra_llc_pdu *lpp) 82 | { 83 | struct tllc_defrag_q_e *dqe; 84 | struct msgb *msg; 85 | 86 | dqe = get_dqe_for_ns(llcs, lpp->ns, 0); 87 | msg = dqe->tl_sdu; 88 | 89 | printf("<> "); 90 | msg->l3h = msg->data; 91 | rx_tl_sdu(tms, msg, msgb_l3len(msg)); 92 | 93 | if (tun_fd < 0) { 94 | tun_fd = tun_alloc("tun0"); 95 | fprintf(stderr, "tun_fd=%d\n", tun_fd); 96 | } 97 | if (tun_fd >= 0) { 98 | uint8_t buf[4096]; 99 | int len = osmo_ubit2pbit(buf, msg->l3h+3+4+4+4+4, msgb_l3len(msg)-3-4-4-4-4); 100 | write(tun_fd, buf, len); 101 | } 102 | 103 | llist_del(&dqe->list); 104 | talloc_free(msg); 105 | talloc_free(dqe); 106 | return 0; 107 | } 108 | 109 | /* Receive TM-SDU (MAC SDU == LLC PDU) */ 110 | /* this resembles TMA-UNITDATA.ind (TM-SDU / length) */ 111 | int rx_tm_sdu(struct tetra_mac_state *tms, struct msgb *msg, unsigned int len) 112 | { 113 | if (!len) { 114 | return -1; 115 | } else if (len < 4) { 116 | printf("WARNING rx_tm_sdu: l2len too small: %d\n", len); 117 | return -1; 118 | } 119 | 120 | struct tetra_llc_pdu lpp; 121 | memset(&lpp, 0, sizeof(lpp)); 122 | tetra_llc_pdu_parse(&lpp, msg->l2h, len); 123 | msg->l3h = lpp.tl_sdu; 124 | msg->tail = msg->l3h + lpp.tl_sdu_len; // Strips off FCS (if present) 125 | msg->len = msg->tail - msg->head; 126 | 127 | printf("TM-SDU(%s)", tetra_get_llc_pdut_dec_name(lpp.pdu_type)); 128 | if (lpp.have_fcs) { 129 | printf(" fcs=%s ", (lpp.have_fcs && lpp.fcs_invalid ? "BAD" : "OK")); 130 | } 131 | printf(" l3len=%d", msgb_l3len(msg)); 132 | if (msgb_l3len(msg)) { 133 | printf(" %s", osmo_ubit_dump(msg->l3h, msgb_l3len(msg))); 134 | } 135 | printf("\n"); 136 | 137 | if (!lpp.tl_sdu_len) { 138 | return len; 139 | } 140 | 141 | switch (lpp.pdu_type) { 142 | case TLLC_PDUT_DEC_BL_ADATA: 143 | case TLLC_PDUT_DEC_BL_DATA: 144 | case TLLC_PDUT_DEC_BL_UDATA: 145 | case TLLC_PDUT_DEC_BL_ACK: 146 | case TLLC_PDUT_DEC_AL_SETUP: 147 | case TLLC_PDUT_DEC_AL_ACK: 148 | case TLLC_PDUT_DEC_AL_RNR: 149 | case TLLC_PDUT_DEC_AL_RECONNECT: 150 | case TLLC_PDUT_DEC_AL_DISC: 151 | /* directly hand it to MLE */ 152 | rx_tl_sdu(tms, msg, lpp.tl_sdu_len); 153 | break; 154 | case TLLC_PDUT_DEC_AL_DATA: 155 | case TLLC_PDUT_DEC_AL_UDATA: 156 | case TLLC_PDUT_DEC_ALX_DATA: 157 | case TLLC_PDUT_DEC_ALX_UDATA: 158 | /* input into LLC defragmenter */ 159 | tllc_defrag_in(&g_llcs, &lpp, msg, len); 160 | break; 161 | case TLLC_PDUT_DEC_AL_FINAL: 162 | case TLLC_PDUT_DEC_AL_UFINAL: 163 | case TLLC_PDUT_DEC_ALX_FINAL: 164 | case TLLC_PDUT_DEC_ALX_UFINAL: 165 | /* input into LLC defragmenter */ 166 | tllc_defrag_in(&g_llcs, &lpp, msg, len); 167 | /* check if the fragment is complete and hand it off*/ 168 | tllc_defrag_out(tms, &g_llcs, &lpp); 169 | break; 170 | 171 | case TLLC_PDUT_DEC_UNKNOWN: 172 | case TLLC_PDUT_DEC_ALX_ACK: 173 | case TLLC_PDUT_DEC_ALX_RNR: 174 | /* fixme: unhandled types */ 175 | break; 176 | } 177 | 178 | return len; 179 | } 180 | -------------------------------------------------------------------------------- /src/crypto/tea3.c: -------------------------------------------------------------------------------- 1 | /* TETRA TEA3 keystream generator implementation */ 2 | /* 3 | * Copyright (C) 2023 Midnight Blue B.V. 4 | * 5 | * Author: Wouter Bokslag 6 | * 7 | * SPDX-License-Identifier: AGPL-3.0+ 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * See the COPYING file in the main directory for details. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "tea3.h" 29 | 30 | 31 | const uint16_t g_awTea3LutA[8] = { 0x92A7, 0xA761, 0x974C, 0x6B8C, 0x29CE, 0x176C, 0x39D4, 0x7463 }; 32 | const uint16_t g_awTea3LutB[8] = { 0x9D58, 0xA46D, 0x176C, 0x79C4, 0xC62B, 0xB2C9, 0x4D93, 0x2E93 }; 33 | const uint8_t g_abTea3Sbox[256] = { 34 | 0x7D, 0xBF, 0x7B, 0x92, 0xAE, 0x7C, 0xF2, 0x10, 0x5A, 0x0F, 0x61, 0x7A, 0x98, 0x76, 0x07, 0x64, 35 | 0xEE, 0x89, 0xF7, 0xBA, 0xC2, 0x02, 0x0D, 0xE8, 0x56, 0x2E, 0xCA, 0x58, 0xC0, 0xFA, 0x2A, 0x01, 36 | 0x57, 0x6E, 0x3F, 0x4B, 0x9C, 0xDA, 0xA6, 0x5B, 0x41, 0x26, 0x50, 0x24, 0x3E, 0xF8, 0x0A, 0x86, 37 | 0xB6, 0x5C, 0x34, 0xE9, 0x06, 0x88, 0x1F, 0x39, 0x33, 0xDF, 0xD9, 0x78, 0xD8, 0xA8, 0x51, 0xB2, 38 | 0x09, 0xCD, 0xA1, 0xDD, 0x8E, 0x62, 0x69, 0x4D, 0x23, 0x2B, 0xA9, 0xE1, 0x53, 0x94, 0x90, 0x1E, 39 | 0xB4, 0x3B, 0xF9, 0x4E, 0x36, 0xFE, 0xB5, 0xD1, 0xA2, 0x8D, 0x66, 0xCE, 0xB7, 0xC4, 0x60, 0xED, 40 | 0x96, 0x4F, 0x31, 0x79, 0x35, 0xEB, 0x8F, 0xBB, 0x54, 0x14, 0xCB, 0xDE, 0x6B, 0x2D, 0x19, 0x82, 41 | 0x80, 0xAC, 0x17, 0x05, 0xFF, 0xA4, 0xCF, 0xC6, 0x6F, 0x65, 0xE6, 0x74, 0xC8, 0x93, 0xF4, 0x7E, 42 | 0xF3, 0x43, 0x9F, 0x71, 0xAB, 0x9A, 0x0B, 0x87, 0x55, 0x70, 0x0C, 0xAD, 0xCC, 0xA5, 0x44, 0xE7, 43 | 0x46, 0x45, 0x03, 0x30, 0x1A, 0xEA, 0x67, 0x99, 0xDB, 0x4A, 0x42, 0xD7, 0xAA, 0xE4, 0xC2, 0xD5, 44 | 0xF0, 0x77, 0x20, 0xC3, 0x3C, 0x16, 0xB9, 0xE2, 0xEF, 0x6C, 0x3D, 0x1B, 0x22, 0x84, 0x2F, 0x81, 45 | 0x1D, 0xB1, 0x3A, 0xE5, 0x73, 0x40, 0xD0, 0x18, 0xC7, 0x6A, 0x9E, 0x91, 0x48, 0x27, 0x95, 0x72, 46 | 0x68, 0x0E, 0x00, 0xFC, 0xC5, 0x5F, 0xF1, 0xF5, 0x38, 0x11, 0x7F, 0xE3, 0x5E, 0x13, 0xAF, 0x37, 47 | 0xE0, 0x8A, 0x49, 0x1C, 0x21, 0x47, 0xD4, 0xDC, 0xB0, 0xEC, 0x83, 0x28, 0xB8, 0xF6, 0xA7, 0xC9, 48 | 0x63, 0x59, 0xBD, 0x32, 0x85, 0x08, 0xBE, 0xD3, 0xFD, 0x4C, 0x2C, 0xFB, 0xA0, 0xC1, 0x9D, 0xB3, 49 | 0x52, 0x8C, 0x5D, 0x29, 0x6D, 0x04, 0xBC, 0x25, 0x15, 0x8B, 0x12, 0x9B, 0xD6, 0x75, 0xA3, 0x97 50 | }; 51 | 52 | 53 | static uint64_t tea3_compute_iv(uint32_t dwFrameNumbers) 54 | { 55 | uint32_t dwXorred = dwFrameNumbers ^ 0xC43A7D51; 56 | dwXorred = (dwXorred << 8) | (dwXorred >> 24); 57 | uint64_t qwIv = ((uint64_t)dwFrameNumbers << 32) | dwXorred; 58 | return (qwIv >> 8) | (qwIv << 56); 59 | } 60 | 61 | static uint8_t tea3_state_word_to_newbyte(uint16_t wSt, const uint16_t *awLut) 62 | { 63 | uint8_t bSt0 = wSt; 64 | uint8_t bSt1 = wSt >> 8; 65 | 66 | uint8_t bDist; 67 | uint8_t bOut = 0; 68 | 69 | for (int i = 0; i < 8; i++) { 70 | /* taps on bit 5,6 for bSt0 and bit 5,6 for bSt1 */ 71 | bDist = ((bSt0 >> 5) & 3) | ((bSt1 >> 3) & 12); 72 | if (awLut[i] & (1 << bDist)) 73 | bOut |= 1 << i; 74 | 75 | /* rotate one position */ 76 | bSt0 = ((bSt0 >> 1) | (bSt0 << 7)); 77 | bSt1 = ((bSt1 >> 1) | (bSt1 << 7)); 78 | } 79 | 80 | return bOut; 81 | } 82 | 83 | static uint8_t tea3_reorder_state_byte(uint8_t bStByte) 84 | { 85 | /* simple re-ordering of bits */ 86 | uint8_t bOut = 0; 87 | bOut |= ((bStByte << 6) & 0x40); 88 | bOut |= ((bStByte << 1) & 0x20); 89 | bOut |= ((bStByte << 2) & 0x98); 90 | bOut |= ((bStByte >> 4) & 0x04); 91 | bOut |= ((bStByte >> 3) & 0x01); 92 | bOut |= ((bStByte >> 6) & 0x02); 93 | return bOut; 94 | } 95 | 96 | void tea3(uint32_t dwFrameNumbers, uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut) 97 | { 98 | uint8_t abKeyReg[10]; 99 | uint32_t dwNumSkipRounds = 51; 100 | 101 | /* init registers */ 102 | uint64_t qwIvReg = tea3_compute_iv(dwFrameNumbers); 103 | memcpy(abKeyReg, lpKey, 10); 104 | 105 | for (int i = 0; i < dwNumKsBytes; i++) { 106 | for (int j = 0; j < dwNumSkipRounds; j++) { 107 | /* Step 1: Derive a non-linear feedback byte through sbox and feed back into key register */ 108 | uint8_t bSboxOut = g_abTea3Sbox[abKeyReg[7] ^ abKeyReg[2]] ^ abKeyReg[0]; 109 | memmove(abKeyReg, abKeyReg + 1, 9); 110 | abKeyReg[9] = bSboxOut; 111 | 112 | /* Step 2: Compute 3 bytes derived from current state */ 113 | uint8_t bDerivByte12 = tea3_state_word_to_newbyte((qwIvReg >> 8) & 0xffff, g_awTea3LutA); 114 | uint8_t bDerivByte56 = tea3_state_word_to_newbyte((qwIvReg >> 40) & 0xffff, g_awTea3LutB); 115 | uint8_t bReordByte4 = tea3_reorder_state_byte((qwIvReg >> 32) & 0xff); 116 | 117 | /* Step 3: Combine current state with state derived values, and xor in key derived sbox output */ 118 | uint8_t bNewByte = ((qwIvReg >> 56) ^ bReordByte4 ^ bDerivByte12 ^ bSboxOut) & 0xff; 119 | uint8_t bMixByte = bDerivByte56; 120 | 121 | /* Step 4: Update lfsr: leftshift 8, feed/mix in previously generated bytes */ 122 | qwIvReg = ((qwIvReg << 8) ^ ((uint64_t)bMixByte << 40)) | bNewByte; 123 | } 124 | lpKsOut[i] = (qwIvReg >> 56); 125 | dwNumSkipRounds = 19; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/phy/tetra_burst_sync.c: -------------------------------------------------------------------------------- 1 | /* Implementation of TETRA burst synchronization */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | //#define DEBUG 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | struct tetra_phy_state t_phy_state; 35 | 36 | void tetra_burst_rx_cb(const uint8_t *burst, unsigned int len, enum tetra_train_seq type, void *priv); 37 | 38 | static void make_bitbuf_space(struct tetra_rx_state *trs, unsigned int len) 39 | { 40 | unsigned int bitbuf_space = sizeof(trs->bitbuf) - trs->bits_in_buf; 41 | 42 | if (bitbuf_space < len) { 43 | unsigned int delta = len - bitbuf_space; 44 | 45 | DEBUGP("bitbuf left: %u, shrinking by %u\n", bitbuf_space, delta); 46 | memmove(trs->bitbuf, trs->bitbuf + delta, trs->bits_in_buf - delta); 47 | trs->bits_in_buf -= delta; 48 | trs->bitbuf_start_bitnum += delta; 49 | bitbuf_space = sizeof(trs->bitbuf) - trs->bits_in_buf; 50 | } 51 | } 52 | 53 | /* input a raw bitstream into the tetra burst synchronizaer */ 54 | int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len) 55 | { 56 | int rc; 57 | unsigned int train_seq_offs; 58 | 59 | DEBUGP("burst_sync_in: %u bits, state %u\n", len, trs->state); 60 | 61 | /* First: append the data to the bitbuf */ 62 | make_bitbuf_space(trs, len); 63 | memcpy(trs->bitbuf + trs->bits_in_buf, bits, len); 64 | trs->bits_in_buf += len; 65 | 66 | switch (trs->state) { 67 | case RX_S_UNLOCKED: 68 | if (trs->bits_in_buf < TETRA_BITS_PER_TS*2) { 69 | /* wait for more bits to arrive */ 70 | DEBUGP("-> waiting for more bits to arrive\n"); 71 | return len; 72 | } 73 | DEBUGP("-> trying to find training sequence between bit %u and %u\n", 74 | trs->bitbuf_start_bitnum, trs->bits_in_buf); 75 | rc = tetra_find_train_seq(trs->bitbuf, trs->bits_in_buf, 76 | (1 << TETRA_TRAIN_SYNC), &train_seq_offs); 77 | if (rc < 0) 78 | return rc; 79 | printf("found SYNC training sequence in bit #%u\n", train_seq_offs); 80 | trs->state = RX_S_KNOW_FSTART; 81 | trs->next_frame_start_bitnum = trs->bitbuf_start_bitnum + train_seq_offs + 296; 82 | #if 0 83 | if (train_seq_offs < 214) { 84 | /* not enough leading bits for start of burst */ 85 | /* we just drop everything that we received so far */ 86 | trs->bitbuf_start_bitnum += trs->bits_in_buf; 87 | trs->bits_in_buf = 0; 88 | } 89 | #endif 90 | break; 91 | case RX_S_KNOW_FSTART: 92 | /* we are locked, i.e. already know when the next frame should start */ 93 | if (trs->bitbuf_start_bitnum + trs->bits_in_buf < trs->next_frame_start_bitnum) 94 | return 0; 95 | else { 96 | /* shift start of frame to start of bitbuf */ 97 | int offset = trs->next_frame_start_bitnum - trs->bitbuf_start_bitnum; 98 | int bits_remaining = trs->bits_in_buf - offset; 99 | 100 | memmove(trs->bitbuf, trs->bitbuf+offset, bits_remaining); 101 | trs->bits_in_buf = bits_remaining; 102 | trs->bitbuf_start_bitnum += offset; 103 | 104 | trs->next_frame_start_bitnum += TETRA_BITS_PER_TS; 105 | trs->state = RX_S_LOCKED; 106 | } 107 | case RX_S_LOCKED: 108 | if (trs->bits_in_buf < TETRA_BITS_PER_TS) { 109 | /* not sufficient data for the full frame yet */ 110 | return len; 111 | } else { 112 | /* we have successfully received (at least) one frame */ 113 | tetra_tdma_time_add_tn(&t_phy_state.time, 1); 114 | printf("\nBURST"); 115 | DEBUGP(": %s", osmo_ubit_dump(trs->bitbuf, TETRA_BITS_PER_TS)); 116 | printf("\n"); 117 | rc = tetra_find_train_seq(trs->bitbuf, trs->bits_in_buf, 118 | (1 << TETRA_TRAIN_NORM_1)| 119 | (1 << TETRA_TRAIN_NORM_2)| 120 | (1 << TETRA_TRAIN_SYNC), &train_seq_offs); 121 | switch (rc) { 122 | case TETRA_TRAIN_SYNC: 123 | if (train_seq_offs == 214) 124 | tetra_burst_rx_cb(trs->bitbuf, TETRA_BITS_PER_TS, rc, trs->burst_cb_priv); 125 | else { 126 | fprintf(stderr, "#### SYNC burst at offset %u?!?\n", train_seq_offs); 127 | trs->state = RX_S_UNLOCKED; 128 | } 129 | break; 130 | case TETRA_TRAIN_NORM_1: 131 | case TETRA_TRAIN_NORM_2: 132 | case TETRA_TRAIN_NORM_3: 133 | if (train_seq_offs == 244) 134 | tetra_burst_rx_cb(trs->bitbuf, TETRA_BITS_PER_TS, rc, trs->burst_cb_priv); 135 | else 136 | fprintf(stderr, "#### SYNC burst at offset %u?!?\n", train_seq_offs); 137 | break; 138 | default: 139 | fprintf(stderr, "#### could not find successive burst training sequence\n"); 140 | trs->state = RX_S_UNLOCKED; 141 | break; 142 | } 143 | 144 | /* move remainder to start of buffer */ 145 | trs->bits_in_buf -= TETRA_BITS_PER_TS; 146 | memmove(trs->bitbuf, trs->bitbuf+TETRA_BITS_PER_TS, trs->bits_in_buf); 147 | trs->bitbuf_start_bitnum += TETRA_BITS_PER_TS; 148 | trs->next_frame_start_bitnum += TETRA_BITS_PER_TS; 149 | } 150 | break; 151 | 152 | } 153 | return len; 154 | } 155 | -------------------------------------------------------------------------------- /src/crypto/tea2.c: -------------------------------------------------------------------------------- 1 | /* TETRA TEA2 keystream generator implementation */ 2 | /* 3 | * Copyright (C) 2023 Midnight Blue B.V. 4 | * 5 | * Author: Wouter Bokslag 6 | * 7 | * SPDX-License-Identifier: AGPL-3.0+ 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * See the COPYING file in the main directory for details. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "tea2.h" 29 | 30 | 31 | const uint16_t g_abTea2LutA[8] = { 0x2579, 0x86E5, 0xB6C8, 0x31D6, 0x7394, 0x934D, 0x638E, 0xC68B }; 32 | const uint16_t g_abTea2LutB[8] = { 0xD68A, 0x97A1, 0xB2C9, 0x239E, 0x9C71, 0x36E8, 0xC9B2, 0x6CD1 }; 33 | const uint8_t g_abTea2Sbox[256] = { 34 | 0x62, 0xDA, 0xFD, 0xB6, 0xBB, 0x9C, 0xD8, 0x2A, 0xAB, 0x28, 0x6E, 0x42, 0xE7, 0x1C, 0x78, 0x9E, 35 | 0xFC, 0xCA, 0x81, 0x8E, 0x32, 0x3B, 0xB4, 0xEF, 0x9F, 0x8B, 0xDB, 0x94, 0x0F, 0x9A, 0xA2, 0x96, 36 | 0x1B, 0x7A, 0xFF, 0xAA, 0xC5, 0xD6, 0xBC, 0x24, 0xDF, 0x44, 0x03, 0x09, 0x0B, 0x57, 0x90, 0xBA, 37 | 0x7F, 0x1F, 0xCF, 0x71, 0x98, 0x07, 0xF8, 0xA1, 0x60, 0xF7, 0x52, 0x8D, 0xE5, 0xD7, 0x69, 0x87, 38 | 0x14, 0xED, 0x92, 0xEB, 0xB3, 0x2F, 0xE9, 0x3D, 0xC6, 0x50, 0x5A, 0xA7, 0x45, 0x18, 0x11, 0xC4, 39 | 0xCE, 0xAC, 0xF4, 0x1D, 0x82, 0x54, 0x3E, 0x49, 0xD5, 0xEE, 0x84, 0x35, 0x41, 0x3A, 0xEC, 0x34, 40 | 0x17, 0xE0, 0xC9, 0xFE, 0xE8, 0xCB, 0xE6, 0xAE, 0x68, 0xE2, 0x6B, 0x46, 0xC8, 0x47, 0xB2, 0xE3, 41 | 0x97, 0x10, 0x0E, 0xB8, 0x76, 0x5B, 0xBE, 0xF5, 0xA6, 0x3C, 0x8F, 0xF6, 0xD1, 0xAF, 0xC0, 0x5E, 42 | 0x7E, 0xCD, 0x7C, 0x51, 0x6D, 0x74, 0x2C, 0x16, 0xF2, 0xA5, 0x65, 0x64, 0x58, 0x72, 0x1E, 0xF1, 43 | 0x04, 0xA8, 0x13, 0x53, 0x31, 0xB1, 0x20, 0xD3, 0x75, 0x5F, 0xA4, 0x56, 0x06, 0x8A, 0x8C, 0xD9, 44 | 0x70, 0x12, 0x29, 0x61, 0x4F, 0x4C, 0x15, 0x05, 0xD2, 0xBD, 0x7D, 0x9B, 0x99, 0x83, 0x2B, 0x25, 45 | 0xD0, 0x23, 0x48, 0x3F, 0xB0, 0x2E, 0x0D, 0x0C, 0xC7, 0xCC, 0xB7, 0x5C, 0xF0, 0xBF, 0x2D, 0x4E, 46 | 0x40, 0x39, 0x9D, 0x21, 0x37, 0x77, 0x73, 0x4B, 0x4D, 0x5D, 0xFA, 0xDE, 0x00, 0x80, 0x85, 0x6F, 47 | 0x22, 0x91, 0xDC, 0x26, 0x38, 0xE4, 0x4A, 0x79, 0x6A, 0x67, 0x93, 0xF3, 0xFB, 0x19, 0xA0, 0x7B, 48 | 0xF9, 0x95, 0x89, 0x66, 0xB9, 0xD4, 0xC1, 0xDD, 0x63, 0x33, 0xE1, 0xC3, 0xB5, 0xA3, 0xC2, 0x27, 49 | 0x0A, 0x88, 0xA9, 0x1A, 0x6C, 0x43, 0xEA, 0xAD, 0x30, 0x86, 0x36, 0x59, 0x08, 0x55, 0x01, 0x02 50 | }; 51 | 52 | 53 | static uint64_t tea2_expand_iv(uint32_t dwFrameNumbers) 54 | { 55 | uint32_t dwXorred = dwFrameNumbers ^ 0x5A6E3278; 56 | dwXorred = (dwXorred << 8) | (dwXorred >> 24); 57 | uint64_t qwIv = ((uint64_t)dwFrameNumbers << 32) | dwXorred; 58 | return (qwIv >> 8) | (qwIv << 56); 59 | } 60 | 61 | static uint8_t tea2_state_word_to_newbyte(uint16_t wSt, const uint16_t *awLut) 62 | { 63 | 64 | uint8_t bSt0 = wSt; 65 | uint8_t bSt1 = wSt >> 8; 66 | uint8_t bDist; 67 | uint8_t bOut = 0; 68 | 69 | for (int i = 0; i < 8; i++) { 70 | /* taps on bit 0,2 for bSt0 and bit 0,7 for bSt1 */ 71 | bDist = ((bSt0 >> 1) & 0x1) | ((bSt0 >> 1) & 0x2) | ((bSt1 >> 5) & 0x4) | ((bSt1 << 3) & 0x8); 72 | if (awLut[i] & (1 << bDist)) 73 | bOut |= 1 << i; 74 | 75 | /* rotate one position */ 76 | bSt0 = ((bSt0 >> 1) | (bSt0 << 7)); 77 | bSt1 = ((bSt1 >> 1) | (bSt1 << 7)); 78 | } 79 | 80 | return bOut; 81 | } 82 | 83 | static uint8_t tea2_reorder_state_byte(uint8_t bStByte) 84 | { 85 | /* simple re-ordering of bits */ 86 | uint8_t bOut = 0; 87 | bOut |= ((bStByte << 6) & 0x40); 88 | bOut |= ((bStByte << 3) & 0x10); 89 | bOut |= ((bStByte >> 2) & 0x01); 90 | bOut |= ((bStByte << 2) & 0x20); 91 | bOut |= ((bStByte << 3) & 0x80); 92 | bOut |= ((bStByte >> 4) & 0x02); 93 | bOut |= ((bStByte >> 3) & 0x08); 94 | bOut |= ((bStByte >> 5) & 0x04); 95 | return bOut; 96 | } 97 | 98 | void tea2(uint32_t dwFrameNumbers, uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut) 99 | { 100 | uint8_t abKeyReg[10]; 101 | uint32_t dwNumSkipRounds = 51; 102 | 103 | /* init registers */ 104 | uint64_t qwIvReg = tea2_expand_iv(dwFrameNumbers); 105 | memcpy(abKeyReg, lpKey, 10); 106 | 107 | for (int i = 0; i < dwNumKsBytes; i++) { 108 | for (int j = 0; j < dwNumSkipRounds; j++) { 109 | /* Step 1: Derive a non-linear feedback byte through sbox and feed back into key register */ 110 | uint8_t bSboxOut = g_abTea2Sbox[abKeyReg[0] ^ abKeyReg[7]]; 111 | memmove(abKeyReg, abKeyReg + 1, 9); 112 | abKeyReg[9] = bSboxOut; 113 | 114 | /* Step 2: Compute 3 bytes derived from current state */ 115 | uint8_t bDerivByte01 = tea2_state_word_to_newbyte((qwIvReg >> 0) & 0xffff, g_abTea2LutA); 116 | uint8_t bDerivByte34 = tea2_state_word_to_newbyte((qwIvReg >> 24) & 0xffff, g_abTea2LutB); 117 | uint8_t bReordByte5 = tea2_reorder_state_byte((qwIvReg >> 40) & 0xff); 118 | 119 | /* Step 3: Combine current state with state derived values, and xor in key derived sbox output */ 120 | uint8_t bNewByte = ((qwIvReg >> 56) ^ (qwIvReg >> 16) ^ bReordByte5 ^ bDerivByte01 ^ bSboxOut) & 0xff; 121 | uint8_t bMixByte = bDerivByte34; 122 | 123 | /* Step 4: Update lfsr: leftshift 8, feed/mix in previously generated bytes */ 124 | qwIvReg = ((qwIvReg << 8) ^ ((uint64_t)bMixByte << 24)) | bNewByte; 125 | } 126 | 127 | lpKsOut[i] = qwIvReg >> 56; 128 | dwNumSkipRounds = 19; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /etsi_codec-patches/log_stderr.patch: -------------------------------------------------------------------------------- 1 | diff -ur codec.orig/c-code/cdecoder.c codec.patched/c-code/cdecoder.c 2 | --- codec.orig/c-code/cdecoder.c 1995-06-06 11:32:14.000000000 +0200 3 | +++ codec.patched/c-code/cdecoder.c 2014-11-18 22:53:06.000000000 +0100 4 | @@ -67,23 +67,23 @@ 5 | /* Parse arguments */ 6 | if ( argc != 3 ) 7 | { 8 | - puts( "usage : cdecoder input_file output_file" ); 9 | - puts( "format for input_file : $6B21...114 bits"); 10 | - puts( " ...$6B22...114..." ); 11 | - puts( " ...$6B26...114...$6B21"); 12 | - puts( "format for output_file : two 138 (BFI + 137) bit frames"); 13 | + fputs("usage : cdecoder input_file output_file",stderr ); 14 | + fputs("format for input_file : $6B21...114 bits",stderr); 15 | + fputs(" ...$6B22...114...",stderr ); 16 | + fputs(" ...$6B26...114...$6B21",stderr); 17 | + fputs("format for output_file : two 138 (BFI + 137) bit frames",stderr); 18 | exit( 1 ); 19 | } 20 | 21 | if ( (fin = fopen( argv[1], "rb" )) == NULL ) 22 | { 23 | - puts("cdecoder: can't open input_file" ); 24 | + fputs("cdecoder: can't open input_file" ,stderr); 25 | exit( 1 ); 26 | } 27 | 28 | if ( (fout = fopen( argv[2], "wb" )) == NULL ) 29 | { 30 | - puts("cdecoder: can't open output_file" ); 31 | + fputs("cdecoder: can't open output_file",stderr ); 32 | exit( 1 ); 33 | } 34 | 35 | @@ -93,7 +93,7 @@ 36 | /* read Input_array (1 TETRA frame = 2 speech frames) from input file */ 37 | if (Read_Tetra_File (fin, Interleaved_coded_array) == -1) 38 | { 39 | - puts ("cdecoder: reached end of input_file"); 40 | + fputs ("cdecoder: reached end of input_file",stderr); 41 | break; 42 | } 43 | 44 | @@ -116,7 +116,7 @@ 45 | in "Coded_array" */ 46 | 47 | /* Message in case the Frame was stolen */ 48 | - if (bfi1) printf("Frame Nb %ld was stolen\n",Loop_counter+1); 49 | + if (bfi1) fprintf(stderr,"Frame Nb %ld was stolen\n",Loop_counter+1); 50 | 51 | /* Channel Decoding */ 52 | bfi2 = Channel_Decoding(first_pass,Frame_stealing, 53 | @@ -126,35 +126,35 @@ 54 | /* Increment Loop counter */ 55 | Loop_counter++; 56 | /* Message in case the Bad Frame Indicator was set */ 57 | - if (bfi2) printf("Frame Nb %ld Bfi active\n\n",Loop_counter); 58 | + if (bfi2) fprintf(stderr,"Frame Nb %ld Bfi active\n\n",Loop_counter); 59 | 60 | /* writing Reordered_array to output file */ 61 | /* bfi bit */ 62 | if( fwrite( &bfi1, sizeof(short), 1, fout ) != 1 ) { 63 | - puts( "cdecoder: can't write to output_file" ); 64 | + fputs( "cdecoder: can't write to output_file",stderr ); 65 | break; 66 | } 67 | /* 1st speech frame */ 68 | if( fwrite( Reordered_array, sizeof(short), 137, fout ) != 137 ) 69 | { 70 | - puts( "cdecoder: can't write to output_file" ); 71 | + fputs("cdecoder: can't write to output_file",stderr ); 72 | break; 73 | } 74 | /* bfi bit */ 75 | if( fwrite( &bfi2, sizeof(short), 1, fout ) != 1 ) { 76 | - puts( "cdecoder: can't write to output_file" ); 77 | + fputs("cdecoder: can't write to output_file",stderr ); 78 | break; 79 | } 80 | /* 2nd speech frame */ 81 | if( fwrite( Reordered_array+137, sizeof(short), 137, fout ) 82 | != 137 ) { 83 | - puts( "cdecoder: can't write to output_file" ); 84 | + fputs("cdecoder: can't write to output_file",stderr ); 85 | break; 86 | } 87 | } 88 | 89 | - printf("%ld Channel Frames processed\n",Loop_counter); 90 | - printf("ie %ld Speech Frames\n",2*Loop_counter); 91 | + fprintf(stderr,"%ld Channel Frames processed\n",Loop_counter); 92 | + fprintf(stderr,"ie %ld Speech Frames\n",2*Loop_counter); 93 | 94 | /* closing files */ 95 | fclose( fin ); 96 | diff -ur codec.orig/c-code/sdecoder.c codec.patched/c-code/sdecoder.c 97 | --- codec.orig/c-code/sdecoder.c 1995-06-06 10:55:28.000000000 +0200 98 | +++ codec.patched/c-code/sdecoder.c 2014-11-18 22:53:06.000000000 +0100 99 | @@ -58,15 +58,15 @@ 100 | 101 | if ( argc != 3 ) 102 | { 103 | - printf("Usage : sdecoder serial_file synth_file\n"); 104 | - printf("\n"); 105 | - printf("Format for serial_file:\n"); 106 | - printf(" Serial stream input is read from a binary file\n"); 107 | - printf(" where each 16-bit word represents 1 encoded bit.\n"); 108 | - printf(" BFI + 137 bits by frame\n"); 109 | - printf("\n"); 110 | - printf("Format for synth_file:\n"); 111 | - printf(" Synthesis is written to a binary file of 16 bits data.\n"); 112 | + fprintf(stderr,"Usage : sdecoder serial_file synth_file\n"); 113 | + fprintf(stderr,"\n"); 114 | + fprintf(stderr,"Format for serial_file:\n"); 115 | + fprintf(stderr," Serial stream input is read from a binary file\n"); 116 | + fprintf(stderr," where each 16-bit word represents 1 encoded bit.\n"); 117 | + fprintf(stderr," BFI + 137 bits by frame\n"); 118 | + fprintf(stderr,"\n"); 119 | + fprintf(stderr,"Format for synth_file:\n"); 120 | + fprintf(stderr," Synthesis is written to a binary file of 16 bits data.\n"); 121 | exit( 1 ); 122 | } 123 | 124 | @@ -74,13 +74,13 @@ 125 | 126 | if( (f_serial = fopen(argv[1],"rb") ) == NULL ) 127 | { 128 | - printf("Input file '%s' does not exist !!\n",argv[1]); 129 | + fprintf(stderr,"Input file '%s' does not exist !!\n",argv[1]); 130 | exit(0); 131 | } 132 | 133 | if( (f_syn = fopen(argv[2], "wb") ) == NULL ) 134 | { 135 | - printf("Cannot open file '%s' !!\n", argv[2]); 136 | + fprintf(stderr,"Cannot open file '%s' !!\n", argv[2]); 137 | exit(0); 138 | } 139 | 140 | @@ -95,7 +95,7 @@ 141 | 142 | while( fread(serial, sizeof(Word16), serial_size, f_serial) == serial_size) 143 | { 144 | - printf("frame=%d\n", ++frame); 145 | + fprintf(stderr,"frame=%d\n", ++frame); 146 | 147 | Bits2prm_Tetra(serial, parm); /* serial to parameters */ 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/crypto/tea1.c: -------------------------------------------------------------------------------- 1 | /* TETRA TEA1 keystream generator implementation */ 2 | /* 3 | * Copyright (C) 2023 Midnight Blue B.V. 4 | * 5 | * Author: Wouter Bokslag 6 | * 7 | * SPDX-License-Identifier: AGPL-3.0+ 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Affero General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Affero General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Affero General Public License 20 | * along with this program. If not, see . 21 | * See the COPYING file in the main directory for details. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "tea1.h" 29 | 30 | 31 | const uint16_t g_awTea1LutA[8] = { 0xDA86, 0x85E9, 0x29B5, 0x2BC6, 0x8C6B, 0x974C, 0xC671, 0x93E2 }; 32 | const uint16_t g_awTea1LutB[8] = { 0x85D6, 0x791A, 0xE985, 0xC671, 0x2B9C, 0xEC92, 0xC62B, 0x9C47 }; 33 | const uint8_t g_abTea1Sbox[256] = { 34 | 0x9B, 0xF8, 0x3B, 0x72, 0x75, 0x62, 0x88, 0x22, 0xFF, 0xA6, 0x10, 0x4D, 0xA9, 0x97, 0xC3, 0x7B, 35 | 0x9F, 0x78, 0xF3, 0xB6, 0xA0, 0xCC, 0x17, 0xAB, 0x4A, 0x41, 0x8D, 0x89, 0x25, 0x87, 0xD3, 0xE3, 36 | 0xCE, 0x47, 0x35, 0x2C, 0x6D, 0xFC, 0xE7, 0x6A, 0xB8, 0xB7, 0xFA, 0x8B, 0xCD, 0x74, 0xEE, 0x11, 37 | 0x23, 0xDE, 0x39, 0x6C, 0x1E, 0x8E, 0xED, 0x30, 0x73, 0xBE, 0xBB, 0x91, 0xCA, 0x69, 0x60, 0x49, 38 | 0x5F, 0xB9, 0xC0, 0x06, 0x34, 0x2A, 0x63, 0x4B, 0x90, 0x28, 0xAC, 0x50, 0xE4, 0x6F, 0x36, 0xB0, 39 | 0xA4, 0xD2, 0xD4, 0x96, 0xD5, 0xC9, 0x66, 0x45, 0xC5, 0x55, 0xDD, 0xB2, 0xA1, 0xA8, 0xBF, 0x37, 40 | 0x32, 0x2B, 0x3E, 0xB5, 0x5C, 0x54, 0x67, 0x92, 0x56, 0x4C, 0x20, 0x6B, 0x42, 0x9D, 0xA7, 0x58, 41 | 0x0E, 0x52, 0x68, 0x95, 0x09, 0x7F, 0x59, 0x9C, 0x65, 0xB1, 0x64, 0x5E, 0x4F, 0xBA, 0x81, 0x1C, 42 | 0xC2, 0x0C, 0x02, 0xB4, 0x31, 0x5B, 0xFD, 0x1D, 0x0A, 0xC8, 0x19, 0x8F, 0x83, 0x8A, 0xCF, 0x33, 43 | 0x9E, 0x3A, 0x80, 0xF2, 0xF9, 0x76, 0x26, 0x44, 0xF1, 0xE2, 0xC4, 0xF5, 0xD6, 0x51, 0x46, 0x07, 44 | 0x14, 0x61, 0xF4, 0xC1, 0x24, 0x7A, 0x94, 0x27, 0x00, 0xFB, 0x04, 0xDF, 0x1F, 0x93, 0x71, 0x53, 45 | 0xEA, 0xD8, 0xBD, 0x3D, 0xD0, 0x79, 0xE6, 0x7E, 0x4E, 0x9A, 0xD7, 0x98, 0x1B, 0x05, 0xAE, 0x03, 46 | 0xC7, 0xBC, 0x86, 0xDB, 0x84, 0xE8, 0xD1, 0xF7, 0x16, 0x21, 0x6E, 0xE5, 0xCB, 0xA3, 0x1A, 0xEC, 47 | 0xA2, 0x7D, 0x18, 0x85, 0x48, 0xDA, 0xAA, 0xF0, 0x08, 0xC6, 0x40, 0xAD, 0x57, 0x0D, 0x29, 0x82, 48 | 0x7C, 0xE9, 0x8C, 0xFE, 0xDC, 0x0F, 0x2D, 0x3C, 0x2E, 0xF6, 0x15, 0x2F, 0xAF, 0xE1, 0xEB, 0x3F, 49 | 0x99, 0x43, 0x13, 0x0B, 0xE0, 0xA5, 0x12, 0x77, 0x5D, 0xB3, 0x38, 0xD9, 0xEF, 0x5A, 0x01, 0x70}; 50 | 51 | uint64_t tea1_expand_iv(uint32_t dwShortIv) 52 | { 53 | uint32_t dwXorred = dwShortIv ^ 0x96724FA1; 54 | dwXorred = (dwXorred << 8) | (dwXorred >> 24); 55 | uint64_t qwIv = ((uint64_t)dwShortIv << 32) | dwXorred; 56 | return (qwIv >> 8) | (qwIv << 56); 57 | } 58 | 59 | uint8_t tea1_state_word_to_newbyte(uint16_t wSt, const uint16_t *awLut) 60 | { 61 | uint8_t bSt0 = wSt; 62 | uint8_t bSt1 = wSt >> 8; 63 | uint8_t bDist; 64 | uint8_t bOut = 0; 65 | 66 | for (int i = 0; i < 8; i++) { 67 | /* taps on bit 7,0 for bSt0 and bit 1,2 for bSt1 */ 68 | bDist = ((bSt0 >> 7) & 1) | ((bSt0 << 1) & 2) | ((bSt1 << 1) & 12); 69 | if (awLut[i] & (1 << bDist)) 70 | bOut |= 1 << i; 71 | 72 | /* rotate one position */ 73 | bSt0 = ((bSt0 >> 1) | (bSt0 << 7)); 74 | bSt1 = ((bSt1 >> 1) | (bSt1 << 7)); 75 | } 76 | 77 | return bOut; 78 | } 79 | 80 | uint8_t tea1_reorder_state_byte(uint8_t bStByte) 81 | { 82 | /* simple re-ordering of bits */ 83 | uint8_t bOut = 0; 84 | bOut |= ((bStByte << 6) & 0x40); 85 | bOut |= ((bStByte << 1) & 0x20); 86 | bOut |= ((bStByte << 2) & 0x08); 87 | bOut |= ((bStByte >> 3) & 0x14); 88 | bOut |= ((bStByte >> 2) & 0x01); 89 | bOut |= ((bStByte >> 5) & 0x02); 90 | bOut |= ((bStByte << 4) & 0x80); 91 | return bOut; 92 | } 93 | 94 | int32_t tea1_init_key_register(const uint8_t *lpKey) 95 | { 96 | int32_t dwResult = 0; 97 | for (int i = 0; i < 10; i++) 98 | dwResult = (dwResult << 8) | g_abTea1Sbox[((dwResult >> 24) ^ lpKey[i] ^ dwResult) & 0xff]; 99 | 100 | return dwResult; 101 | } 102 | 103 | void tea1_inner(uint64_t qwIvReg, uint32_t dwKeyReg, uint32_t dwNumKsBytes, uint8_t *lpKsOut) 104 | { 105 | uint32_t dwNumSkipRounds = 54; 106 | 107 | for (int i = 0; i < dwNumKsBytes; i++) { 108 | for (int j = 0; j < dwNumSkipRounds; j++) { 109 | /* Step 1: Derive a non-linear feedback byte through sbox and feed back into key register */ 110 | uint8_t bSboxOut = g_abTea1Sbox[((dwKeyReg >> 24) ^ dwKeyReg) & 0xff]; 111 | dwKeyReg = (dwKeyReg << 8) | bSboxOut; 112 | 113 | /* Step 2: Compute 3 bytes derived from current state */ 114 | uint8_t bDerivByte12 = tea1_state_word_to_newbyte((qwIvReg >> 8) & 0xffff, g_awTea1LutA); 115 | uint8_t bDerivByte56 = tea1_state_word_to_newbyte((qwIvReg >> 40) & 0xffff, g_awTea1LutB); 116 | uint8_t bReordByte4 = tea1_reorder_state_byte((qwIvReg >> 32) & 0xff); 117 | 118 | /* Step 3: Combine current state with state derived values, and xor in key derived sbox output */ 119 | uint8_t bNewByte = (bDerivByte56 ^ (qwIvReg >> 56) ^ bReordByte4 ^ bSboxOut) & 0xff; 120 | uint8_t bMixByte = bDerivByte12; 121 | 122 | /* Step 4: Update lfsr: leftshift 8, feed/mix in previously generated bytes */ 123 | qwIvReg = ((qwIvReg << 8) ^ ((uint64_t)bMixByte << 32)) | bNewByte; 124 | } 125 | 126 | lpKsOut[i] = (qwIvReg >> 56); 127 | dwNumSkipRounds = 19; 128 | } 129 | } 130 | 131 | void tea1(uint32_t dwFrameNumbers, const uint8_t *lpKey, uint32_t dwNumKsBytes, uint8_t *lpKsOut) 132 | { 133 | /* Initialize IV and key register */ 134 | uint64_t qwIvReg = tea1_expand_iv(dwFrameNumbers); 135 | uint32_t dwKeyReg = tea1_init_key_register(lpKey); 136 | 137 | /* Invoke actual TEA1 core function */ 138 | tea1_inner(qwIvReg, dwKeyReg, dwNumKsBytes, lpKsOut); 139 | } 140 | -------------------------------------------------------------------------------- /src/tetra_mac_pdu.h: -------------------------------------------------------------------------------- 1 | #ifndef TETRA_MAC_PDU 2 | #define TETRA_MAC_PDU 3 | 4 | #define MACPDU_LEN_2ND_STOLEN -2 5 | #define MACPDU_LEN_START_FRAG -1 6 | 7 | enum tetra_mac_pdu_types { 8 | TETRA_PDU_T_MAC_RESOURCE = 0, 9 | TETRA_PDU_T_MAC_FRAG_END = 1, 10 | TETRA_PDU_T_BROADCAST = 2, 11 | TETRA_PDU_T_MAC_SUPPL = 3, 12 | }; 13 | 14 | enum tetra_mac_frage_pdu_types { 15 | TETRA_MAC_FRAGE_FRAG = 0, 16 | TETRA_MAC_FRAGE_END = 1, 17 | }; 18 | 19 | enum tetra_mac_bcast_pdu_types { 20 | TETRA_MAC_BC_SYSINFO = 0, 21 | TETRA_MAC_BC_ACCESS_DEFINE = 1, 22 | }; 23 | 24 | enum tetra_mac_supp_pdu_types { 25 | TETRA_MAC_SUPP_D_BLCK = 0, 26 | }; 27 | 28 | enum tetra_bs_serv_details { 29 | BS_SERVDET_REG_RQD = (1 << 11), 30 | BS_SERVDET_DEREG_RQD = (1 << 10), 31 | BS_SERVDET_PRIO_CELL = (1 << 9), 32 | BS_SERVDET_MIN_MODE = (1 << 8), 33 | BS_SERVDET_MIGRATION = (1 << 7), 34 | BS_SERVDET_SYS_W_SERV = (1 << 6), 35 | BS_SERVDET_VOICE_SERV = (1 << 5), 36 | BS_SERVDET_CSD_SERV = (1 << 4), 37 | BS_SERVDET_SNDCP_SERV = (1 << 2), 38 | BS_SERVDET_AIR_ENCR = (1 << 1), 39 | BS_SERVDET_ADV_LINK = (1 << 0), 40 | }; 41 | 42 | enum tetra_mac_optional_field_flags { 43 | TETRA_MAC_OPT_FIELD_EVEN_MULTIFRAME = 0, 44 | TETRA_MAC_OPT_FIELD_ODD_MULTIFRAME = 1, 45 | TETRA_MAC_OPT_FIELD_ACCESS_CODE = 2, 46 | TETRA_MAC_OPT_FIELD_EXT_SERVICES = 3 47 | }; 48 | 49 | const char *tetra_get_bs_serv_det_name(uint32_t pdu_type); 50 | 51 | struct tetra_mle_si_decoded { 52 | uint16_t la; 53 | uint16_t subscr_class; 54 | uint16_t bs_service_details; 55 | }; 56 | 57 | struct tetra_si_decoded { 58 | uint16_t main_carrier; 59 | uint8_t freq_band; 60 | uint8_t freq_offset; 61 | uint8_t duplex_spacing; 62 | uint8_t reverse_operation; 63 | uint8_t num_of_csch; 64 | uint8_t ms_txpwr_max_cell; 65 | uint8_t rxlev_access_min; 66 | uint8_t access_parameter; 67 | uint8_t radio_dl_timeout; 68 | int cck_valid_no_hf; 69 | union { 70 | uint16_t cck_id; 71 | uint16_t hyperframe_number; 72 | }; 73 | uint8_t option_field; 74 | union { 75 | uint32_t frame_bitmap; 76 | uint32_t access_code; 77 | uint32_t ext_service; 78 | }; 79 | struct tetra_mle_si_decoded mle_si; 80 | }; 81 | 82 | const char *tetra_get_macpdu_name(uint8_t pdu_type); 83 | 84 | void macpdu_decode_sysinfo(struct tetra_si_decoded *sid, const uint8_t *si_bits); 85 | 86 | 87 | /* Section 21.4.7.2 ACCESS-ASSIGN PDU */ 88 | enum tetra_acc_ass_hdr { 89 | TETRA_ACC_ASS_DLCC_ULCO, 90 | TETRA_ACC_ASS_DLF1_ULCA, 91 | TETRA_ACC_ASS_DLF1_ULAO, 92 | TETRA_ACC_ASS_DLF1_ULF1, 93 | }; 94 | 95 | enum tetra_acc_ass_hdr_f18 { 96 | TETRA_ACC_ASS_ULCO, 97 | TETRA_ACC_ASS_ULCA, 98 | TETRA_ACC_ASS_ULAO, 99 | TETRA_ACC_ASS_ULCA2, 100 | }; 101 | 102 | enum tetra_dl_usage { 103 | TETRA_DL_US_UNALLOC = 0, 104 | TETRA_DL_US_ASS_CTRL = 1, 105 | TETRA_DL_US_COM_CTRL = 2, 106 | TETRA_DL_US_RESERVED = 3, 107 | TETRA_DL_US_TRAFFIC, 108 | }; 109 | 110 | enum tetra_ul_usage { 111 | TETRA_UL_US_UNALLOC = 0, 112 | TETRA_UL_US_TRAFFIC, 113 | }; 114 | 115 | /* Section 21.5.1 */ 116 | enum tetra_access_field_bf_len { 117 | TETRA_ACC_BFL_RES_SUBS = 0, 118 | TETRA_ACC_BFL_CLCH_SUBS = 1, 119 | TETRA_ACC_BFL_ONGOING = 2, 120 | TETRA_ACC_BFL_1 = 3, 121 | TETRA_ACC_BFL_2 = 4, 122 | TETRA_ACC_BFL_3 = 5, 123 | TETRA_ACC_BFL_4 = 7, 124 | TETRA_ACC_BFL_5 = 6, 125 | TETRA_ACC_BFL_6 = 8, 126 | TETRA_ACC_BFL_8 = 9, 127 | TETRA_ACC_BFL_10 = 0xa, 128 | TETRA_ACC_BFL_12 = 0xb, 129 | TETRA_ACC_BFL_16 = 0xc, 130 | TETRA_ACC_BFL_20 = 0xd, 131 | TETRA_ACC_BFL_24 = 0xe, 132 | TETRA_ACC_BFL_32 = 0xf, 133 | }; 134 | 135 | struct tetra_access_field { 136 | uint8_t access_code; 137 | enum tetra_access_field_bf_len base_frame_len; 138 | }; 139 | 140 | enum tetra_acc_ass_pres { 141 | TETRA_ACC_ASS_PRES_ACCESS1 = (1 << 0), 142 | TETRA_ACC_ASS_PRES_ACCESS2 = (1 << 1), 143 | TETRA_ACC_ASS_PRES_DL_USAGE = (1 << 2), 144 | TETRA_ACC_ASS_PRES_UL_USAGE = (1 << 3), 145 | }; 146 | 147 | struct tetra_acc_ass_decoded { 148 | uint8_t hdr; 149 | uint32_t pres; /* which of the fields below are present */ 150 | 151 | enum tetra_ul_usage ul_usage; 152 | enum tetra_dl_usage dl_usage; 153 | struct tetra_access_field access[2]; 154 | }; 155 | 156 | void macpdu_decode_access_assign(struct tetra_acc_ass_decoded *aad, const uint8_t *bits, int f18); 157 | const char *tetra_get_dl_usage_name(uint8_t num); 158 | const char *tetra_get_ul_usage_name(uint8_t num); 159 | 160 | enum tetra_mac_res_addr_type { 161 | ADDR_TYPE_NULL = 0, 162 | ADDR_TYPE_SSI = 1, 163 | ADDR_TYPE_EVENT_LABEL = 2, 164 | ADDR_TYPE_USSI = 3, 165 | ADDR_TYPE_SMI = 4, 166 | ADDR_TYPE_SSI_EVENT = 5, 167 | ADDR_TYPE_SSI_USAGE = 6, 168 | ADDR_TYPE_SMI_EVENT = 7, 169 | }; 170 | const char *tetra_get_addr_t_name(uint8_t addrt); 171 | 172 | enum tetra_mac_alloc_type { 173 | TMAC_ALLOC_T_REPLACE = 0, 174 | TMAC_ALLOC_T_ADDITIONAL = 1, 175 | TMAC_ALLOC_T_QUIT_GO = 2, 176 | TMAC_ALLOC_T_REPL_SLOT1 = 3, 177 | }; 178 | const char *tetra_get_alloc_t_name(uint8_t alloct); 179 | 180 | struct tetra_chan_alloc_decoded { 181 | uint8_t type; 182 | uint8_t timeslot; 183 | uint8_t ul_dl; 184 | uint8_t clch_perm; 185 | uint8_t cell_chg_f; 186 | uint16_t carrier_nr; 187 | uint8_t ext_carr_pres; 188 | struct { 189 | uint8_t freq_band; 190 | uint8_t freq_offset; 191 | uint8_t duplex_spc; 192 | uint8_t reverse_oper; 193 | } ext_carr; 194 | uint8_t monit_pattern; 195 | uint8_t monit_patt_f18; 196 | struct { 197 | uint8_t ul_dl_ass; 198 | uint8_t bandwidth; 199 | uint8_t modulation; 200 | uint8_t max_ul_qam; 201 | uint8_t conf_chan_stat; 202 | uint8_t bs_imbalance; 203 | uint8_t bs_tx_rel; 204 | uint8_t napping_sts; 205 | } aug; 206 | }; 207 | 208 | struct tetra_addr { 209 | uint8_t type; 210 | uint16_t mcc; 211 | uint16_t mnc; 212 | uint32_t ssi; 213 | 214 | uint16_t event_label; 215 | uint8_t usage_marker; 216 | }; 217 | 218 | struct tetra_resrc_decoded { 219 | uint8_t fill_bits; 220 | uint8_t grant_position; 221 | uint8_t encryption_mode; 222 | uint8_t is_encrypted; // Set to 0 if not encrypted or decrypted successfully 223 | uint8_t rand_acc_flag; 224 | int macpdu_length; 225 | struct tetra_addr addr; 226 | 227 | uint8_t power_control_pres; 228 | 229 | struct { 230 | uint8_t nr_slots; 231 | uint8_t delay; 232 | uint8_t pres; 233 | } slot_granting; 234 | 235 | uint8_t chan_alloc_pres; 236 | struct tetra_chan_alloc_decoded cad; 237 | }; 238 | int macpdu_decode_resource(struct tetra_resrc_decoded *rsd, const uint8_t *bits, uint8_t is_decrypted); 239 | 240 | int macpdu_decode_chan_alloc(struct tetra_chan_alloc_decoded *cad, const uint8_t *bits); 241 | 242 | const char *tetra_addr_dump(const struct tetra_addr *addr); 243 | 244 | const char *tetra_get_ul_dl_name(uint8_t ul_dl); 245 | 246 | #endif 247 | -------------------------------------------------------------------------------- /src/demod/simdemod3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # SPDX-License-Identifier: GPL-3.0 6 | # 7 | # GNU Radio Python Flow Graph 8 | # Title: DQPSK demodulator for Telive, with the AFC packet sending option disabled 9 | # Author: Jacek Lipkowski SQ5BPF 10 | # Description: cqpsk demodulator for tetra. based on pi4dqpsk_rx.grc (by 'Luca Bortolotti' @optiluca) from https://gitlab.com/larryth/tetra-kit/ . It is meant as a replacement for simdemod2.py, taked the data from stdin, and dumpd demodulated data to STDOUT. Also sends via UDP the information how much the signal is mistuned (this option is disabled for the version committed to osmo-tetra) 11 | # GNU Radio version: 3.10.5.1 12 | 13 | from gnuradio import analog 14 | from gnuradio import blocks 15 | from gnuradio import digital 16 | from gnuradio import gr 17 | from gnuradio.filter import firdes 18 | from gnuradio.fft import window 19 | import sys 20 | import signal 21 | from argparse import ArgumentParser 22 | from gnuradio.eng_arg import eng_float, intx 23 | from gnuradio import eng_notation 24 | import cmath 25 | import os 26 | 27 | 28 | 29 | 30 | class simdemod3(gr.top_block): 31 | 32 | def __init__(self): 33 | gr.top_block.__init__(self, "DQPSK demodulator for Telive, with the AFC packet sending option disabled", catch_exceptions=True) 34 | 35 | ################################################## 36 | # Variables 37 | ################################################## 38 | self.sps = sps = 2 39 | self.nfilts = nfilts = 32 40 | self.constel = constel = digital.constellation_dqpsk().base() 41 | self.constel.gen_soft_dec_lut(8) 42 | self.variable_adaptive_algorithm_0 = variable_adaptive_algorithm_0 = digital.adaptive_algorithm_cma( constel, 10e-3, 1).base() 43 | self.samp_rate = samp_rate = 2000000 44 | self.rrc_taps = rrc_taps = firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), 0.35, 11*sps*nfilts) 45 | self.decim = decim = 32 46 | self.channel_rate = channel_rate = 36000 47 | self.arity = arity = 4 48 | 49 | ################################################## 50 | # Blocks 51 | ################################################## 52 | 53 | self.stdout_sink = blocks.file_descriptor_sink(gr.sizeof_char*1, 1) 54 | self.digital_pfb_clock_sync_xxx_0 = digital.pfb_clock_sync_ccf(sps, (2*cmath.pi/100.0), rrc_taps, nfilts, (nfilts/2), 1.5, sps) 55 | self.digital_map_bb_0 = digital.map_bb(constel.pre_diff_code()) 56 | self.digital_linear_equalizer_0 = digital.linear_equalizer(15, sps, variable_adaptive_algorithm_0, True, [ ], 'corr_est') 57 | self.digital_fll_band_edge_cc_0 = digital.fll_band_edge_cc(sps, 0.35, 45, (cmath.pi/100.0)) 58 | self.digital_diff_phasor_cc_0 = digital.diff_phasor_cc() 59 | self.digital_constellation_decoder_cb_0 = digital.constellation_decoder_cb(constel) 60 | self.blocks_unpack_k_bits_bb_0 = blocks.unpack_k_bits_bb(constel.bits_per_symbol()) 61 | self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_float*1) 62 | self.blocks_file_descriptor_source_0 = blocks.file_descriptor_source(gr.sizeof_gr_complex*1, 0, False) 63 | self.analog_feedforward_agc_cc_0 = analog.feedforward_agc_cc(8, 1) 64 | 65 | 66 | ################################################## 67 | # Connections 68 | ################################################## 69 | self.connect((self.analog_feedforward_agc_cc_0, 0), (self.digital_fll_band_edge_cc_0, 0)) 70 | self.connect((self.blocks_file_descriptor_source_0, 0), (self.analog_feedforward_agc_cc_0, 0)) 71 | self.connect((self.blocks_unpack_k_bits_bb_0, 0), (self.stdout_sink, 0)) 72 | self.connect((self.digital_constellation_decoder_cb_0, 0), (self.digital_map_bb_0, 0)) 73 | self.connect((self.digital_diff_phasor_cc_0, 0), (self.digital_constellation_decoder_cb_0, 0)) 74 | self.connect((self.digital_fll_band_edge_cc_0, 2), (self.blocks_null_sink_0, 1)) 75 | self.connect((self.digital_fll_band_edge_cc_0, 3), (self.blocks_null_sink_0, 2)) 76 | self.connect((self.digital_fll_band_edge_cc_0, 1), (self.blocks_null_sink_0, 0)) 77 | self.connect((self.digital_fll_band_edge_cc_0, 0), (self.digital_pfb_clock_sync_xxx_0, 0)) 78 | self.connect((self.digital_linear_equalizer_0, 0), (self.digital_diff_phasor_cc_0, 0)) 79 | self.connect((self.digital_map_bb_0, 0), (self.blocks_unpack_k_bits_bb_0, 0)) 80 | self.connect((self.digital_pfb_clock_sync_xxx_0, 0), (self.digital_linear_equalizer_0, 0)) 81 | 82 | 83 | def get_sps(self): 84 | return self.sps 85 | 86 | def set_sps(self, sps): 87 | self.sps = sps 88 | self.set_rrc_taps(firdes.root_raised_cosine(self.nfilts, self.nfilts, 1.0/float(self.sps), 0.35, 11*self.sps*self.nfilts)) 89 | 90 | def get_nfilts(self): 91 | return self.nfilts 92 | 93 | def set_nfilts(self, nfilts): 94 | self.nfilts = nfilts 95 | self.set_rrc_taps(firdes.root_raised_cosine(self.nfilts, self.nfilts, 1.0/float(self.sps), 0.35, 11*self.sps*self.nfilts)) 96 | 97 | def get_constel(self): 98 | return self.constel 99 | 100 | def set_constel(self, constel): 101 | self.constel = constel 102 | 103 | def get_variable_adaptive_algorithm_0(self): 104 | return self.variable_adaptive_algorithm_0 105 | 106 | def set_variable_adaptive_algorithm_0(self, variable_adaptive_algorithm_0): 107 | self.variable_adaptive_algorithm_0 = variable_adaptive_algorithm_0 108 | 109 | def get_samp_rate(self): 110 | return self.samp_rate 111 | 112 | def set_samp_rate(self, samp_rate): 113 | self.samp_rate = samp_rate 114 | 115 | def get_rrc_taps(self): 116 | return self.rrc_taps 117 | 118 | def set_rrc_taps(self, rrc_taps): 119 | self.rrc_taps = rrc_taps 120 | self.digital_pfb_clock_sync_xxx_0.update_taps(self.rrc_taps) 121 | 122 | def get_decim(self): 123 | return self.decim 124 | 125 | def set_decim(self, decim): 126 | self.decim = decim 127 | 128 | def get_channel_rate(self): 129 | return self.channel_rate 130 | 131 | def set_channel_rate(self, channel_rate): 132 | self.channel_rate = channel_rate 133 | 134 | def get_arity(self): 135 | return self.arity 136 | 137 | def set_arity(self, arity): 138 | self.arity = arity 139 | 140 | 141 | 142 | 143 | def main(top_block_cls=simdemod3, options=None): 144 | tb = top_block_cls() 145 | 146 | def sig_handler(sig=None, frame=None): 147 | tb.stop() 148 | tb.wait() 149 | 150 | sys.exit(0) 151 | 152 | signal.signal(signal.SIGINT, sig_handler) 153 | signal.signal(signal.SIGTERM, sig_handler) 154 | 155 | tb.start() 156 | 157 | tb.wait() 158 | 159 | 160 | if __name__ == '__main__': 161 | main() 162 | -------------------------------------------------------------------------------- /etsi_codec-patches/round_private.patch: -------------------------------------------------------------------------------- 1 | Index: etsi/amr-code/channel.h 2 | =================================================================== 3 | --- etsi.orig/amr-code/channel.h 2011-05-29 11:42:12.000000000 +0200 4 | +++ etsi/amr-code/channel.h 2011-05-29 11:42:23.000000000 +0200 5 | @@ -139,7 +139,7 @@ 6 | Word16 negate(Word16 var1); /* Short negate, 1 */ 7 | Word16 norm_l(Word32 L_var1); /* Long norm, 30 */ 8 | Word16 norm_s(Word16 var1); /* Short norm, 15 */ 9 | -Word16 round(Word32 L_var1); /* Round, 1 */ 10 | +Word16 etsi_round(Word32 L_var1); /* Round, 1 */ 11 | Word16 shl(Word16 var1, Word16 var2); /* Short shift left, 1 */ 12 | Word16 shr(Word16 var1, Word16 var2); /* Short shift right, 1 */ 13 | Word16 sub(Word16 var1, Word16 var2); /* Short sub, 1 */ 14 | Index: etsi/amr-code/source.h 15 | =================================================================== 16 | --- etsi.orig/amr-code/source.h 2011-05-29 11:41:07.000000000 +0200 17 | +++ etsi/amr-code/source.h 2011-05-29 11:41:20.000000000 +0200 18 | @@ -164,7 +164,7 @@ 19 | Word16 negate(Word16 var1); /* Short negate, 1 */ 20 | Word16 norm_l(Word32 L_var1); /* Long norm, 30 */ 21 | Word16 norm_s(Word16 var1); /* Short norm, 15 */ 22 | -Word16 round(Word32 L_var1); /* Round, 1 */ 23 | +Word16 etsi_round(Word32 L_var1); /* Round, 1 */ 24 | Word16 shl(Word16 var1, Word16 var2); /* Short shift left, 1 */ 25 | Word16 shr(Word16 var1, Word16 var2); /* Short shift right, 1 */ 26 | Word16 sub(Word16 var1, Word16 var2); /* Short sub, 1 */ 27 | Index: etsi/amr-code/tetra_op.c 28 | =================================================================== 29 | --- etsi.orig/amr-code/tetra_op.c 2011-05-29 11:41:07.000000000 +0200 30 | +++ etsi/amr-code/tetra_op.c 2011-05-29 11:41:54.000000000 +0200 31 | @@ -31,7 +31,7 @@ 32 | * - negate() 33 | * - norm_l() 34 | * - norm_s() 35 | -* - round() 36 | +* - etsi_round() 37 | * - sature() 38 | * - shl() 39 | * - shr() 40 | @@ -1336,14 +1336,14 @@ 41 | 42 | /************************************************************************ 43 | * 44 | -* Function Name : round 45 | +* Function Name : etsi_round 46 | * 47 | * Purpose : 48 | * 49 | * Round the lower 16 bits of the 32 bit input number into its MS 16 bits 50 | * with saturation. Shift the resulting bits right by 16 and return the 16 51 | * bit number: 52 | -* round(L_var1) = extract_h(L_add(L_var1,32768)) 53 | +* etsi_round(L_var1) = extract_h(L_add(L_var1,32768)) 54 | * 55 | * Complexity Weight : 1 56 | * 57 | @@ -1365,7 +1365,7 @@ 58 | * 59 | ************************************************************************/ 60 | 61 | -Word16 round(Word32 L_var1) 62 | +Word16 etsi_round(Word32 L_var1) 63 | { 64 | Word16 var_out; 65 | Word32 L_arrondi; 66 | Index: etsi/c-code/channel.h 67 | =================================================================== 68 | --- etsi.orig/c-code/channel.h 2011-05-29 11:46:28.000000000 +0200 69 | +++ etsi/c-code/channel.h 2011-05-29 11:46:57.000000000 +0200 70 | @@ -130,7 +130,7 @@ 71 | Word16 negate(Word16 var1); /* Short negate, 1 */ 72 | Word16 norm_l(Word32 L_var1); /* Long norm, 30 */ 73 | Word16 norm_s(Word16 var1); /* Short norm, 15 */ 74 | -Word16 round(Word32 L_var1); /* Round, 1 */ 75 | +Word16 etsi_round(Word32 L_var1); /* Round, 1 */ 76 | Word16 shl(Word16 var1, Word16 var2); /* Short shift left, 1 */ 77 | Word16 shr(Word16 var1, Word16 var2); /* Short shift right, 1 */ 78 | Word16 sub(Word16 var1, Word16 var2); /* Short sub, 1 */ 79 | Index: etsi/c-code/source.h 80 | =================================================================== 81 | --- etsi.orig/c-code/source.h 2011-05-29 11:46:28.000000000 +0200 82 | +++ etsi/c-code/source.h 2011-05-29 11:47:06.000000000 +0200 83 | @@ -164,7 +164,7 @@ 84 | Word16 negate(Word16 var1); /* Short negate, 1 */ 85 | Word16 norm_l(Word32 L_var1); /* Long norm, 30 */ 86 | Word16 norm_s(Word16 var1); /* Short norm, 15 */ 87 | -Word16 round(Word32 L_var1); /* Round, 1 */ 88 | +Word16 etsi_round(Word32 L_var1); /* Round, 1 */ 89 | Word16 shl(Word16 var1, Word16 var2); /* Short shift left, 1 */ 90 | Word16 shr(Word16 var1, Word16 var2); /* Short shift right, 1 */ 91 | Word16 sub(Word16 var1, Word16 var2); /* Short sub, 1 */ 92 | Index: etsi/c-code/sub_dsp.c 93 | =================================================================== 94 | --- etsi.orig/c-code/sub_dsp.c 2011-05-29 11:46:28.000000000 +0200 95 | +++ etsi/c-code/sub_dsp.c 2011-05-29 11:47:19.000000000 +0200 96 | @@ -554,7 +554,7 @@ 97 | 98 | fac[0] = gamma; 99 | for(i=1; i 4 | 5 | # Usage: 6 | # src$ ./demod/python/osmosdr-tetra_demod_fft.py -o /dev/stdout | ./float_to_bits /dev/stdin /dev/stdout | ./tetra-rx /dev/stdin 7 | # 8 | # Adjust the center frequency (-f) and gain (-g) according to your needs. 9 | # Use left click in Wideband Spectrum window to roughly select a TETRA carrier. 10 | # In Wideband Spectrum you can also tune by 1/4 of the bandwidth by clicking on the rightmost/leftmost spectrum side. 11 | # Use left click in Channel Spectrum windows to fine tune the carrier by clicking on the left or right side of the spectrum. 12 | 13 | 14 | import sys 15 | import math 16 | from gnuradio import gr, gru, eng_notation, blocks, filter 17 | from gnuradio.filter import pfb 18 | from gnuradio.eng_option import eng_option 19 | from gnuradio.wxgui import fftsink2, scopesink2, forms, TRIG_MODE_AUTO 20 | from grc_gnuradio import wxgui as grc_wxgui 21 | from optparse import OptionParser 22 | import osmosdr 23 | import wx 24 | 25 | try: 26 | import cqpsk 27 | except: 28 | from tetra_demod import cqpsk 29 | 30 | # applies frequency translation, resampling and demodulation 31 | 32 | class top_block(grc_wxgui.top_block_gui): 33 | def __init__(self): 34 | grc_wxgui.top_block_gui.__init__(self, title="Top Block") 35 | 36 | options = get_options() 37 | 38 | self.ifreq = options.frequency 39 | self.rfgain = options.gain 40 | 41 | self.src = osmosdr.source(options.args) 42 | self.src.set_center_freq(self.ifreq) 43 | self.src.set_sample_rate(int(options.sample_rate)) 44 | 45 | if self.rfgain is None: 46 | self.src.set_gain_mode(1) 47 | self.iagc = 1 48 | self.rfgain = 0 49 | else: 50 | self.iagc = 0 51 | self.src.set_gain_mode(0) 52 | self.src.set_gain(self.rfgain) 53 | 54 | # may differ from the requested rate 55 | sample_rate = self.src.get_sample_rate() 56 | sys.stderr.write("sample rate: %d\n" % (sample_rate)) 57 | 58 | symbol_rate = 18000 59 | sps = 2 # output rate will be 36,000 60 | out_sample_rate = symbol_rate * sps 61 | 62 | options.low_pass = options.low_pass / 2.0 63 | 64 | if sample_rate == 96000: # FunCube Dongle 65 | first_decim = 2 66 | else: 67 | first_decim = 10 68 | 69 | self.offset = 0 70 | 71 | taps = filter.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.2, filter.firdes.WIN_HANN) 72 | self.tuner = filter.freq_xlating_fir_filter_ccf(first_decim, taps, self.offset, sample_rate) 73 | 74 | self.demod = cqpsk.cqpsk_demod( 75 | samples_per_symbol = sps, 76 | excess_bw=0.35, 77 | costas_alpha=0.03, 78 | gain_mu=0.05, 79 | mu=0.05, 80 | omega_relative_limit=0.05, 81 | log=options.log, 82 | verbose=options.verbose) 83 | 84 | self.output = blocks.file_sink(gr.sizeof_float, options.output_file) 85 | 86 | rerate = float(sample_rate / float(first_decim)) / float(out_sample_rate) 87 | sys.stderr.write("resampling factor: %f\n" % rerate) 88 | 89 | if rerate.is_integer(): 90 | sys.stderr.write("using pfb decimator\n") 91 | self.resamp = pfb.decimator_ccf(int(rerate)) 92 | else: 93 | sys.stderr.write("using pfb resampler\n") 94 | self.resamp = pfb.arb_resampler_ccf(1 / rerate) 95 | 96 | self.connect(self.src, self.tuner, self.resamp, self.demod, self.output) 97 | 98 | self.Main = wx.Notebook(self.GetWin(), style=wx.NB_TOP) 99 | self.Main.AddPage(grc_wxgui.Panel(self.Main), "Wideband Spectrum") 100 | self.Main.AddPage(grc_wxgui.Panel(self.Main), "Channel Spectrum") 101 | self.Main.AddPage(grc_wxgui.Panel(self.Main), "Soft Bits") 102 | 103 | def set_ifreq(ifreq): 104 | self.ifreq = ifreq 105 | self._ifreq_text_box.set_value(self.ifreq) 106 | self.src.set_center_freq(self.ifreq) 107 | 108 | self._ifreq_text_box = forms.text_box( 109 | parent=self.GetWin(), 110 | value=self.ifreq, 111 | callback=set_ifreq, 112 | label="Center Frequency", 113 | converter=forms.float_converter(), 114 | ) 115 | self.Add(self._ifreq_text_box) 116 | 117 | def set_iagc(iagc): 118 | self.iagc = iagc 119 | self._agc_check_box.set_value(self.iagc) 120 | self.src.set_gain_mode(self.iagc, 0) 121 | self.src.set_gain(0 if self.iagc == 1 else self.rfgain, 0) 122 | 123 | self._agc_check_box = forms.check_box( 124 | parent=self.GetWin(), 125 | value=self.iagc, 126 | callback=set_iagc, 127 | label="Automatic Gain", 128 | true=1, 129 | false=0, 130 | ) 131 | 132 | self.Add(self._agc_check_box) 133 | 134 | def set_rfgain(rfgain): 135 | self.rfgain = rfgain 136 | self._rfgain_slider.set_value(self.rfgain) 137 | self._rfgain_text_box.set_value(self.rfgain) 138 | self.src.set_gain(0 if self.iagc == 1 else self.rfgain, 0) 139 | 140 | _rfgain_sizer = wx.BoxSizer(wx.VERTICAL) 141 | self._rfgain_text_box = forms.text_box( 142 | parent=self.GetWin(), 143 | sizer=_rfgain_sizer, 144 | value=self.rfgain, 145 | callback=set_rfgain, 146 | label="RF Gain", 147 | converter=forms.float_converter(), 148 | proportion=0, 149 | ) 150 | self._rfgain_slider = forms.slider( 151 | parent=self.GetWin(), 152 | sizer=_rfgain_sizer, 153 | value=self.rfgain, 154 | callback=set_rfgain, 155 | minimum=0, 156 | maximum=50, 157 | num_steps=200, 158 | style=wx.SL_HORIZONTAL, 159 | cast=float, 160 | proportion=1, 161 | ) 162 | 163 | self.Add(_rfgain_sizer) 164 | 165 | self.Add(self.Main) 166 | 167 | def fftsink2_callback(x, y): 168 | if abs(x / (sample_rate / 2)) > 0.9: 169 | set_ifreq(self.ifreq + x / 2) 170 | else: 171 | sys.stderr.write("coarse tuned to: %d Hz\n" % x) 172 | self.offset = x 173 | self.tuner.set_center_freq(self.offset) 174 | 175 | self.scope = fftsink2.fft_sink_c(self.Main.GetPage(0).GetWin(), 176 | title="Wideband Spectrum (click to coarse tune)", 177 | fft_size=1024, 178 | sample_rate=sample_rate, 179 | ref_scale=2.0, 180 | ref_level=0, 181 | y_divs=10, 182 | fft_rate=10, 183 | average=False, 184 | avg_alpha=0.6) 185 | 186 | self.Main.GetPage(0).Add(self.scope.win) 187 | self.scope.set_callback(fftsink2_callback) 188 | 189 | self.connect(self.src, self.scope) 190 | 191 | def fftsink2_callback2(x, y): 192 | self.offset = self.offset - (x / 10) 193 | sys.stderr.write("fine tuned to: %d Hz\n" % self.offset) 194 | self.tuner.set_center_freq(self.offset) 195 | 196 | self.scope2 = fftsink2.fft_sink_c(self.Main.GetPage(1).GetWin(), 197 | title="Channel Spectrum (click to fine tune)", 198 | fft_size=1024, 199 | sample_rate=out_sample_rate, 200 | ref_scale=2.0, 201 | ref_level=-20, 202 | y_divs=10, 203 | fft_rate=10, 204 | average=False, 205 | avg_alpha=0.6) 206 | 207 | self.Main.GetPage(1).Add(self.scope2.win) 208 | self.scope2.set_callback(fftsink2_callback2) 209 | 210 | self.connect(self.resamp, self.scope2) 211 | 212 | self.scope3 = scopesink2.scope_sink_f( 213 | self.Main.GetPage(2).GetWin(), 214 | title="Soft Bits", 215 | sample_rate=out_sample_rate, 216 | v_scale=0, 217 | v_offset=0, 218 | t_scale=0.001, 219 | ac_couple=False, 220 | xy_mode=False, 221 | num_inputs=1, 222 | trig_mode=TRIG_MODE_AUTO, 223 | y_axis_label="Counts", 224 | ) 225 | self.Main.GetPage(2).Add(self.scope3.win) 226 | 227 | self.connect(self.demod, self.scope3) 228 | 229 | def get_options(): 230 | parser = OptionParser(option_class=eng_option) 231 | 232 | parser.add_option("-a", "--args", type="string", default="", 233 | help="gr-osmosdr device arguments") 234 | parser.add_option("-s", "--sample-rate", type="eng_float", default=1800000, 235 | help="set receiver sample rate (default 1800000)") 236 | parser.add_option("-f", "--frequency", type="eng_float", default=394.4e6, 237 | help="set receiver center frequency") 238 | parser.add_option("-g", "--gain", type="eng_float", default=None, 239 | help="set receiver gain") 240 | 241 | # demodulator related settings 242 | parser.add_option("-l", "--log", action="store_true", default=False, help="dump debug .dat files") 243 | parser.add_option("-L", "--low-pass", type="eng_float", default=25e3, help="low pass cut-off", metavar="Hz") 244 | parser.add_option("-o", "--output-file", type="string", default="out.float", help="specify the bit output file") 245 | parser.add_option("-v", "--verbose", action="store_true", default=False, help="dump demodulation data") 246 | (options, args) = parser.parse_args() 247 | if len(args) != 0: 248 | parser.print_help() 249 | raise SystemExit, 1 250 | 251 | return (options) 252 | 253 | if __name__ == '__main__': 254 | tb = top_block() 255 | tb.Run(True) 256 | -------------------------------------------------------------------------------- /src/tetra_llc_pdu.c: -------------------------------------------------------------------------------- 1 | /* Implementation of some PDU parsing of the TETRA LLC */ 2 | 3 | /* (C) 2011 by Harald Welte 4 | * All Rights Reserved 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation; either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "tetra_common.h" 32 | #include "tetra_llc_pdu.h" 33 | 34 | static const struct value_string tetra_llc_pdut_names[] = { 35 | { TLLC_PDUT_BL_ADATA, "BL-ADATA" }, 36 | { TLLC_PDUT_BL_DATA, "BL-DATA" }, 37 | { TLLC_PDUT_BL_UDATA, "BL-UDATA" }, 38 | { TLLC_PDUT_BL_ACK, "BL-ACK" }, 39 | { TLLC_PDUT_BL_ADATA_FCS, "BL-ADATA-FCS" }, 40 | { TLLC_PDUT_BL_DATA_FCS, "BL-DATA-FCS" }, 41 | { TLLC_PDUT_BL_UDATA_FCS, "BL-UDATA-FCS" }, 42 | { TLLC_PDUT_BL_ACK_FCS, "BL-ACK-FCS" }, 43 | { TLLC_PDUT_AL_SETUP, "AL-SETUP" }, 44 | { TLLC_PDUT_AL_DATA_FINAL, "AL-DATA/FINAL" }, 45 | { TLLC_PDUT_AL_UDATA_UFINAL, "AL-UDATA/FINAL" }, 46 | { TLLC_PDUT_AL_ACK_RNR, "AL-ACK/AL-RNR" }, 47 | { TLLC_PDUT_AL_RECONNECT, "AL-RECONNECT" }, 48 | { TLLC_PDUT_SUPPL, "AL-SUPPLEMENTARY" }, 49 | { TLLC_PDUT_L2SIG, "AL-L2SIG" }, 50 | { TLLD_PDUT_AL_DISC, "AL-DISC" }, 51 | { 0, NULL } 52 | }; 53 | const char *tetra_get_llc_pdut_name(uint8_t pdut) 54 | { 55 | return get_value_string(tetra_llc_pdut_names, pdut); 56 | } 57 | 58 | static const struct value_string pdut_dec_names[] = { 59 | { TLLC_PDUT_DEC_BL_ADATA, "BL-ADATA" }, 60 | { TLLC_PDUT_DEC_BL_DATA, "BL-DATA" }, 61 | { TLLC_PDUT_DEC_BL_UDATA, "BL-UDATA" }, 62 | { TLLC_PDUT_DEC_BL_ACK, "BL-ACK" }, 63 | { TLLC_PDUT_DEC_AL_SETUP, "AL-SETUP" }, 64 | { TLLC_PDUT_DEC_AL_DATA, "AL-DATA" }, 65 | { TLLC_PDUT_DEC_AL_FINAL, "AL-FINAL" }, 66 | { TLLC_PDUT_DEC_AL_UDATA, "AL-UDATA" }, 67 | { TLLC_PDUT_DEC_AL_UFINAL, "AL-UFINAL" }, 68 | { TLLC_PDUT_DEC_AL_ACK, "AL-ACK" }, 69 | { TLLC_PDUT_DEC_AL_RNR, "AL-RNR" }, 70 | { TLLC_PDUT_DEC_AL_RECONNECT, "AL-RECONNECT" }, 71 | { TLLC_PDUT_DEC_AL_DISC, "AL-DISC" }, 72 | { TLLC_PDUT_DEC_ALX_DATA, "ALX-DATA" }, 73 | { TLLC_PDUT_DEC_ALX_FINAL, "ALX-FINAL" }, 74 | { TLLC_PDUT_DEC_ALX_UDATA, "ALX-UDATA" }, 75 | { TLLC_PDUT_DEC_ALX_UFINAL, "ALX-UFINAL" }, 76 | { TLLC_PDUT_DEC_ALX_ACK, "ALX-ACK" }, 77 | { TLLC_PDUT_DEC_ALX_RNR, "ALX-RNR" }, 78 | { 0, NULL } 79 | }; 80 | 81 | const char *tetra_get_llc_pdut_dec_name(enum tllc_pdut_dec pdut) 82 | { 83 | return get_value_string(pdut_dec_names, pdut); 84 | } 85 | 86 | static const uint8_t tetra_llc_pdu_lengths[16] = { 87 | 6, /* TLLC_PDUT_BL_ADATA */ 88 | 5, /* TLLC_PDUT_BL_DATA */ 89 | 4, /* TLLC_PDUT_BL_UDATA */ 90 | 5, /* TLLC_PDUT_BL_ACK */ 91 | 6 + 32, /* TLLC_PDUT_BL_ADATA_FCS */ 92 | 5 + 32, /* TLLC_PDUT_BL_DATA_FCS */ 93 | 4 + 32, /* TLLC_PDUT_BL_UDATA_FCS */ 94 | 5 + 32, /* TLLC_PDUT_BL_ACK_FCS */ 95 | 0, /* Not implemented, TLLC_PDUT_AL_SETUP */ 96 | 13, /* TLLC_PDUT_AL_DATA_FINAL */ 97 | 17, /* TLLC_PDUT_AL_UDATA_UFINAL */ 98 | 1, /* Not implemented, TLLC_PDUT_AL_ACK_RNR */ 99 | 0, /* Not implemented, TLLC_PDUT_AL_RECONNECT */ 100 | 0, /* Not implemented, TLLC_PDUT_SUPPL */ 101 | 0, /* Not implemented, TLLC_PDUT_L2SIG */ 102 | 0, /* Not implemented, TLLD_PDUT_AL_DISC */ 103 | }; 104 | 105 | static uint32_t tetra_llc_compute_fcs(const uint8_t *buf, int len) 106 | { 107 | uint32_t crc = 0xFFFFFFFF; 108 | if (len < 32) { 109 | crc <<= (32 - len); 110 | } 111 | 112 | for (size_t i = 0; i < len; i++) { 113 | uint8_t bit = (buf[i] ^ (crc >> 31)) & 1; 114 | crc <<= 1; 115 | if (bit) { 116 | crc = crc ^ 0x04C11DB7; 117 | } 118 | } 119 | return ~crc; 120 | } 121 | 122 | static bool tetra_llc_check_fcs(struct tetra_llc_pdu *lpp, uint8_t *buf, int len) 123 | { 124 | uint32_t computed_fcs = tetra_llc_compute_fcs(buf, len); 125 | return lpp->fcs == computed_fcs; 126 | } 127 | 128 | int tetra_llc_pdu_parse(struct tetra_llc_pdu *lpp, uint8_t *buf, int len) 129 | { 130 | uint8_t *cur = buf; 131 | uint8_t pdu_type; 132 | 133 | lpp->have_fcs = false; 134 | lpp->fcs = 0; 135 | lpp->fcs_invalid = false; 136 | pdu_type = bits_to_uint(cur, 4); 137 | cur += 4; 138 | 139 | /* Check length to prevent out of bounds reads */ 140 | if (len < tetra_llc_pdu_lengths[pdu_type]) { 141 | printf("WARNING llc pdu too small to parse, needed %d\n", tetra_llc_pdu_lengths[pdu_type]); 142 | lpp->tl_sdu_len = 0; 143 | return len; 144 | } 145 | 146 | switch (pdu_type) { 147 | 148 | case TLLC_PDUT_BL_ADATA: 149 | case TLLC_PDUT_BL_ADATA_FCS: 150 | lpp->nr = *cur++; 151 | lpp->ns = *cur++; 152 | lpp->tl_sdu = cur; 153 | lpp->tl_sdu_len = len - (cur - buf); 154 | lpp->pdu_type = TLLC_PDUT_DEC_BL_ADATA; 155 | 156 | if (pdu_type == TLLC_PDUT_BL_ADATA_FCS) { 157 | lpp->tl_sdu_len -= 32; 158 | lpp->have_fcs = true; 159 | lpp->fcs = bits_to_uint(buf + len - 32, 32); 160 | lpp->fcs_invalid = !tetra_llc_check_fcs(lpp, cur, lpp->tl_sdu_len); 161 | } 162 | break; 163 | 164 | case TLLC_PDUT_BL_DATA: 165 | case TLLC_PDUT_BL_DATA_FCS: 166 | lpp->ns = *cur++; 167 | lpp->tl_sdu = cur; 168 | lpp->tl_sdu_len = len - (cur - buf); 169 | lpp->pdu_type = TLLC_PDUT_DEC_BL_DATA; 170 | 171 | if (pdu_type == TLLC_PDUT_BL_DATA_FCS) { 172 | lpp->tl_sdu_len -= 32; 173 | lpp->have_fcs = true; 174 | lpp->fcs = bits_to_uint(buf + len - 32, 32); 175 | lpp->fcs_invalid = !tetra_llc_check_fcs(lpp, cur, lpp->tl_sdu_len); 176 | } 177 | break; 178 | 179 | case TLLC_PDUT_BL_UDATA: 180 | case TLLC_PDUT_BL_UDATA_FCS: 181 | lpp->tl_sdu = cur; 182 | lpp->tl_sdu_len = len - (cur - buf); 183 | lpp->pdu_type = TLLC_PDUT_DEC_BL_UDATA; 184 | 185 | if (pdu_type == TLLC_PDUT_BL_UDATA_FCS) { 186 | lpp->tl_sdu_len -= 32; 187 | lpp->have_fcs = true; 188 | lpp->fcs = bits_to_uint(buf + len - 32, 32); 189 | lpp->fcs_invalid = !tetra_llc_check_fcs(lpp, cur, lpp->tl_sdu_len); 190 | } 191 | break; 192 | 193 | case TLLC_PDUT_BL_ACK: 194 | case TLLC_PDUT_BL_ACK_FCS: 195 | lpp->nr = *cur++; 196 | lpp->tl_sdu = cur; 197 | lpp->tl_sdu_len = len - (cur - buf); 198 | lpp->pdu_type = TLLC_PDUT_DEC_BL_ACK; 199 | 200 | if (pdu_type == TLLC_PDUT_BL_ACK_FCS) { 201 | lpp->tl_sdu_len -= 32; 202 | lpp->have_fcs = true; 203 | lpp->fcs = bits_to_uint(buf + len - 32, 32); 204 | lpp->fcs_invalid = !tetra_llc_check_fcs(lpp, cur, lpp->tl_sdu_len); 205 | } 206 | break; 207 | 208 | case TLLC_PDUT_AL_SETUP: 209 | /* TODO FIXME IMPLEMENT */ 210 | lpp->pdu_type = TLLC_PDUT_DEC_AL_SETUP; 211 | lpp->tl_sdu = cur; 212 | lpp->tl_sdu_len = 0; 213 | break; 214 | 215 | case TLLC_PDUT_AL_DATA_FINAL: 216 | if (*cur++) { 217 | /* FINAL */ 218 | cur++; /* if set, AL_FINAL_AR */ 219 | lpp->ns = bits_to_uint(cur, 3); cur += 3; 220 | lpp->ss = bits_to_uint(cur, 8); cur += 8; 221 | 222 | lpp->tl_sdu = cur; 223 | lpp->tl_sdu_len = len - (cur - buf); 224 | lpp->pdu_type = TLLC_PDUT_DEC_AL_FINAL; 225 | 226 | /* Needs to be defragmented so FCS is checked elsewhere */ 227 | lpp->have_fcs = 1; 228 | 229 | } else { 230 | /* DATA Table 21.19 */ 231 | cur++; 232 | lpp->ns = bits_to_uint(cur, 3); cur += 3; 233 | lpp->ss = bits_to_uint(cur, 8); cur += 8; 234 | lpp->tl_sdu = cur; 235 | lpp->tl_sdu_len = len - (cur - buf); 236 | lpp->pdu_type = TLLC_PDUT_DEC_AL_DATA; 237 | } 238 | break; 239 | 240 | case TLLC_PDUT_AL_UDATA_UFINAL: 241 | if (*cur++) { 242 | /* UFINAL 21.2.3.7 / Table 21.26 */ 243 | lpp->ns = bits_to_uint(cur, 8); cur += 8; 244 | lpp->ss = bits_to_uint(cur, 8); cur += 8; 245 | lpp->tl_sdu = cur; 246 | lpp->tl_sdu_len = len - (cur - buf); 247 | lpp->pdu_type = TLLC_PDUT_DEC_AL_UFINAL; 248 | 249 | /* Needs to be defragmented so FCS is checked elsewhere */ 250 | lpp->have_fcs = 1; 251 | 252 | } else { 253 | /* UDATA 21.2.3.6 / Table 21.24 */ 254 | lpp->ns = bits_to_uint(cur, 8); cur += 8; 255 | lpp->ss = bits_to_uint(cur, 8); cur += 8; 256 | lpp->tl_sdu = cur; 257 | lpp->tl_sdu_len = len - (cur - buf); 258 | lpp->pdu_type = TLLC_PDUT_DEC_AL_UDATA; 259 | } 260 | break; 261 | 262 | case TLLC_PDUT_AL_ACK_RNR: 263 | /* TODO FIXME IMPLEMENT */ 264 | if (*cur++) { 265 | /* AL-ACK 21.2.3.1 */ 266 | lpp->pdu_type = TLLC_PDUT_DEC_AL_ACK; 267 | } else { 268 | /* AL-RNR 21.2.3.1 */ 269 | lpp->pdu_type = TLLC_PDUT_DEC_AL_RNR; 270 | } 271 | 272 | lpp->tl_sdu = cur; 273 | lpp->tl_sdu_len = 0; 274 | break; 275 | 276 | case TLLC_PDUT_AL_RECONNECT: 277 | /* TODO FIXME IMPLEMENT */ 278 | lpp->pdu_type = TLLC_PDUT_DEC_AL_RECONNECT; 279 | lpp->tl_sdu = cur; 280 | lpp->tl_sdu_len = 0; 281 | break; 282 | 283 | case TLLD_PDUT_AL_DISC: 284 | /* TODO FIXME IMPLEMENT */ 285 | lpp->pdu_type = TLLC_PDUT_DEC_AL_DISC; 286 | lpp->tl_sdu = cur; 287 | lpp->tl_sdu_len = 0; 288 | break; 289 | 290 | case TLLC_PDUT_SUPPL: 291 | case TLLC_PDUT_L2SIG: 292 | /* TODO FIXME IMPLEMENT */ 293 | default: 294 | /* Prevent further parsing */ 295 | lpp->pdu_type = TLLC_PDUT_DEC_UNKNOWN; 296 | lpp->tl_sdu = cur; 297 | lpp->tl_sdu_len = 0; 298 | } 299 | 300 | /* Sanity check to prevent (further) out of bounds reads */ 301 | if (len < cur - buf) { 302 | lpp->tl_sdu_len = 0; 303 | return len; 304 | } 305 | 306 | return cur - buf; 307 | } 308 | -------------------------------------------------------------------------------- /src/lower_mac/tetra_conv_enc.c: -------------------------------------------------------------------------------- 1 | /* Tetra convolutional encoder, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ 2 | 3 | /* This converts from type type-2 bits into type-3 bits */ 4 | 5 | /* (C) 2011 by Harald Welte 6 | * All Rights Reserved 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU Affero General Public License as published by 10 | * the Free Software Foundation; either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Affero General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Affero General Public License 19 | * along with this program. If not, see . 20 | * 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | static char *dump_state(struct conv_enc_state *ces) 35 | { 36 | static char pbuf[1024]; 37 | snprintf(pbuf, sizeof(pbuf), "%u-%u-%u-%u", ces->delayed[0], 38 | ces->delayed[1], ces->delayed[2], ces->delayed[3]); 39 | return pbuf; 40 | } 41 | 42 | /* Mother code according to Section 8.2.3.1.1 */ 43 | static uint8_t conv_enc_in_bit(struct conv_enc_state *ces, uint8_t bit, uint8_t *out) 44 | { 45 | uint8_t g1, g2, g3, g4; 46 | uint8_t *delayed = ces->delayed; 47 | 48 | DEBUGP("State in: %s, Input bit: %u: ", dump_state(ces), bit); 49 | 50 | /* G1 = 1 + D + D4 */ 51 | g1 = (bit + delayed[0] + delayed[3]) % 2; 52 | /* G2 = 1 + D2 + D3 + D4 */ 53 | g2 = (bit + delayed[1] + delayed[2] + delayed[3]) % 2; 54 | /* G3 = 1 + D + D2 + D4 */ 55 | g3 = (bit + delayed[0] + delayed[1] + delayed[3]) % 2; 56 | /* G4 = 1 + D + D3 + D4 */ 57 | g4 = (bit + delayed[0] + delayed[2] + delayed[3]) % 2; 58 | 59 | /* shift the state and input our new bit */ 60 | ces->delayed[3] = ces->delayed[2]; 61 | ces->delayed[2] = ces->delayed[1]; 62 | ces->delayed[1] = ces->delayed[0]; 63 | ces->delayed[0] = bit; 64 | 65 | DEBUGP("Output bits: %u-%u-%u-%u, State out: %s\n", g1, g2, g3, g4, 66 | dump_state(ces)); 67 | 68 | *out++ = g1; 69 | *out++ = g2; 70 | *out++ = g3; 71 | *out++ = g4; 72 | 73 | return (g1 | (g2 << 1) | (g3 << 2) | (g4 << 3)); 74 | } 75 | 76 | /* in: bit-per-byte (len), out: bit-per-byte (4*len) */ 77 | int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out) 78 | { 79 | int i; 80 | 81 | for (i = 0; i < len; i++) { 82 | conv_enc_in_bit(ces, in[i], out); 83 | out += 4; 84 | } 85 | return 0; 86 | } 87 | 88 | int conv_enc_init(struct conv_enc_state *ces) 89 | { 90 | memset(ces->delayed, 0, sizeof(ces->delayed)); 91 | return 0; 92 | } 93 | 94 | /* Puncturing */ 95 | 96 | const uint8_t P_rate2_3[] = { 0, 1, 2, 5 }; 97 | const uint8_t P_rate1_3[] = { 0, 1, 2, 3, 5, 6, 7 }; 98 | 99 | /* Voice */ 100 | const uint8_t P_rate8_12[] = { 0, 1, 2, 4 }; 101 | const uint8_t P_rate8_18[] = { 0, 1, 2, 3, 4, 5, 7, 8, 10, 11 }; 102 | const uint8_t P_rate8_17[] = { 0, 1, 2, 3, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23 }; 103 | 104 | struct puncturer { 105 | enum tetra_rcpc_puncturer type; 106 | const uint8_t *P; 107 | uint8_t t; 108 | uint8_t period; 109 | uint32_t (*i_func)(uint32_t j); 110 | }; 111 | 112 | static uint32_t i_func_equals(uint32_t j) 113 | { 114 | return j; 115 | } 116 | 117 | static uint32_t i_func_292(uint32_t j) 118 | { 119 | return (j + ((j-1)/65)); 120 | } 121 | 122 | static uint32_t i_func_148(uint32_t j) 123 | { 124 | return (j + ((j-1)/35)); 125 | } 126 | 127 | /* Section 8.2.3.1.3 */ 128 | static const struct puncturer punct_2_3 = { 129 | .type = TETRA_RCPC_PUNCT_2_3, 130 | .P = P_rate2_3, 131 | .t = 3, 132 | .period = 8, 133 | .i_func = &i_func_equals, 134 | }; 135 | 136 | /* Section 8.2.3.1.4 */ 137 | static const struct puncturer punct_1_3 = { 138 | .type = TETRA_RCPC_PUNCT_1_3, 139 | .P = P_rate1_3, 140 | .t = 6, 141 | .period = 8, 142 | .i_func = &i_func_equals, 143 | }; 144 | 145 | /* Section 8.2.3.1.5 */ 146 | static const struct puncturer punct_292_432 = { 147 | .type = TETRA_RCPC_PUNCT_292_432, 148 | .P = P_rate2_3, 149 | .t = 3, 150 | .period = 8, 151 | .i_func = &i_func_292, 152 | }; 153 | 154 | /* Section 8.2.3.1.6 */ 155 | static const struct puncturer punct_148_432 = { 156 | .type = TETRA_RCPC_PUNCT_148_432, 157 | .P = P_rate1_3, 158 | .t = 6, 159 | .period = 8, 160 | .i_func = &i_func_148, 161 | }; 162 | 163 | /* EN 300 395-2 Section 5.5.2.1 */ 164 | static const struct puncturer punct_112_168 = { 165 | .type = TETRA_RCPC_PUNCT_112_168, 166 | .P = P_rate8_12, 167 | .t = 3, 168 | .period = 6, 169 | .i_func = &i_func_equals, 170 | }; 171 | 172 | /* EN 300 395-2 Section 5.5.2.2 */ 173 | static const struct puncturer punct_72_162 = { 174 | .type = TETRA_RCPC_PUNCT_72_162, 175 | .P = P_rate8_18, 176 | .t = 9, 177 | .period = 12, 178 | .i_func = &i_func_equals, 179 | }; 180 | 181 | /* EN 300 395-2 Section 5.6.2.1 */ 182 | static const struct puncturer punct_38_80 = { 183 | .type = TETRA_RCPC_PUNCT_72_162, 184 | .P = P_rate8_17, 185 | .t = 17, 186 | .period = 24, 187 | .i_func = &i_func_equals, 188 | }; 189 | 190 | static const struct puncturer *tetra_puncts[] = { 191 | [TETRA_RCPC_PUNCT_2_3] = &punct_2_3, 192 | [TETRA_RCPC_PUNCT_1_3] = &punct_1_3, 193 | [TETRA_RCPC_PUNCT_292_432] = &punct_292_432, 194 | [TETRA_RCPC_PUNCT_148_432] = &punct_148_432, 195 | [TETRA_RCPC_PUNCT_112_168] = &punct_112_168, 196 | [TETRA_RCPC_PUNCT_72_162] = &punct_72_162, 197 | [TETRA_RCPC_PUNCT_38_80] = &punct_38_80, 198 | }; 199 | 200 | /* Puncture the mother code (in) and write 'len' symbols to out */ 201 | int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out) 202 | { 203 | const struct puncturer *punct; 204 | uint32_t i, j, k; 205 | uint8_t t; 206 | const uint8_t *P; 207 | 208 | if (pu >= ARRAY_SIZE(tetra_puncts)) 209 | return -EINVAL; 210 | 211 | punct = tetra_puncts[pu]; 212 | t = punct->t; 213 | P = punct->P; 214 | 215 | /* Section 8.2.3.1.2 */ 216 | for (j = 1; j <= len; j++) { 217 | i = punct->i_func(j); 218 | k = punct->period * ((i-1)/t) + P[i - t*((i-1)/t)]; 219 | DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); 220 | out[j-1] = in[k-1]; 221 | } 222 | return 0; 223 | } 224 | 225 | /* De-Puncture the 'len' type-3 bits (in) and write mother code to out */ 226 | int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out) 227 | { 228 | const struct puncturer *punct; 229 | uint32_t i, j, k; 230 | uint8_t t; 231 | const uint8_t *P; 232 | 233 | if (pu >= ARRAY_SIZE(tetra_puncts)) 234 | return -EINVAL; 235 | 236 | punct = tetra_puncts[pu]; 237 | t = punct->t; 238 | P = punct->P; 239 | 240 | /* Section 8.2.3.1.2 */ 241 | for (j = 1; j <= len; j++) { 242 | i = punct->i_func(j); 243 | k = punct->period * ((i-1)/t) + P[i - t*((i-1)/t)]; 244 | DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); 245 | out[k-1] = in[j-1]; 246 | } 247 | return 0; 248 | } 249 | 250 | struct punct_test_param { 251 | uint16_t type2_len; 252 | uint16_t type3_len; 253 | uint16_t mother_rate; /* rate 1/3 for speech, 1/4 for data */ 254 | enum tetra_rcpc_puncturer punct; 255 | }; 256 | 257 | static const struct punct_test_param punct_test_params[] = { 258 | { 80, 120, 4, TETRA_RCPC_PUNCT_2_3 }, /* BSCH */ 259 | { 292, 432, 4, TETRA_RCPC_PUNCT_292_432 }, /* TCH/4.8 */ 260 | { 148, 432, 4, TETRA_RCPC_PUNCT_148_432 }, /* TCH/2.4 */ 261 | { 144, 216, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HD, BNCH, STCH */ 262 | { 112, 168, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/HU */ 263 | { 288, 432, 4, TETRA_RCPC_PUNCT_2_3 }, /* SCH/F */ 264 | { 112, 168, 3, TETRA_RCPC_PUNCT_112_168 }, /* Speech class 1 */ 265 | { 72, 162, 3, TETRA_RCPC_PUNCT_72_162 }, /* Speech class 2 */ 266 | { 38, 80, 3, TETRA_RCPC_PUNCT_38_80 }, /* Speech class 2 in STCH */ 267 | }; 268 | 269 | static int mother_memcmp(const uint8_t *mother, const uint8_t *depunct, int len) 270 | { 271 | unsigned int i, equal = 0; 272 | 273 | for (i = 0; i < len; i++) { 274 | /* ignore any 0xff-initialized part */ 275 | if (depunct[i] == 0xff) 276 | continue; 277 | if (depunct[i] != mother[i]) 278 | return -1; 279 | equal++; 280 | } 281 | 282 | return equal; 283 | } 284 | 285 | static int test_one_punct(const struct punct_test_param *ptp) 286 | { 287 | uint8_t *mother_buf; 288 | uint8_t *depunct_buf; 289 | uint8_t *type3_buf; 290 | int i, j, mother_len; 291 | 292 | printf("==> Testing Puncture/Depuncture mode %u (%u/%u)\n", 293 | ptp->punct, ptp->type2_len, ptp->type3_len); 294 | 295 | mother_len = ptp->type2_len * ptp->mother_rate; 296 | mother_buf = malloc(mother_len); 297 | depunct_buf = malloc(ptp->type2_len * ptp->mother_rate); 298 | type3_buf = malloc(ptp->type3_len); 299 | 300 | /* initialize mother buffer with sequence of bytes starting at 0 */ 301 | for (i = 0, j = 0; i < mother_len; i++, j++) { 302 | if (j == 0xff) 303 | j = 0; 304 | mother_buf[i] = j; 305 | } 306 | 307 | /* puncture the mother_buf to type3_buf using rate 2/3 on 60 bits */ 308 | get_punctured_rate(ptp->punct, mother_buf, ptp->type3_len, type3_buf); 309 | 310 | /* initialize the de-punctured buffer */ 311 | memset(depunct_buf, 0xff, mother_len); 312 | 313 | /* de-puncture into the depunct_buf (i.e. what happens at the receiver) */ 314 | tetra_rcpc_depunct(ptp->punct, type3_buf, ptp->type3_len, depunct_buf); 315 | 316 | DEBUGP("MOTH: %s\n", osmo_hexdump(mother_buf, mother_len)); 317 | DEBUGP("PUNC: %s\n", osmo_hexdump(type3_buf, ptp->type3_len)); 318 | DEBUGP("DEPU: %s\n", osmo_hexdump(depunct_buf, mother_len)); 319 | 320 | i = mother_memcmp(mother_buf, depunct_buf, mother_len); 321 | if (i < 0) { 322 | fprintf(stderr, "Mother buf != Depunct buf\n"); 323 | return i; 324 | } else if (i != ptp->type3_len) { 325 | fprintf(stderr, "Depunct buf only has %u equal symbols, we need %u\n", 326 | i, ptp->type3_len); 327 | return -EINVAL; 328 | } 329 | 330 | free(type3_buf); 331 | free(depunct_buf); 332 | free(mother_buf); 333 | 334 | return 0; 335 | } 336 | 337 | int tetra_punct_test(void) 338 | { 339 | int i, rc; 340 | 341 | for (i = 0; i < ARRAY_SIZE(punct_test_params); i++) { 342 | rc = test_one_punct(&punct_test_params[i]); 343 | if (rc < 0) 344 | return rc; 345 | } 346 | 347 | return 0; 348 | } 349 | -------------------------------------------------------------------------------- /src/conv_enc_test.c: -------------------------------------------------------------------------------- 1 | /* (C) 2011 by Harald Welte 2 | * All Rights Reserved 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | 32 | #include "tetra_common.h" 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "testpdu.h" 41 | 42 | 43 | #define swap16(x) ((x)<<8)|((x)>>8) 44 | 45 | static unsigned int num_crc_err; 46 | 47 | /* incoming TP-SAP UNITDATA.ind from PHY into lower MAC */ 48 | void tp_sap_udata_ind(enum tp_sap_data_type type, int blk_num, const uint8_t *bits, unsigned int len, void *priv) 49 | { 50 | } 51 | 52 | static void decode_schf(const uint8_t *bits) 53 | { 54 | uint8_t type4[1024]; 55 | uint8_t type3dp[1024*4]; 56 | uint8_t type3[1024]; 57 | uint8_t type2[1024]; 58 | 59 | printf("SCH/f type5: %s\n", osmo_ubit_dump(bits, 432)); 60 | memcpy(type4, bits, 432); 61 | tetra_scramb_bits(SCRAMB_INIT, type4, 432); 62 | printf("SCH/F type4: %s\n", osmo_ubit_dump(type4, 432)); 63 | /* Run (120,11) block deinterleaving: type-3 bits */ 64 | block_deinterleave(432, 103, type4, type3); 65 | printf("SCH/F type3: %s\n", osmo_ubit_dump(type3, 432)); 66 | /* De-puncture */ 67 | memset(type3dp, 0xff, sizeof(type3dp)); 68 | tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 432, type3dp); 69 | printf("SCH/F type3dp: %s\n", osmo_ubit_dump(type3dp, 288*4)); 70 | viterbi_dec_sb1_wrapper(type3dp, type2, 288); 71 | printf("SCH/F type2: %s\n", osmo_ubit_dump(type2, 288)); 72 | 73 | { 74 | uint16_t crc; 75 | crc = crc16_ccitt_bits(type2, 288-4); 76 | printf("CRC COMP: 0x%04x ", crc); 77 | if (crc == 0x1d0f) 78 | printf("OK\n"); 79 | else { 80 | printf("WRONG\n"); 81 | num_crc_err++; 82 | } 83 | } 84 | printf("SCH/F type1: %s\n", osmo_ubit_dump(type2, 268)); 85 | } 86 | 87 | /* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ 88 | int build_ndb_schf() 89 | { 90 | /* input: 268 type-1 bits */ 91 | uint8_t type2[284]; 92 | uint8_t master[284*4]; 93 | uint8_t type3[432]; 94 | uint8_t type4[432]; 95 | uint8_t type5[432]; 96 | uint8_t bb_type5[30]; 97 | uint8_t burst[255*2]; 98 | uint16_t crc; 99 | uint8_t *cur; 100 | uint32_t bb_rm3014, bb_rm3014_be; 101 | 102 | memset(type2, 0, sizeof(type2)); 103 | cur = type2; 104 | 105 | /* Use pdu_sync from testpdu.c */ 106 | cur += osmo_pbit2ubit(type2, pdu_schf, 268); 107 | 108 | crc = ~crc16_ccitt_bits(type2, 268); 109 | crc = swap16(crc); 110 | cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); 111 | 112 | /* Append 4 tail bits: type-2 bits */ 113 | cur += 4; 114 | 115 | printf("SCH/F type2: %s\n", osmo_ubit_dump(type2, 288)); 116 | 117 | /* Run rate 2/3 RCPC code: type-3 bits*/ 118 | { 119 | struct conv_enc_state *ces = calloc(1, sizeof(*ces)); 120 | conv_enc_init(ces); 121 | conv_enc_input(ces, type2, 288, master); 122 | get_punctured_rate(TETRA_RCPC_PUNCT_2_3, master, 432, type3); 123 | free(ces); 124 | } 125 | printf("SCH/F type3: %s\n", osmo_ubit_dump(type3, 432)); 126 | 127 | /* Run (432,103) block interleaving: type-4 bits */ 128 | block_interleave(432, 103, type3, type4); 129 | printf("SCH/F type4: %s\n", osmo_ubit_dump(type4, 432)); 130 | 131 | /* Run scrambling (all-zero): type-5 bits */ 132 | memcpy(type5, type4, 432); 133 | tetra_scramb_bits(SCRAMB_INIT, type5, 432); 134 | printf("SCH/F type5: %s\n", osmo_ubit_dump(type5, 432)); 135 | 136 | decode_schf(type5); 137 | 138 | /* Use pdu_acc_ass from testpdu.c */ 139 | /* Run it through (30,14) RM code: type-2=3=4 bits */ 140 | printf("AACH type-1: %s\n", osmo_ubit_dump(pdu_acc_ass, 2)); 141 | bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); 142 | printf("AACH RM3014: 0x0%x\n", bb_rm3014); 143 | /* convert to big endian */ 144 | bb_rm3014_be = htonl(bb_rm3014); 145 | /* shift two bits left as it is only a 30 bit value */ 146 | bb_rm3014_be <<= 2; 147 | osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); 148 | /* Run scrambling (all-zero): type-5 bits */ 149 | printf("AACH type-5: %s\n", osmo_ubit_dump(bb_type5, 30)); 150 | 151 | /* Finally, hand it into the physical layer */ 152 | build_norm_c_d_burst(burst, type5, bb_type5, type5+216, 0); 153 | printf("cont norm DL burst: %s\n", osmo_ubit_dump(burst, 255*2)); 154 | 155 | return 0; 156 | } 157 | 158 | 159 | static void decode_sb1(const uint8_t *bits) 160 | { 161 | uint8_t type4[1024]; 162 | uint8_t type3dp[1024*4]; 163 | uint8_t type3[1024]; 164 | uint8_t type2[1024]; 165 | 166 | printf("SB1 type5: %s\n", osmo_ubit_dump(bits, 120)); 167 | memcpy(type4, bits, 120); 168 | tetra_scramb_bits(SCRAMB_INIT, type4, 120); 169 | printf("SB1 type4: %s\n", osmo_ubit_dump(type4, 120)); 170 | /* Run (120,11) block deinterleaving: type-3 bits */ 171 | block_deinterleave(120, 11, type4, type3); 172 | printf("SB1 type3: %s\n", osmo_ubit_dump(type3, 120)); 173 | /* De-puncture */ 174 | memset(type3dp, 0xff, sizeof(type3dp)); 175 | tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 120, type3dp); 176 | printf("SB1 type3dp: %s\n", osmo_ubit_dump(type3dp, 80*4)); 177 | viterbi_dec_sb1_wrapper(type3dp, type2, 80); 178 | printf("SB1 type2: %s\n", osmo_ubit_dump(type2, 80)); 179 | 180 | { 181 | uint16_t crc; 182 | crc = crc16_ccitt_bits(type2, 76); 183 | printf("CRC COMP: 0x%04x ", crc); 184 | if (crc == 0x1d0f) 185 | printf("OK\n"); 186 | else { 187 | printf("WRONG\n"); 188 | num_crc_err++; 189 | } 190 | } 191 | 192 | printf("TN %s ", osmo_ubit_dump(type2+10, 2)); 193 | printf("MCC %s ", osmo_ubit_dump(type2+31, 10)); 194 | printf("MNC %s\n", osmo_ubit_dump(type2+41, 14)); 195 | } 196 | 197 | /* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ 198 | int build_sb() 199 | { 200 | uint8_t sb_type2[80]; 201 | uint8_t sb_master[80*4]; 202 | uint8_t sb_type3[120]; 203 | uint8_t sb_type4[120]; 204 | uint8_t sb_type5[120]; 205 | 206 | uint8_t si_type2[140]; 207 | uint8_t si_master[140*4]; 208 | uint8_t si_type3[216]; 209 | uint8_t si_type4[216]; 210 | uint8_t si_type5[216]; 211 | 212 | uint8_t bb_type5[30]; 213 | uint8_t burst[255*2]; 214 | uint16_t crc; 215 | uint8_t *cur; 216 | uint32_t bb_rm3014, bb_rm3014_be; 217 | 218 | memset(sb_type2, 0, sizeof(sb_type2)); 219 | cur = sb_type2; 220 | 221 | /* Use pdu_sync from testpdu.c */ 222 | cur += osmo_pbit2ubit(sb_type2, pdu_sync, 60); 223 | 224 | crc = ~crc16_ccitt_bits(sb_type2, 60); 225 | crc = swap16(crc); 226 | cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); 227 | 228 | /* Append 4 tail bits: type-2 bits */ 229 | cur += 4; 230 | 231 | printf("SYNC type2: %s\n", osmo_ubit_dump(sb_type2, 80)); 232 | 233 | /* Run rate 2/3 RCPC code: type-3 bits*/ 234 | { 235 | struct conv_enc_state *ces = calloc(1, sizeof(*ces)); 236 | conv_enc_init(ces); 237 | conv_enc_input(ces, sb_type2, 80, sb_master); 238 | get_punctured_rate(TETRA_RCPC_PUNCT_2_3, sb_master, 120, sb_type3); 239 | free(ces); 240 | } 241 | printf("SYNC type3: %s\n", osmo_ubit_dump(sb_type3, 120)); 242 | 243 | /* Run (120,11) block interleaving: type-4 bits */ 244 | block_interleave(120, 11, sb_type3, sb_type4); 245 | printf("SYNC type4: %s\n", osmo_ubit_dump(sb_type4, 120)); 246 | 247 | /* Run scrambling (all-zero): type-5 bits */ 248 | memcpy(sb_type5, sb_type4, 120); 249 | tetra_scramb_bits(SCRAMB_INIT, sb_type5, 120); 250 | printf("SYNC type5: %s\n", osmo_ubit_dump(sb_type5, 120)); 251 | 252 | decode_sb1(sb_type5); 253 | 254 | /* Use pdu_sysinfo from testpdu.c */ 255 | memset(si_type2, 0, sizeof(si_type2)); 256 | cur = si_type2; 257 | cur += osmo_pbit2ubit(si_type2, pdu_sysinfo, 124); 258 | 259 | /* Run it through CRC16-CCITT */ 260 | crc = ~crc16_ccitt_bits(si_type2, 124); 261 | crc = swap16(crc); 262 | cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); 263 | 264 | /* Append 4 tail bits: type-2 bits */ 265 | cur += 4; 266 | 267 | printf("SI type2: %s\n", osmo_ubit_dump(si_type2, 140)); 268 | 269 | /* Run rate 2/3 RCPC code: type-3 bits */ 270 | { 271 | struct conv_enc_state *ces = calloc(1, sizeof(*ces)); 272 | conv_enc_init(ces); 273 | conv_enc_input(ces, sb_type2, 144, si_master); 274 | get_punctured_rate(TETRA_RCPC_PUNCT_2_3, si_master, 216, si_type3); 275 | free(ces); 276 | } 277 | printf("SI type3: %s\n", osmo_ubit_dump(si_type3, 216)); 278 | 279 | /* Run (216,101) block interleaving: type-4 bits */ 280 | block_interleave(216, 101, si_type3, si_type4); 281 | printf("SI type4: %s\n", osmo_ubit_dump(si_type4, 216)); 282 | 283 | /* Run scrambling (all-zero): type-5 bits */ 284 | memcpy(si_type5, si_type4, 216); 285 | 286 | 287 | /* Use pdu_acc_ass from testpdu.c */ 288 | /* Run it through (30,14) RM code: type-2=3=4 bits */ 289 | printf("AACH type-1: %s\n", osmo_ubit_dump(pdu_acc_ass, 2)); 290 | bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); 291 | printf("AACH RM3014: 0x0%x\n", bb_rm3014); 292 | /* convert to big endian */ 293 | bb_rm3014_be = htonl(bb_rm3014); 294 | /* shift two bits left as it is only a 30 bit value */ 295 | bb_rm3014_be <<= 2; 296 | osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); 297 | /* Run scrambling (all-zero): type-5 bits */ 298 | printf("AACH type-5: %s\n", osmo_ubit_dump(bb_type5, 30)); 299 | 300 | /* Finally, hand it into the physical layer */ 301 | build_sync_c_d_burst(burst, sb_type5, bb_type5, si_type5); 302 | printf("cont sync DL burst: %s\n", osmo_ubit_dump(burst, 255*2)); 303 | 304 | return 0; 305 | } 306 | 307 | int main(int argc, char **argv) 308 | { 309 | int i; 310 | uint32_t ret; 311 | 312 | /* first: run some subsystem tests */ 313 | ret = tetra_punct_test(); 314 | if (ret < 0) 315 | exit(1); 316 | 317 | tetra_rm3014_init(); 318 | #if 0 319 | int err; 320 | uint16_t out; 321 | ret = tetra_rm3014_compute(0x1001); 322 | printf("RM3014: 0x%08x\n", ret); 323 | 324 | err = tetra_rm3014_decode(ret, &out); 325 | printf("RM3014: 0x%x error: %d\n", out, err); 326 | #endif 327 | 328 | /* finally, build some test PDUs and encocde them */ 329 | testpdu_init(); 330 | #if 0 331 | build_sb(); 332 | 333 | build_ndb_schf(); 334 | #else 335 | /* iterate over various random PDUs and throw them throguh the viterbi */ 336 | srand(time(NULL)); 337 | for (i = 0; i < 100; i++) { 338 | uint32_t r = rand(); 339 | osmo_store32le(r, pdu_sync); 340 | osmo_store32le(r, pdu_sync + 4); 341 | //build_sb(); 342 | 343 | osmo_store32le(r, pdu_schf); 344 | osmo_store32le(r, pdu_schf + 4); 345 | build_ndb_schf(); 346 | } 347 | #endif 348 | 349 | printf("total number of CRC Errors: %u\n", num_crc_err); 350 | 351 | exit(0); 352 | } 353 | --------------------------------------------------------------------------------