mirror of
				https://github.com/brmlab/osmo-tetra.git
				synced 2025-10-31 23:43:58 +01:00 
			
		
		
		
	initial import of Osmocom TETRA phy and lower MAC code
This commit is contained in:
		
							parent
							
								
									a4c4e5a1ab
								
							
						
					
					
						commit
						7ee08faee0
					
				
					 45 changed files with 4217 additions and 0 deletions
				
			
		
							
								
								
									
										106
									
								
								src/lower_mac/crc_simple.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/lower_mac/crc_simple.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| /* CRC16 (most likely the ITU variant) working on plain bits as a trial
 | ||||
|  * to find out if simple padding to the right is going to be enough | ||||
|  */ | ||||
| /* (C) 2011 by Holger Hans Peter Freyther
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <lower_mac/crc_simple.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * X.25 rec 2.2.7.4 Frame Check Sequence. This should be | ||||
|  * CRC ITU-T from the kernel or such. | ||||
|  */ | ||||
| #define GEN_POLY 0x1021 | ||||
| 
 | ||||
| uint16_t get_nth_bit(const uint8_t *input, int _bit) | ||||
| { | ||||
| 	uint16_t val; | ||||
| 	int byte = _bit / 8; | ||||
| 	int bit = 7 - _bit % 8; | ||||
| 
 | ||||
| 	val = (input[byte] & (1 << bit)) >> bit; | ||||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This is mostly from http://en.wikipedia.org/wiki/Computation_of_CRC
 | ||||
|  * Code fragment 2. Due some stupidity it took longer to implement than | ||||
|  * it should have taken. | ||||
|  */ | ||||
| uint16_t crc16_itut_bytes(uint16_t crc, const uint8_t *input, int number_bits) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < number_bits; ++i) { | ||||
| 		uint16_t bit = get_nth_bit(input, i); | ||||
| 
 | ||||
| 		crc ^= bit << 15; | ||||
| 		if ((crc & 0x8000)) { | ||||
| 			crc <<= 1; | ||||
| 			crc ^= GEN_POLY; | ||||
| 		} else { | ||||
| 			crc <<= 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return crc; | ||||
| } | ||||
| 
 | ||||
| uint16_t crc16_itut_bits(uint16_t crc, const uint8_t *input, int number_bits) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < number_bits; ++i) { | ||||
| 		uint16_t bit = input[i] & 0x1; | ||||
| 
 | ||||
| 		crc ^= bit << 15; | ||||
| 		if ((crc & 0x8000)) { | ||||
| 			crc <<= 1; | ||||
| 			crc ^= GEN_POLY; | ||||
| 		} else { | ||||
| 			crc <<= 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return crc; | ||||
| } | ||||
| 
 | ||||
| uint16_t crc16_itut_poly(uint16_t crc, uint32_t poly, const uint8_t *input, int number_bits) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < number_bits; ++i) { | ||||
| 		uint16_t bit = input[i] & 0x1; | ||||
| 
 | ||||
| 		crc ^= bit << 15; | ||||
| 		if ((crc & 0x8000)) { | ||||
| 			crc <<= 1; | ||||
| 			crc ^= poly; | ||||
| 		} else { | ||||
| 			crc <<= 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return crc; | ||||
| } | ||||
| 
 | ||||
| uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len) | ||||
| { | ||||
| 	return crc16_itut_bits(0xffff, bits, len); | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/lower_mac/crc_simple.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/lower_mac/crc_simple.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /* (C) 2011 by Holger Hans Peter Freyther
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef CRC_16 | ||||
| #define CRC_16 | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Code to generate a CRC16-ITU-T as of the X.25 specification and | ||||
|  * compatible with Linux's implementations. At least the polynom is | ||||
|  * coming from the X.25 spec. | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * This is working of bits packed together. The high bits will be | ||||
|  * accessed first. If you only pass one bit the array needs to contain | ||||
|  * a 0xF0 instead of a 0x01. This interface is compatible with the | ||||
|  * normal CRC interface (besides the length). | ||||
|  */ | ||||
| uint16_t crc16_itut_bytes(uint16_t crc, | ||||
| 			  const uint8_t *input, const int number_bits); | ||||
| 
 | ||||
| /**
 | ||||
|  * Each byte contains one bit. Calculate the CRC16-ITU-T for it. | ||||
|  */ | ||||
| uint16_t crc16_itut_bits(uint16_t crc, | ||||
| 			 const uint8_t *input, const int number_bits); | ||||
| 
 | ||||
| 
 | ||||
| uint16_t crc16_ccitt_bits(uint8_t *bits, unsigned int len); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										301
									
								
								src/lower_mac/tetra_conv_enc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/lower_mac/tetra_conv_enc.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,301 @@ | |||
| /* Tetra convolutional encoder, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ | ||||
| 
 | ||||
| /* This converts from type type-2 bits into type-3 bits */ | ||||
| 
 | ||||
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <osmocore/utils.h> | ||||
| 
 | ||||
| #include <tetra_common.h> | ||||
| #include <lower_mac/tetra_conv_enc.h> | ||||
| 
 | ||||
| static char *dump_state(struct conv_enc_state *ces) | ||||
| { | ||||
| 	static char pbuf[1024]; | ||||
| 	snprintf(pbuf, sizeof(pbuf), "%u-%u-%u-%u", ces->delayed[0], | ||||
| 		ces->delayed[1], ces->delayed[2], ces->delayed[3]); | ||||
| 	return pbuf; | ||||
| } | ||||
| 
 | ||||
| /* Mother code according to Section 8.2.3.1.1 */ | ||||
| static uint8_t conv_enc_in_bit(struct conv_enc_state *ces, uint8_t bit, uint8_t *out) | ||||
| { | ||||
| 	uint8_t g1, g2, g3, g4; | ||||
| 	uint8_t *delayed = ces->delayed; | ||||
| 
 | ||||
| 	DEBUGP("State in: %s, Input bit: %u: ", dump_state(ces), bit); | ||||
| 
 | ||||
| 	/* G1 = 1 + D + D4 */ | ||||
| 	g1 = (bit + delayed[0] + delayed[3]) % 2; | ||||
| 	/* G2 = 1 + D2 + D3 + D4 */ | ||||
| 	g2 = (bit + delayed[1] + delayed[2] + delayed[3]) % 2; | ||||
| 	/* G3 = 1 + D + D2 + D4 */ | ||||
| 	g3 = (bit + delayed[0] + delayed[1] + delayed[3]) % 2; | ||||
| 	/* G4 = 1 + D + D3 + D4 */ | ||||
| 	g4 = (bit + delayed[0] + delayed[2] + delayed[3]) % 2; | ||||
| 
 | ||||
| 	/* shift the state and input our new bit */ | ||||
| 	ces->delayed[3] = ces->delayed[2]; | ||||
| 	ces->delayed[2] = ces->delayed[1]; | ||||
| 	ces->delayed[1] = ces->delayed[0]; | ||||
| 	ces->delayed[0] = bit; | ||||
| 
 | ||||
| 	DEBUGP("Output bits: %u-%u-%u-%u, State out: %s\n", g1, g2, g3, g4, | ||||
| 		dump_state(ces)); | ||||
| 
 | ||||
| 	*out++ = g1; | ||||
| 	*out++ = g2; | ||||
| 	*out++ = g3; | ||||
| 	*out++ = g4; | ||||
| 
 | ||||
| 	return (g1 | (g2 << 1) | (g3 << 2) | (g4 << 3)); | ||||
| } | ||||
| 
 | ||||
| /* in: bit-per-byte (len), out: bit-per-byte (4*len) */ | ||||
| int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		conv_enc_in_bit(ces, in[i], out); | ||||
| 		out += 4; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int conv_enc_init(struct conv_enc_state *ces) | ||||
| { | ||||
| 	memset(ces->delayed, 0, sizeof(ces->delayed)); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Puncturing */ | ||||
| 
 | ||||
| const uint8_t P_rate2_3[] = { 0, 1, 2, 5 }; | ||||
| const uint8_t P_rate1_3[] = { 0, 1, 2, 3, 5, 6, 7 }; | ||||
| 
 | ||||
| struct puncturer { | ||||
| 	enum tetra_rcpc_puncturer type; | ||||
| 	const uint8_t *P; | ||||
| 	uint8_t t; | ||||
| 	uint32_t (*i_func)(uint32_t j); | ||||
| }; | ||||
| 
 | ||||
| static uint32_t i_func_equals(uint32_t j) | ||||
| { | ||||
| 	return j; | ||||
| } | ||||
| 
 | ||||
| static uint32_t i_func_292(uint32_t j) | ||||
| { | ||||
| 	return (j + ((j-1)/65)); | ||||
| } | ||||
| 
 | ||||
| static uint32_t i_func_148(uint32_t j) | ||||
| { | ||||
| 	return (j + ((j-1)/35)); | ||||
| } | ||||
| 
 | ||||
| /* Section 8.2.3.1.3 */ | ||||
| static const struct puncturer punct_2_3 = { | ||||
| 	.type = TETRA_RCPC_PUNCT_2_3, | ||||
| 	.P = P_rate2_3, | ||||
| 	.t = 3, | ||||
| 	.i_func = &i_func_equals, | ||||
| }; | ||||
| 
 | ||||
| /* Section 8.2.3.1.4 */ | ||||
| static const struct puncturer punct_1_3 = { | ||||
| 	.type = TETRA_RCPC_PUNCT_1_3, | ||||
| 	.P = P_rate1_3, | ||||
| 	.t = 6, | ||||
| 	.i_func = &i_func_equals, | ||||
| }; | ||||
| 
 | ||||
| /* Section 8.2.3.1.5 */ | ||||
| static const struct puncturer punct_292_432 = { | ||||
| 	.type = TETRA_RCPC_PUNCT_292_432, | ||||
| 	.P = P_rate2_3, | ||||
| 	.t = 3, | ||||
| 	.i_func = &i_func_292, | ||||
| }; | ||||
| 
 | ||||
| /* Section 8.2.3.1.6 */ | ||||
| static const struct puncturer punct_148_432 = { | ||||
| 	.type = TETRA_RCPC_PUNCT_148_432, | ||||
| 	.P = P_rate1_3, | ||||
| 	.t = 6, | ||||
| 	.i_func = &i_func_148, | ||||
| }; | ||||
| 
 | ||||
| static const struct puncturer *tetra_puncts[] = { | ||||
| 	[TETRA_RCPC_PUNCT_2_3]		= &punct_2_3, | ||||
| 	[TETRA_RCPC_PUNCT_1_3]		= &punct_1_3, | ||||
| 	[TETRA_RCPC_PUNCT_292_432]	= &punct_292_432, | ||||
| 	[TETRA_RCPC_PUNCT_148_432]	= &punct_148_432, | ||||
| }; | ||||
| 
 | ||||
| /* Puncture the mother code (in) and write 'len' symbols to out */ | ||||
| int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out) | ||||
| { | ||||
| 	const struct puncturer *punct; | ||||
| 	uint32_t i, j, k; | ||||
| 	uint8_t t; | ||||
| 	const uint8_t *P; | ||||
| 
 | ||||
| 	if (pu >= ARRAY_SIZE(tetra_puncts)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	punct = tetra_puncts[pu]; | ||||
| 	t = punct->t; | ||||
| 	P = punct->P; | ||||
| 
 | ||||
| 	/* Section 8.2.3.1.2 */ | ||||
| 	for (j = 1; j <= len; j++) { | ||||
| 		i = punct->i_func(j); | ||||
| 		k = 8 * ((i-1)/t) + P[i - t*((i-1)/t)]; | ||||
| 		DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); | ||||
| 		out[j-1] = in[k-1]; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* De-Puncture the 'len' type-3 bits (in) and write mother code to out */ | ||||
| int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out) | ||||
| { | ||||
| 	const struct puncturer *punct; | ||||
| 	uint32_t i, j, k; | ||||
| 	uint8_t t; | ||||
| 	const uint8_t *P; | ||||
| 
 | ||||
| 	if (pu >= ARRAY_SIZE(tetra_puncts)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	punct = tetra_puncts[pu]; | ||||
| 	t = punct->t; | ||||
| 	P = punct->P; | ||||
| 
 | ||||
| 	/* Section 8.2.3.1.2 */ | ||||
| 	for (j = 1; j <= len; j++) { | ||||
| 		i = punct->i_func(j); | ||||
| 		k = 8 * ((i-1)/t) + P[i - t*((i-1)/t)]; | ||||
| 		DEBUGP("j = %u, i = %u, k = %u\n", j, i, k); | ||||
| 		out[k-1] = in[j-1]; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct punct_test_param { | ||||
| 	uint16_t type2_len; | ||||
| 	uint16_t type3_len; | ||||
| 	enum tetra_rcpc_puncturer punct; | ||||
| }; | ||||
| 
 | ||||
| static const struct punct_test_param punct_test_params[] = { | ||||
| 	{ 80, 120, TETRA_RCPC_PUNCT_2_3 },		/* BSCH */ | ||||
| 	{ 292, 432, TETRA_RCPC_PUNCT_292_432 },		/* TCH/4.8 */ | ||||
| 	{ 148, 432, TETRA_RCPC_PUNCT_148_432 },		/* TCH/2.4 */ | ||||
| 	{ 144, 216, TETRA_RCPC_PUNCT_2_3 },		/* SCH/HD, BNCH, STCH */ | ||||
| 	{ 112, 168, TETRA_RCPC_PUNCT_2_3 },		/* SCH/HU */ | ||||
| 	{ 288, 432, TETRA_RCPC_PUNCT_2_3 },		/* SCH/F */ | ||||
| }; | ||||
| 
 | ||||
| static int mother_memcmp(const uint8_t *mother, const uint8_t *depunct, int len) | ||||
| { | ||||
| 	unsigned int i, equal = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) { | ||||
| 		/* ignore any 0xff-initialized part */ | ||||
| 		if (depunct[i] == 0xff) | ||||
| 			continue; | ||||
| 		if (depunct[i] != mother[i]) | ||||
| 			return -1; | ||||
| 		equal++; | ||||
| 	} | ||||
| 
 | ||||
| 	return equal; | ||||
| } | ||||
| 
 | ||||
| static int test_one_punct(const struct punct_test_param *ptp) | ||||
| { | ||||
| 	uint8_t *mother_buf; | ||||
| 	uint8_t *depunct_buf; | ||||
| 	uint8_t *type3_buf; | ||||
| 	int i, mother_len; | ||||
| 
 | ||||
| 	printf("==> Testing Puncture/Depuncture mode %u (%u/%u)\n", | ||||
| 		ptp->punct, ptp->type2_len, ptp->type3_len); | ||||
| 
 | ||||
| 	mother_len = ptp->type2_len*4; | ||||
| 	mother_buf = malloc(mother_len); | ||||
| 	depunct_buf = malloc(ptp->type2_len*4); | ||||
| 	type3_buf = malloc(ptp->type3_len); | ||||
| 
 | ||||
| 	/* initialize mother buffer with sequence of bytes starting at 0 */ | ||||
| 	for (i = 0; i < mother_len; i++) | ||||
| 		mother_buf[i] = i & 0xff; | ||||
| 
 | ||||
| 	/* puncture the mother_buf to type3_buf using rate 2/3 on 60 bits */ | ||||
| 	get_punctured_rate(ptp->punct, mother_buf, ptp->type3_len, type3_buf); | ||||
| 
 | ||||
| 	/* initialize the de-punctured buffer */ | ||||
| 	memset(depunct_buf, 0xff, mother_len); | ||||
| 
 | ||||
| 	/* de-puncture into the depunct_buf (i.e. what happens at the receiver) */ | ||||
| 	tetra_rcpc_depunct(ptp->punct, type3_buf, ptp->type3_len, depunct_buf); | ||||
| 
 | ||||
| 	DEBUGP("MOTH: %s\n", hexdump(mother_buf, mother_len)); | ||||
| 	DEBUGP("PUNC: %s\n", hexdump(type3_buf, ptp->type3_len)); | ||||
| 	DEBUGP("DEPU: %s\n", hexdump(depunct_buf, mother_len)); | ||||
| 
 | ||||
| 	i = mother_memcmp(mother_buf, depunct_buf, mother_len); | ||||
| 	if (i < 0) { | ||||
| 		fprintf(stderr, "Mother buf != Depunct buf\n"); | ||||
| 		return i; | ||||
| 	} else if (i != ptp->type3_len) { | ||||
| 		fprintf(stderr, "Depunct buf only has %u equal symbols, we need %u\n", | ||||
| 			i, ptp->type3_len); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	free(type3_buf); | ||||
| 	free(depunct_buf); | ||||
| 	free(mother_buf); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int tetra_punct_test(void) | ||||
| { | ||||
| 	int i, rc; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(punct_test_params); i++) { | ||||
| 		rc = test_one_punct(&punct_test_params[i]); | ||||
| 		if (rc < 0) | ||||
| 			return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/lower_mac/tetra_conv_enc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/lower_mac/tetra_conv_enc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| #ifndef TETRA_CONV_ENC_H | ||||
| #define TETRA_CONV_ENC_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| struct conv_enc_state { | ||||
| 	uint8_t delayed[4]; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* in: one-bit-per-byte out: 4bit-per-byte, both 'len' long */ | ||||
| int conv_enc_input(struct conv_enc_state *ces, uint8_t *in, int len, uint8_t *out); | ||||
| 
 | ||||
| int conv_enc_init(struct conv_enc_state *ces); | ||||
| 
 | ||||
| enum tetra_rcpc_puncturer { | ||||
| 	TETRA_RCPC_PUNCT_2_3, | ||||
| 	TETRA_RCPC_PUNCT_1_3, | ||||
| 	TETRA_RCPC_PUNCT_292_432, | ||||
| 	TETRA_RCPC_PUNCT_148_432, | ||||
| }; | ||||
| 
 | ||||
| /* Puncture the mother code (in) and write 'len' symbols to out */ | ||||
| int get_punctured_rate(enum tetra_rcpc_puncturer pu, uint8_t *in, int len, uint8_t *out); | ||||
| 
 | ||||
| 
 | ||||
| /* De-Puncture the 'len' type-3 bits (in) and write mother code to out */ | ||||
| int tetra_rcpc_depunct(enum tetra_rcpc_puncturer pu, const uint8_t *in, int len, uint8_t *out); | ||||
| 
 | ||||
| /* Self-test the puncturing/de-puncturing */ | ||||
| int tetra_punct_test(void); | ||||
| 
 | ||||
| #endif /* TETRA_CONV_ENC_H */ | ||||
							
								
								
									
										59
									
								
								src/lower_mac/tetra_interleave.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/lower_mac/tetra_interleave.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| /* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ | ||||
| 
 | ||||
| /* This converts from type type-3 bits into type-4 bits (and vice versa) */ | ||||
| 
 | ||||
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| 
 | ||||
| #include <osmocore/utils.h> | ||||
| 
 | ||||
| #include <tetra_common.h> | ||||
| 
 | ||||
| #include <lower_mac/tetra_interleave.h> | ||||
| 
 | ||||
| /* Section 8.2.4.1 Block interleaving for phase modulation */ | ||||
| static uint32_t block_interl_func(uint32_t K, uint32_t a, uint32_t i) | ||||
| { | ||||
| 	return (1 + ( (a * i) % K)); | ||||
| } | ||||
| 
 | ||||
| void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 1; i <= K; i++) { | ||||
| 		uint32_t k = block_interl_func(K, a, i); | ||||
| 		DEBUGP("interl: i=%u, k=%u\n", i, k); | ||||
| 		out[k-1] = in[i-1]; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 1; i <= K; i++) { | ||||
| 		uint32_t k = block_interl_func(K, a, i); | ||||
| 		DEBUGP("deinterl: i=%u, k=%u\n", i, k); | ||||
| 		out[i-1] = in[k-1]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/lower_mac/tetra_interleave.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/lower_mac/tetra_interleave.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| #ifndef TETRA_INTERLEAVE_H | ||||
| #define TETRA_INTERLEAVE_H | ||||
| /* Tetra block interleaver, according to ETSI EN 300 392-2 V3.2.1 (2007-09) */ | ||||
| 
 | ||||
| /* This converts from type type-3 bits into type-4 bits (and vice versa) */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| void block_interleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out); | ||||
| void block_deinterleave(uint32_t K, uint32_t a, const uint8_t *in, uint8_t *out); | ||||
| 
 | ||||
| #endif /* TETRA_INTERLEAVE_H */ | ||||
							
								
								
									
										257
									
								
								src/lower_mac/tetra_lower_mac.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/lower_mac/tetra_lower_mac.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,257 @@ | |||
| /* TETRA lower MAC layer main routine, between TP-SAP and TMV-SAP */ | ||||
| 
 | ||||
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include <osmocore/utils.h> | ||||
| 
 | ||||
| #include <tetra_common.h> | ||||
| #include <tetra_tdma.h> | ||||
| #include <phy/tetra_burst.h> | ||||
| #include <phy/tetra_burst_sync.h> | ||||
| #include <lower_mac/crc_simple.h> | ||||
| #include <lower_mac/tetra_scramb.h> | ||||
| #include <lower_mac/tetra_interleave.h> | ||||
| #include <lower_mac/tetra_conv_enc.h> | ||||
| #include <tetra_prim.h> | ||||
| #include "tetra_upper_mac.h" | ||||
| #include <lower_mac/viterbi.h> | ||||
| 
 | ||||
| 
 | ||||
| struct tetra_blk_param { | ||||
| 	const char *name; | ||||
| 	uint16_t type345_bits; | ||||
| 	uint16_t type2_bits; | ||||
| 	uint16_t type1_bits; | ||||
| 	uint16_t interleave_a; | ||||
| 	uint8_t have_crc16; | ||||
| }; | ||||
| 
 | ||||
| /* try to aggregate all of the magic numbers somewhere central */ | ||||
| static const struct tetra_blk_param tetra_blk_param[] = { | ||||
| 	[TPSAP_T_SB1] = { | ||||
| 		.name		= "SB1", | ||||
| 		.type345_bits 	= 120, | ||||
| 		.type2_bits	= 80, | ||||
| 		.type1_bits	= 60, | ||||
| 		.interleave_a	= 11, | ||||
| 		.have_crc16	= 1, | ||||
| 	}, | ||||
| 	[TPSAP_T_SB2] = { | ||||
| 		.name		= "SB2", | ||||
| 		.type345_bits	= 216, | ||||
| 		.type2_bits	= 144, | ||||
| 		.type1_bits	= 124, | ||||
| 		.interleave_a	= 101, | ||||
| 		.have_crc16	= 1, | ||||
| 	}, | ||||
| 	[TPSAP_T_NDB] = { | ||||
| 		.name		= "NDB", | ||||
| 		.type345_bits	= 216, | ||||
| 		.type2_bits	= 144, | ||||
| 		.type1_bits	= 124, | ||||
| 		.interleave_a	= 101, | ||||
| 		.have_crc16	= 1, | ||||
| 	}, | ||||
| 	[TPSAP_T_SCH_HU] = { | ||||
| 		.name		= "SCH/HU", | ||||
| 		.type345_bits	= 168, | ||||
| 		.type2_bits	= 112, | ||||
| 		.type1_bits	= 92, | ||||
| 		.interleave_a	= 13, | ||||
| 		.have_crc16	= 1, | ||||
| 	}, | ||||
| 	[TPSAP_T_SCH_F] = { | ||||
| 		.name		= "SCH/F", | ||||
| 		.type345_bits	= 432, | ||||
| 		.type2_bits	= 288, | ||||
| 		.type1_bits	= 268, | ||||
| 		.interleave_a	= 103, | ||||
| 		.have_crc16	= 1, | ||||
| 	}, | ||||
| 	[TPSAP_T_BBK] = { | ||||
| 		.name		= "BBK", | ||||
| 		.type345_bits	= 30, | ||||
| 		.type2_bits	= 30, | ||||
| 		.type1_bits	= 14, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| struct tetra_cell_data { | ||||
| 	uint16_t mcc; | ||||
| 	uint16_t mnc; | ||||
| 	uint8_t colour_code; | ||||
| 	struct tetra_tdma_time time; | ||||
| 
 | ||||
| 	uint32_t scramb_init; | ||||
| }; | ||||
| 
 | ||||
| static struct tetra_cell_data _tcd, *tcd = &_tcd; | ||||
| 
 | ||||
| int is_bsch(struct tetra_tdma_time *tm) | ||||
| { | ||||
| 	if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+1)%4)) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int is_bnch(struct tetra_tdma_time *tm) | ||||
| { | ||||
| 	if (tm->fn == 18 && tm->tn == 4 - ((tm->mn+3)%4)) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct tetra_tmvsap_prim *tmvsap_prim_alloc(uint16_t prim, uint8_t op) | ||||
| { | ||||
| 	struct tetra_tmvsap_prim *ttp; | ||||
| 
 | ||||
| 	//ttp = talloc_zero(NULL, struct tetra_tmvsap_prim);
 | ||||
| 	ttp = calloc(1, sizeof(struct tetra_tmvsap_prim)); | ||||
| 	ttp->oph.sap = TETRA_SAP_TMV; | ||||
| 	ttp->oph.primitive = prim; | ||||
| 	ttp->oph.operation = op; | ||||
| 
 | ||||
| 	return ttp; | ||||
| } | ||||
| 
 | ||||
| /* incoming TP-SAP UNITDATA.ind  from PHY into lower MAC */ | ||||
| void tp_sap_udata_ind(enum tp_sap_data_type type, const uint8_t *bits, unsigned int len, void *priv) | ||||
| { | ||||
| 	/* various intermediary buffers */ | ||||
| 	uint8_t type4[512]; | ||||
| 	uint8_t type3dp[512*4]; | ||||
| 	uint8_t type3[512]; | ||||
| 	uint8_t type2[512]; | ||||
| 
 | ||||
| 	const struct tetra_blk_param *tbp = &tetra_blk_param[type]; | ||||
| 	const char *time_str; | ||||
| 
 | ||||
| 	/* TMV-SAP.UNITDATA.ind primitive which we will send to the upper MAC */ | ||||
| 	struct tetra_tmvsap_prim *ttp; | ||||
| 	struct tmv_unitdata_param *tup; | ||||
| 
 | ||||
| 	ttp = tmvsap_prim_alloc(PRIM_TMV_UNITDATA, PRIM_OP_INDICATION); | ||||
| 	tup = &ttp->u.unitdata; | ||||
| 
 | ||||
| 	/* update the cell time */ | ||||
| 	memcpy(&tcd->time, &t_phy_state.time, sizeof(tcd->time)); | ||||
| 	time_str = tetra_tdma_time_dump(&tcd->time); | ||||
| 
 | ||||
| 	if (type == TPSAP_T_SB2 && is_bnch(&tcd->time)) { | ||||
| 		tup->lchan = TETRA_LC_BNCH; | ||||
| 		printf("BNCH FOLLOWS\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	printf("%s %s type5: %s\n", tbp->name, tetra_tdma_time_dump(&tcd->time), | ||||
| 		bitdump(bits, tbp->type345_bits)); | ||||
| 
 | ||||
| 	/* De-scramble, pay special attention to SB1 pre-defined scrambling */ | ||||
| 	memcpy(type4, bits, tbp->type345_bits); | ||||
| 	if (type == TPSAP_T_SB1) { | ||||
| 		tetra_scramb_bits(SCRAMB_INIT, type4, tbp->type345_bits); | ||||
| 		tup->scrambling_code = SCRAMB_INIT; | ||||
| 	} else { | ||||
| 		tetra_scramb_bits(tcd->scramb_init, type4, tbp->type345_bits); | ||||
| 		tup->scrambling_code = tcd->scramb_init; | ||||
| 	} | ||||
| 
 | ||||
| 	printf("%s %s type4: %s\n", tbp->name, time_str, | ||||
| 		bitdump(type4, tbp->type345_bits)); | ||||
| 
 | ||||
| 	if (tbp->interleave_a) { | ||||
| 		/* Run block deinterleaving: type-3 bits */ | ||||
| 		block_deinterleave(tbp->type345_bits, tbp->interleave_a, type4, type3); | ||||
| 		printf("%s %s type3: %s\n", tbp->name, time_str, | ||||
| 			bitdump(type3, tbp->type345_bits)); | ||||
| 		/* De-puncture */ | ||||
| 		memset(type3dp, 0xff, sizeof(type3dp)); | ||||
| 		tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, tbp->type345_bits, type3dp); | ||||
| 		printf("%s %s type3dp: %s\n", tbp->name, time_str, | ||||
| 			bitdump(type3dp, tbp->type2_bits*4)); | ||||
| 		viterbi_dec_sb1_wrapper(type3dp, type2, tbp->type2_bits); | ||||
| 		printf("%s %s type2: %s\n", tbp->name, time_str, | ||||
| 			bitdump(type2, tbp->type2_bits)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (tbp->have_crc16) { | ||||
| 		uint16_t crc = crc16_ccitt_bits(type2, tbp->type1_bits+16); | ||||
| 		printf("CRC COMP: 0x%04x ", crc); | ||||
| 		if (crc == TETRA_CRC_OK) { | ||||
| 			printf("OK\n"); | ||||
| 			tup->crc_ok = 1; | ||||
| 			printf("%s %s type1: %s\n", tbp->name, time_str, | ||||
| 				bitdump(type2, tbp->type1_bits)); | ||||
| 		} else | ||||
| 			printf("WRONG\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(tup->mac_block, type2, tbp->type1_bits); | ||||
| 	tup->mac_block_len = tbp->type1_bits; | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 	case TPSAP_T_SB1: | ||||
| 		printf("TMB-SAP SYNC CC %s(0x%02x) ", bitdump(type2+4, 6), bits_to_uint(type2+4, 6)); | ||||
| 		printf("TN %s(%u) ", bitdump(type2+10, 2), bits_to_uint(type2+10, 2)); | ||||
| 		printf("FN %s(%2u) ", bitdump(type2+12, 5), bits_to_uint(type2+12, 5)); | ||||
| 		printf("MN %s(%2u) ", bitdump(type2+17, 6), bits_to_uint(type2+17, 6)); | ||||
| 		printf("MCC %s(%u) ", bitdump(type2+31, 10), bits_to_uint(type2+31, 10)); | ||||
| 		printf("MNC %s(%u)\n", bitdump(type2+41, 14), bits_to_uint(type2+41, 14)); | ||||
| 		/* obtain information from SYNC PDU */ | ||||
| 		tcd->colour_code = bits_to_uint(type2+4, 6); | ||||
| 		tcd->time.tn = bits_to_uint(type2+10, 2); | ||||
| 		tcd->time.fn = bits_to_uint(type2+12, 5); | ||||
| 		tcd->time.mn = bits_to_uint(type2+17, 6); | ||||
| 		tcd->mcc = bits_to_uint(type2+31, 10); | ||||
| 		tcd->mnc = bits_to_uint(type2+41, 14); | ||||
| 		/* compute the scrambling code for the current cell */ | ||||
| 		tcd->scramb_init = tetra_scramb_get_init(tcd->mcc, tcd->mnc, tcd->colour_code); | ||||
| 		/* update the PHY layer time */ | ||||
| 		memcpy(&t_phy_state.time, &tcd->time, sizeof(t_phy_state.time)); | ||||
| 		tup->lchan = TETRA_LC_BSCH; | ||||
| 		break; | ||||
| 	case TPSAP_T_SB2: | ||||
| 	case TPSAP_T_NDB: | ||||
| 		/* FIXME: do something */ | ||||
| 		break; | ||||
| 	case TPSAP_T_BBK: | ||||
| 		/* FIXME: RM3014-decode */ | ||||
| 		tup->crc_ok = 1; | ||||
| 		memcpy(tup->mac_block, type4, tbp->type1_bits); | ||||
| 		printf("%s %s type1: %s\n", tbp->name, time_str, bitdump(tup->mac_block, tbp->type1_bits)); | ||||
| 		tup->lchan = TETRA_LC_AACH; | ||||
| 		break; | ||||
| 	case TPSAP_T_SCH_F: | ||||
| 		tup->lchan = TETRA_LC_SCH_F; | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* FIXME: do something */ | ||||
| 		break; | ||||
| 	} | ||||
| 	/* send Rx time along with the TMV-UNITDATA.ind primitive */ | ||||
| 	memcpy(&tup->tdma_time, &tcd->time, sizeof(tup->tdma_time)); | ||||
| 
 | ||||
| 	upper_mac_prim_recv(&ttp->oph, NULL); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										96
									
								
								src/lower_mac/tetra_rm3014.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/lower_mac/tetra_rm3014.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | |||
| /* Shortened (30,14) Reed-Muller (RM) code */ | ||||
| 
 | ||||
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <lower_mac/tetra_rm3014.h> | ||||
| 
 | ||||
| /* Generator matrix from Section 8.2.3.2  */ | ||||
| 
 | ||||
| static const uint8_t rm_30_14_gen[14][16] = { | ||||
| 	{ 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 }, | ||||
| 	{ 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, | ||||
| 	{ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, | ||||
| 	{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, | ||||
| 	{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0 }, | ||||
| 	{ 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0 }, | ||||
| 	{ 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0 }, | ||||
| 	{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 }, | ||||
| 	{ 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 }, | ||||
| 	{ 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1 }, | ||||
| 	{ 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1 }, | ||||
| 	{ 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1 }, | ||||
| 	{ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1 }, | ||||
| 	{ 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 } | ||||
| }; | ||||
| 
 | ||||
| static uint32_t rm_30_14_rows[14]; | ||||
| 
 | ||||
| 
 | ||||
| static uint32_t shift_bits_together(const uint8_t *bits, int len) | ||||
| { | ||||
| 	uint32_t ret = 0; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = len-1; i >= 0; i--) | ||||
| 		ret |= bits[i] << (len-1-i); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void tetra_rm3014_init(void) | ||||
| { | ||||
| 	int i; | ||||
| 	uint32_t val; | ||||
| 
 | ||||
| 	for (i = 0; i < 14; i++) { | ||||
| 		/* upper 14 bits identity matrix */ | ||||
| 		val = (1 << (16+13 - i)); | ||||
| 		/* lower 16 bits from rm_30_14_gen */ | ||||
| 		val |= shift_bits_together(rm_30_14_gen[i], 16); | ||||
| 		rm_30_14_rows[i] = val; | ||||
| 		printf("rm_30_14_rows[%u] = 0x%08x\n", i, val); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| uint32_t tetra_rm3014_compute(const uint16_t in) | ||||
| { | ||||
| 	int i; | ||||
| 	uint32_t val = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < 14; i++) { | ||||
| 		uint32_t bit = (in >> (14-1-i)) & 1; | ||||
| 		if (bit) | ||||
| 			val ^= rm_30_14_rows[i]; | ||||
| 		/* we can skip the 'else' as XOR with 0 has no effect */ | ||||
| 	} | ||||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This is a systematic code. We can remove the control bits | ||||
|  * and then check for an error. Maybe correct it in the future. | ||||
|  */ | ||||
| int tetra_rm3014_decode(const uint32_t inp, uint16_t *out) | ||||
| { | ||||
| 	*out = inp >> 16; | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/lower_mac/tetra_rm3014.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/lower_mac/tetra_rm3014.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| #ifndef TETRA_RM3014_H | ||||
| #define TETRA_RM3014_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| void tetra_rm3014_init(void); | ||||
| uint32_t tetra_rm3014_compute(const uint16_t in); | ||||
| 
 | ||||
| /**
 | ||||
|  * Decode @param inp to @param out and return if there was | ||||
|  * an error in the input. In the future this should correct | ||||
|  * the error or such. | ||||
|  */ | ||||
| int tetra_rm3013_decode(const uint32_t inp, uint16_t *out); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										99
									
								
								src/lower_mac/tetra_scramb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/lower_mac/tetra_scramb.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | |||
| /* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */ | ||||
| 
 | ||||
| /* This converts from type-4 to type-5 bits (and vice versa) */ | ||||
| 
 | ||||
| /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <lower_mac/tetra_scramb.h> | ||||
| 
 | ||||
| /* Tap macro for the standard XOR / Fibonacci form */ | ||||
| #define ST(x, y)	((x) >> (32-y)) | ||||
| 
 | ||||
| /* Tap macro and constant for the Galois form */ | ||||
| #define GL(x)		(1<<(x-1)) | ||||
| #define GALOIS_LFSR	(GL(32)|GL(26)|GL(23)|GL(22)|GL(16)|GL(12)|GL(11)|GL(10)|GL(8)|GL(7)|GL(5)|GL(4)|GL(2)|GL(1)) | ||||
| 
 | ||||
| #if 1 | ||||
| static uint8_t next_lfsr_bit(uint32_t *lf) | ||||
| { | ||||
| 	uint32_t lfsr = *lf; | ||||
| 	uint32_t bit; | ||||
| 
 | ||||
| 	/* taps: 32 26 23 22 16 12 11 10 8 7 5 4 2 1 */ | ||||
| 	bit = (ST(lfsr, 32) ^ ST(lfsr, 26) ^ ST(lfsr, 23) ^ ST(lfsr, 22) ^ | ||||
| 	       ST(lfsr, 16) ^ ST(lfsr, 12) ^ ST(lfsr, 11) ^ ST(lfsr, 10) ^ | ||||
| 	       ST(lfsr, 8) ^ ST(lfsr,  7) ^ ST(lfsr,  5) ^ ST(lfsr,  4) ^ | ||||
| 	       ST(lfsr, 2) ^ ST(lfsr,  1)) & 1; | ||||
| 	lfsr = (lfsr >> 1) | (bit << 31); | ||||
| 
 | ||||
| 	/* update the caller's LFSR state */ | ||||
| 	*lf = lfsr; | ||||
| 
 | ||||
| 	return bit & 0xff; | ||||
| } | ||||
| #else | ||||
| /* WARNING: this version somehow does not produce the desired result! */ | ||||
| static uint8_t next_lfsr_bit(uint32_t *lf) | ||||
| { | ||||
| 	uint32_t lfsr = *lf; | ||||
| 	uint32_t bit = lfsr & 1; | ||||
| 
 | ||||
| 	lfsr = (lfsr >> 1) ^ (uint32_t)(0 - ((lfsr & 1u) & GALOIS_LFSR)); | ||||
| 
 | ||||
| 	*lf = lfsr; | ||||
| 
 | ||||
| 	return bit; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) | ||||
| 		out[i] = next_lfsr_bit(&lfsr_init); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */ | ||||
| int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < len; i++) | ||||
| 		out[i] ^= next_lfsr_bit(&lfsr_init); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour) | ||||
| { | ||||
| 	uint32_t scramb_init; | ||||
| 
 | ||||
| 	mcc &= 0x3ff; | ||||
| 	mnc &= 0x3fff; | ||||
| 	colour &= 0x3f; | ||||
| 
 | ||||
| 	scramb_init = colour | (mnc << 6) | (mcc << 20); | ||||
| 	scramb_init = (scramb_init << 2) | SCRAMB_INIT; | ||||
| 
 | ||||
| 	return scramb_init; | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/lower_mac/tetra_scramb.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/lower_mac/tetra_scramb.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| #ifndef TETRA_SCRAMB_H | ||||
| #define TETRA_SCRAMB_H | ||||
| /* TETRA scrambling according to Section 8.2.5 of EN 300 392-2 V3.2.1 */ | ||||
| 
 | ||||
| /* This converts from type-4 to type-5 bits (and vice versa) */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /* Section 8.2.5.2:
 | ||||
| 	* For scrambling of BSCH, all bits e(1) to e(30) shall equal to zero | ||||
| 		* p(k) = e(1-k) for k = -29, ... 0 | ||||
| 		* p(k) = 1	for k = -31, -30 | ||||
|  */ | ||||
| #define SCRAMB_INIT	3 | ||||
| 
 | ||||
| uint32_t tetra_scramb_get_init(uint16_t mcc, uint16_t mnc, uint8_t colour); | ||||
| 
 | ||||
| int tetra_scramb_get_bits(uint32_t lfsr_init, uint8_t *out, int len); | ||||
| 
 | ||||
| /* XOR the bitstring at 'out/len' using the TETRA scrambling LFSR */ | ||||
| int tetra_scramb_bits(uint32_t lfsr_init, uint8_t *out, int len); | ||||
| 
 | ||||
| #endif /* TETRA_SCRAMB_H */ | ||||
							
								
								
									
										25
									
								
								src/lower_mac/viterbi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/lower_mac/viterbi.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <lower_mac/viterbi_cch.h> | ||||
| 
 | ||||
| void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count) | ||||
| { | ||||
| 	int8_t vit_inp[864*4]; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < sym_count*4; i++) { | ||||
| 		switch (in[i]) { | ||||
| 		case 0: | ||||
| 			vit_inp[i] = 127; | ||||
| 			break; | ||||
| 		case 0xff: | ||||
| 			vit_inp[i] = 0; | ||||
| 			break; | ||||
| 		default: | ||||
| 			vit_inp[i] = -127; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	conv_cch_decode(vit_inp, out, sym_count); | ||||
| } | ||||
							
								
								
									
										6
									
								
								src/lower_mac/viterbi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/lower_mac/viterbi.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| #ifndef VITERBI_H | ||||
| #define VITERBI_H | ||||
| 
 | ||||
| void viterbi_dec_sb1_wrapper(const uint8_t *in, uint8_t *out, unsigned int sym_count); | ||||
| 
 | ||||
| #endif /* VITERBI_H */ | ||||
							
								
								
									
										163
									
								
								src/lower_mac/viterbi_cch.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/lower_mac/viterbi_cch.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | |||
| /* (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
 | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <lower_mac/viterbi.h> | ||||
| 
 | ||||
| 
 | ||||
| #define CONV_CCH_N		4 | ||||
| #define CONV_CCH_K		5 | ||||
| #define CONV_CCH_N_STATES	(1<<(CONV_CCH_K-1)) | ||||
| 
 | ||||
| #define MAX_AE			0x00ffffff | ||||
| 
 | ||||
| 
 | ||||
| static const uint8_t conv_cch_next_output[CONV_CCH_N_STATES][2] = { | ||||
| 	{  0, 15 }, { 11,  4 }, {  6,  9 }, { 13,  2 }, | ||||
| 	{  5, 10 }, { 14,  1 }, {  3, 12 }, {  8,  7 }, | ||||
| 	{ 15,  0 }, {  4, 11 }, {  9,  6 }, {  2, 13 }, | ||||
| 	{ 10,  5 }, {  1, 14 }, { 12,  3 }, {  7,  8 }, | ||||
| }; | ||||
| 
 | ||||
| static const uint8_t conv_cch_next_state[CONV_CCH_N_STATES][2] = { | ||||
| 	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 }, | ||||
| 	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, | ||||
| 	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 }, | ||||
| 	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int conv_cch_encode(uint8_t *input, uint8_t *output, int n) | ||||
| { | ||||
| 	uint8_t state; | ||||
| 	int i; | ||||
| 
 | ||||
| 	state = 0; | ||||
| 
 | ||||
| 	for (i=0; i<n; i++) { | ||||
| 		int bit = input[i]; | ||||
| 		uint8_t out = conv_cch_next_output[state][bit]; | ||||
| 		state = conv_cch_next_state[state][bit]; | ||||
| 
 | ||||
| 		output[(i<<2)  ] = (out >> 3) & 1; | ||||
| 		output[(i<<2)+1] = (out >> 2) & 1; | ||||
| 		output[(i<<2)+2] = (out >> 1) & 1; | ||||
| 		output[(i<<2)+3] =  out       & 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int conv_cch_decode(int8_t *input, uint8_t *output, int n) | ||||
| { | ||||
| 	int i, s, b; | ||||
| 	unsigned int ae[CONV_CCH_N_STATES]; | ||||
| 	unsigned int ae_next[CONV_CCH_N_STATES]; | ||||
| 	int8_t in_sym[CONV_CCH_N]; | ||||
| 	int8_t ev_sym[CONV_CCH_N]; | ||||
| 	int state_history[CONV_CCH_N_STATES][n]; | ||||
| 	int min_ae; | ||||
| 	int min_state; | ||||
| 	int cur_state; | ||||
| 
 | ||||
| 	/* Initial error (only state 0 is valid) */ | ||||
| 	ae[0] = 0; | ||||
| 	for (i=1; i<CONV_CCH_N_STATES; i++) { | ||||
| 		ae[i] = MAX_AE; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Scan the treillis */ | ||||
| 	for (i=0; i<n; i++) { | ||||
| 		/* Reset next accumulated error */ | ||||
| 		for (s=0; s<CONV_CCH_N_STATES; s++) { | ||||
| 			ae_next[s] = MAX_AE; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Get input */ | ||||
| 		in_sym[0] = input[(i<<2)  ]; | ||||
| 		in_sym[1] = input[(i<<2)+1]; | ||||
| 		in_sym[2] = input[(i<<2)+2]; | ||||
| 		in_sym[3] = input[(i<<2)+3]; | ||||
| 
 | ||||
| 		/* Scan all states */ | ||||
| 		for (s=0; s<CONV_CCH_N_STATES; s++) | ||||
| 		{ | ||||
| 			/* Scan possible input bits */ | ||||
| 			for (b=0; b<2; b++) | ||||
| 			{ | ||||
| 				int nae; | ||||
| 
 | ||||
| 				/* Next output and state */ | ||||
| 				uint8_t out   = conv_cch_next_output[s][b]; | ||||
| 				uint8_t state = conv_cch_next_state[s][b]; | ||||
| 
 | ||||
| 				/* Expand */ | ||||
| 				ev_sym[0] = (out >> 3) & 1 ? -127 : 127; | ||||
| 				ev_sym[1] = (out >> 2) & 1 ? -127 : 127; | ||||
| 				ev_sym[2] = (out >> 1) & 1 ? -127 : 127; | ||||
| 				ev_sym[3] =  out       & 1 ? -127 : 127; | ||||
| 
 | ||||
| 				/* New error for this path */ | ||||
| 				#define DIFF(x,y) (((x-y)*(x-y)) >> 9) | ||||
| 				nae = ae[s] + \ | ||||
| 					DIFF(ev_sym[0], in_sym[0]) + \ | ||||
| 					DIFF(ev_sym[1], in_sym[1]) + \ | ||||
| 					DIFF(ev_sym[2], in_sym[2]) + \ | ||||
| 					DIFF(ev_sym[3], in_sym[3]); | ||||
| 
 | ||||
| 				/* Is it survivor */ | ||||
| 				if (ae_next[state] > nae) { | ||||
| 					ae_next[state] = nae; | ||||
| 					state_history[state][i+1] = s; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Copy accumulated error */ | ||||
| 		memcpy(ae, ae_next, sizeof(int) * CONV_CCH_N_STATES); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Find state with least error */ | ||||
| 	min_ae = MAX_AE; | ||||
| 	min_state = -1; | ||||
| 
 | ||||
| 	for (s=0; s<CONV_CCH_N_STATES; s++) | ||||
| 	{ | ||||
| 		if (ae[s] < min_ae) { | ||||
| 			min_ae = ae[s]; | ||||
| 			min_state = s; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Traceback */ | ||||
| 	cur_state = min_state; | ||||
| 	for (i=n-1; i >= 0; i--) | ||||
| 	{ | ||||
| 		min_state = cur_state; | ||||
| 		cur_state = state_history[cur_state][i+1]; | ||||
| 		if (conv_cch_next_state[cur_state][0] == min_state) | ||||
| 			output[i] = 0; | ||||
| 		else | ||||
| 			output[i] = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/lower_mac/viterbi_cch.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/lower_mac/viterbi_cch.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| #ifndef VITERBI_CCH_H | ||||
| #define VITERBI_CCH_H | ||||
| 
 | ||||
| int conv_cch_encode(uint8_t *input, uint8_t *output, int n); | ||||
| int conv_cch_decode(int8_t *input, uint8_t *output, int n); | ||||
| 
 | ||||
| #endif /* VITERBI_CCH_H */ | ||||
							
								
								
									
										159
									
								
								src/lower_mac/viterbi_tch.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/lower_mac/viterbi_tch.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | |||
| /* (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
 | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <lower_mac/viterbi_tch.h> | ||||
| 
 | ||||
| 
 | ||||
| #define CONV_TCH_N		3 | ||||
| #define CONV_TCH_K		5 | ||||
| #define CONV_TCH_N_STATES	(1<<(CONV_TCH_K-1)) | ||||
| 
 | ||||
| #define MAX_AE			0x00ffffff | ||||
| 
 | ||||
| 
 | ||||
| static const uint8_t conv_tch_next_output[CONV_TCH_N_STATES][2] = { | ||||
| 	{ 0, 7 }, { 6, 1 }, { 5, 2 }, { 3, 4 },  | ||||
| 	{ 6, 1 }, { 0, 7 }, { 3, 4 }, { 5, 2 },  | ||||
| 	{ 7, 0 }, { 1, 6 }, { 2, 5 }, { 4, 3 },  | ||||
| 	{ 1, 6 }, { 7, 0 }, { 4, 3 }, { 2, 5 },  | ||||
| }; | ||||
| 
 | ||||
| static const uint8_t conv_tch_next_state[CONV_TCH_N_STATES][2] = { | ||||
| 	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },  | ||||
| 	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },  | ||||
| 	{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },  | ||||
| 	{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },  | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int conv_tch_encode(uint8_t *input, uint8_t *output, int n) | ||||
| { | ||||
| 	uint8_t state; | ||||
| 	int i; | ||||
| 
 | ||||
| 	state = 0; | ||||
| 
 | ||||
| 	for (i=0; i<n; i++) { | ||||
| 		int bit = input[i]; | ||||
| 		uint8_t out = conv_tch_next_output[state][bit]; | ||||
| 		state = conv_tch_next_state[state][bit]; | ||||
| 
 | ||||
| 		output[(i<<2)  ] = (out >> 2) & 1; | ||||
| 		output[(i<<2)+1] = (out >> 1) & 1; | ||||
| 		output[(i<<2)+2] =  out       & 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int conv_tch_decode(int8_t *input, uint8_t *output, int n) | ||||
| { | ||||
| 	int i, s, b; | ||||
| 	unsigned int ae[CONV_TCH_N_STATES]; | ||||
| 	unsigned int ae_next[CONV_TCH_N_STATES]; | ||||
| 	int8_t in_sym[CONV_TCH_N]; | ||||
| 	int8_t ev_sym[CONV_TCH_N]; | ||||
| 	int state_history[CONV_TCH_N_STATES][n]; | ||||
| 	int min_ae; | ||||
| 	int min_state; | ||||
| 	int cur_state; | ||||
| 
 | ||||
| 	/* Initial error (only state 0 is valid) */ | ||||
| 	ae[0] = 0; | ||||
| 	for (i=1; i<CONV_TCH_N_STATES; i++) { | ||||
| 		ae[i] = MAX_AE; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Scan the treillis */ | ||||
| 	for (i=0; i<n; i++) { | ||||
| 		/* Reset next accumulated error */ | ||||
| 		for (s=0; s<CONV_TCH_N_STATES; s++) { | ||||
| 			ae_next[s] = MAX_AE; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Get input */ | ||||
| 		in_sym[0] = input[(i<<2)  ]; | ||||
| 		in_sym[1] = input[(i<<2)+1]; | ||||
| 		in_sym[2] = input[(i<<2)+2]; | ||||
| 
 | ||||
| 		/* Scan all states */ | ||||
| 		for (s=0; s<CONV_TCH_N_STATES; s++) | ||||
| 		{ | ||||
| 			/* Scan possible input bits */ | ||||
| 			for (b=0; b<2; b++) | ||||
| 			{ | ||||
| 				int nae; | ||||
| 
 | ||||
| 				/* Next output and state */ | ||||
| 				uint8_t out   = conv_tch_next_output[s][b]; | ||||
| 				uint8_t state = conv_tch_next_state[s][b]; | ||||
| 
 | ||||
| 				/* Expand */ | ||||
| 				ev_sym[0] = (out >> 2) & 1 ? -127 : 127; | ||||
| 				ev_sym[1] = (out >> 1) & 1 ? -127 : 127; | ||||
| 				ev_sym[2] =  out       & 1 ? -127 : 127; | ||||
| 
 | ||||
| 				/* New error for this path */ | ||||
| 				#define DIFF(x,y) (((x-y)*(x-y)) >> 9) | ||||
| 				nae = ae[s] + \ | ||||
| 					DIFF(ev_sym[0], in_sym[0]) + \ | ||||
| 					DIFF(ev_sym[1], in_sym[1]) + \ | ||||
| 					DIFF(ev_sym[2], in_sym[2]); | ||||
| 
 | ||||
| 				/* Is it survivor */ | ||||
| 				if (ae_next[state] > nae) { | ||||
| 					ae_next[state] = nae; | ||||
| 					state_history[state][i+1] = s; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/* Copy accumulated error */ | ||||
| 		memcpy(ae, ae_next, sizeof(int) * CONV_TCH_N_STATES); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Find state with least error */ | ||||
| 	min_ae = MAX_AE; | ||||
| 	min_state = -1; | ||||
| 
 | ||||
| 	for (s=0; s<CONV_TCH_N_STATES; s++) | ||||
| 	{ | ||||
| 		if (ae[s] < min_ae) { | ||||
| 			min_ae = ae[s]; | ||||
| 			min_state = s; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Traceback */ | ||||
| 	cur_state = min_state; | ||||
| 	for (i=n-1; i >= 0; i--) | ||||
| 	{ | ||||
| 		min_state = cur_state; | ||||
| 		cur_state = state_history[cur_state][i+1]; | ||||
| 		if (conv_tch_next_state[cur_state][0] == min_state) | ||||
| 			output[i] = 0; | ||||
| 		else | ||||
| 			output[i] = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/lower_mac/viterbi_tch.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/lower_mac/viterbi_tch.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| #ifndef VITERBI_TCH_H | ||||
| #define VITERBI_TCH_H | ||||
| 
 | ||||
| int conv_tch_encode(uint8_t *input, uint8_t *output, int n); | ||||
| int conv_tch_decode(int8_t *input, uint8_t *output, int n); | ||||
| 
 | ||||
| #endif /* VITERBI_TCH_H */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Harald Welte
						Harald Welte