mirror of
				https://github.com/brmlab/osmo-tetra.git
				synced 2025-10-31 15:33:59 +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
				
			
		
							
								
								
									
										345
									
								
								src/phy/tetra_burst.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								src/phy/tetra_burst.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,345 @@ | |||
| /* Implementation of TETRA Physical Layer, i.e. what is _below_
 | ||||
|  * CRC, FEC, Interleaving and Scrambling */ | ||||
| 
 | ||||
| /* (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 <phy/tetra_burst.h> | ||||
| 
 | ||||
| #define DQPSK4_BITS_PER_SYM	2 | ||||
| 
 | ||||
| #define SB_BLK1_OFFSET	((6+1+40)*DQPSK4_BITS_PER_SYM) | ||||
| #define SB_BBK_OFFSET	((6+1+40+60+19)*DQPSK4_BITS_PER_SYM) | ||||
| #define SB_BLK2_OFFSET	((6+1+40+60+19+15)*DQPSK4_BITS_PER_SYM) | ||||
| 
 | ||||
| #define SB_BLK1_BITS	(60*DQPSK4_BITS_PER_SYM) | ||||
| #define SB_BBK_BITS	(15*DQPSK4_BITS_PER_SYM) | ||||
| #define SB_BLK2_BITS	(108*DQPSK4_BITS_PER_SYM) | ||||
| 
 | ||||
| #define NDB_BLK1_OFFSET ((5+1+1)*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BBK1_OFFSET	((5+1+1+108)*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BBK2_OFFSET	((5+1+1+108+7+11)*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BLK2_OFFSET	((5+1+1+108+7+11+8)*DQPSK4_BITS_PER_SYM) | ||||
| 
 | ||||
| #define NDB_BBK1_BITS	(7*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BBK2_BITS	(8*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BLK_BITS	(108*DQPSK4_BITS_PER_SYM) | ||||
| #define NDB_BBK_BITS	SB_BBK_BITS | ||||
| 
 | ||||
| 
 | ||||
| /* 9.4.4.3.1 Frequency Correction Field */ | ||||
| static const uint8_t f_bits[80] = { | ||||
| 	/* f1 .. f8 = 1 */ | ||||
| 	1, 1, 1, 1, 1, 1, 1, 1, | ||||
| 	/* f73..f80 = 1*/ | ||||
| 	[72] = 1, [73] = 1, [74] = 1, [75] = 1, | ||||
| 	[76] = 1, [77] = 1, [78] = 1, [79] = 1 }; | ||||
| 
 | ||||
| /* 9.4.4.3.2 Normal Training Sequence */ | ||||
| static const uint8_t n_bits[22] = { 1,1, 0,1, 0,0, 0,0, 1,1, 1,0, 1,0, 0,1, 1,1, 0,1, 0,0 }; | ||||
| static const uint8_t p_bits[22] = { 0,1, 1,1, 1,0, 1,0, 0,1, 0,0, 0,0, 1,1, 0,1, 1,1, 1,0 }; | ||||
| static const uint8_t q_bits[22] = { 1,0, 1,1, 0,1, 1,1, 0,0, 0,0, 0,1, 1,0, 1,0, 1,1, 0,1 }; | ||||
| static const uint8_t N_bits[33] = { 1,1,1, 0,0,1, 1,0,1, 1,1,1, 0,0,0, 1,1,1, 1,0,0, 0,1,1, 1,1,0, 0,0,0, 0,0,0 }; | ||||
| static const uint8_t P_bits[33] = { 1,0,1, 0,1,1, 1,1,1, 1,0,1, 0,1,0, 1,0,1, 1,1,0, 0,0,1, 1,0,0, 0,1,0, 0,1,0 }; | ||||
| 
 | ||||
| /* 9.4.4.3.3 Extended training sequence */ | ||||
| static const uint8_t x_bits[30] = { 1,0, 0,1, 1,1, 0,1, 0,0, 0,0, 1,1, 1,0, 1,0, 0,1, 1,1, 0,1, 0,0, 0,0, 1,1 }; | ||||
| static const uint8_t X_bits[45] = { 0,1,1,1,0,0,1,1,0,1,0,0,0,0,1,0,0,0,1,1,1,0,1,1,0,1,0,1,0,1,1,1,1,1,0,1,0,0,0,0,0,1,1,1,0 }; | ||||
| 
 | ||||
| /* 9.4.4.3.4 Synchronization training sequence */ | ||||
| static const uint8_t y_bits[38] = { 1,1, 0,0, 0,0, 0,1, 1,0, 0,1, 1,1, 0,0, 1,1, 1,0, 1,0, 0,1, 1,1, 0,0, 0,0, 0,1, 1,0, 0,1, 1,1 }; | ||||
| 
 | ||||
| /* 9.4.4.3.5 Tail bits */ | ||||
| static const uint8_t t_bits[4] = { 1, 1, 0, 0 }; | ||||
| static const uint8_t T_bits[6] = { 1, 1, 1, 0, 0, 0 }; | ||||
| 
 | ||||
| /* 9.4.4.3.6 Phase adjustment bits */ | ||||
| enum phase_adj_bits { HA, HB, HC, HD, HE, HF, HG, HH, HI, HJ }; | ||||
| struct phase_adj_n { | ||||
| 	uint16_t n1; | ||||
| 	uint16_t n2; | ||||
| }; | ||||
| 
 | ||||
| /* Table 8.14 */ | ||||
| static const struct phase_adj_n phase_adj_n[] = { | ||||
| 	[HA] = { .n1 = 8,	.n2 = 122 }, | ||||
| 	[HB] = { .n1 = 123,	.n2 = 249 }, | ||||
| 	[HC] = { .n1 = 8,	.n2 = 108 }, | ||||
| 	[HD] = { .n1 = 109,	.n2 = 249 }, | ||||
| 	[HE] = { .n1 = 112,	.n2 = 230 }, | ||||
| 	[HF] = { .n1 = 1,	.n2 = 111 }, | ||||
| 	[HG] = { .n1 = 3,	.n2 = 117 }, | ||||
| 	[HH] = { .n1 = 118,	.n2 = 224 }, | ||||
| 	[HI] = { .n1 = 3,	.n2 = 103 }, | ||||
| 	[HJ] = { .n1 = 104,	.n2 = 224 }, | ||||
| }; | ||||
| 
 | ||||
| static const int8_t bits2phase[] = { | ||||
| 	[0]	= 1,		/* +pi/4 needs to become -pi/4 */ | ||||
| 	[1]	= -1,		/* -pi/4 needs to become +pi/4 */ | ||||
| 	[2]	= +3,		/* +3pi/4 needs to become -3pi/4 */ | ||||
| 	[3]	= -3,		/* -3pi/4 needs to become +3pi/4 */ | ||||
| }; | ||||
| 
 | ||||
| /* offset everything by 3 in order to get positive array index */ | ||||
| #define PHASE(x)	((x)+3) | ||||
| struct phase2bits { | ||||
| 	int8_t phase; | ||||
| 	uint8_t bits[2]; | ||||
| }; | ||||
| static const struct phase2bits phase2bits[] = { | ||||
| 	[PHASE(-3)]	= { -3, {1, 1} }, | ||||
| 	[PHASE(-1)]	= { -1, {0, 1} }, | ||||
| 	[PHASE( 1)]	= {  1, {0, 0} }, | ||||
| 	[PHASE( 3)]	= {  3, {1, 0} }, | ||||
| }; | ||||
| 	 | ||||
| static int32_t calc_phase_adj(int32_t phase) | ||||
| { | ||||
| 	int32_t adj_phase = -(phase % 8); | ||||
| 
 | ||||
| 	/* 'wrap around' to get a value in the range between +3 / -3 */ | ||||
| 	if (adj_phase > 3) | ||||
| 		adj_phase -= 8; | ||||
| 	else if (adj_phase < -3) | ||||
| 		adj_phase += 8; | ||||
| 
 | ||||
| 	return adj_phase; | ||||
| } | ||||
| 
 | ||||
| /* return the cumulative phase shift of all bits (in units of pi/4) */ | ||||
| int32_t sum_up_phase(const uint8_t *bits, unsigned int sym_count) | ||||
| { | ||||
| 	uint8_t sym_in; | ||||
| 	int32_t sum_phase = 0; | ||||
| 	unsigned int n; | ||||
| 
 | ||||
| 	for (n = 0; n < sym_count; n++) { | ||||
| 		/* offset '-1' due to array-index starting at 0 */ | ||||
| 		uint32_t bn = 2*n; | ||||
| 		sym_in = bits[bn]; | ||||
| 		sym_in |= bits[bn+1] << 1; | ||||
| 
 | ||||
| 		sum_phase += bits2phase[sym_in]; | ||||
| 	} | ||||
| 
 | ||||
| 	printf("phase sum over %u symbols: %dpi/4, mod 8 = %dpi/4, wrap = %dpi/4\n", | ||||
| 		sym_count, sum_phase, sum_phase % 8, calc_phase_adj(sum_phase)); | ||||
| 	return sum_phase; | ||||
| } | ||||
| 
 | ||||
| /* compute phase adjustment bits according to 'pa' and write them to {out, out+2} */ | ||||
| void put_phase_adj_bits(const uint8_t *bits, enum phase_adj_bits pa, uint8_t *out) | ||||
| { | ||||
| 	int32_t sum_phase, adj_phase; | ||||
| 	const struct phase_adj_n *pan = &phase_adj_n[pa]; | ||||
| 	const struct phase2bits *p2b; | ||||
| 
 | ||||
| 	/* offset '-1' due to array-index starting at 0 */ | ||||
| 	sum_phase = sum_up_phase(bits + 2*(pan->n1-1), 1 + pan->n2 - pan->n1); | ||||
| 	adj_phase = calc_phase_adj(sum_phase); | ||||
| 
 | ||||
| 	p2b = &phase2bits[adj_phase]; | ||||
| 
 | ||||
| 	*out++ = p2b->bits[0]; | ||||
| 	*out++ = p2b->bits[1]; | ||||
| } | ||||
| 
 | ||||
| /* 9.4.4.2.6 Synchronization continuous downlink burst */ | ||||
| int build_sync_c_d_burst(uint8_t *buf, const uint8_t *sb, const uint8_t *bb, const uint8_t *bkn) | ||||
| { | ||||
| 	uint8_t *cur = buf; | ||||
| 	uint8_t *hc, *hd; | ||||
| 
 | ||||
| 	/* Normal Training Sequence: q11 to q22 */ | ||||
| 	memcpy(cur, q_bits+10, 12); | ||||
| 	cur += 12; | ||||
| 
 | ||||
| 	/* Phase adjustment bits: hc1 to hc2 */ | ||||
| 	hc = cur; | ||||
| 	cur += 2; | ||||
| 
 | ||||
| 	/* Frequency correction: f1 to f80 */ | ||||
| 	memcpy(cur, f_bits, 80); | ||||
| 	cur += 80; | ||||
| 
 | ||||
| 	/* Scrambled synchronization block 1 bits: sb(1) to sb(120) */ | ||||
| 	memcpy(cur, sb, 120); | ||||
| 	cur += 120; | ||||
| 
 | ||||
| 	/* Synchronization training sequence: y1 to y38 */ | ||||
| 	memcpy(cur, y_bits, 38); | ||||
| 	cur+= 38; | ||||
| 
 | ||||
| 	/* Scrambled broadcast bits: bb(1) to bb(30) */ | ||||
| 	memcpy(cur, bb, 30); | ||||
| 	cur += 30; | ||||
| 
 | ||||
| 	/* Scrambled block2 bits: bkn2(1) to bkn2(216) */ | ||||
| 	memcpy(cur, bkn, 216); | ||||
| 	cur += 216; | ||||
| 
 | ||||
| 	/* Phase adjustment bits: hd1 to hd2 */ | ||||
| 	hd = cur; | ||||
| 	cur += 2; | ||||
| 
 | ||||
| 	/* Normal training sequence 3: q1 to q10 */ | ||||
| 	memcpy(cur, q_bits, 10); | ||||
| 	cur += 10; | ||||
| 
 | ||||
| 	/* put in the phase adjustment bits */ | ||||
| 	put_phase_adj_bits(buf, HC, hc); | ||||
| 	put_phase_adj_bits(buf, HD, hd); | ||||
| 
 | ||||
| 	return cur - buf; | ||||
| } | ||||
| 
 | ||||
| /* 9.4.4.2.5 Normal continuous downlink burst */ | ||||
| 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) | ||||
| { | ||||
| 	uint8_t *cur = buf; | ||||
| 	uint8_t *ha, *hb; | ||||
| 
 | ||||
| 	/* Normal Training Sequence: q11 to q22 */ | ||||
| 	memcpy(cur, q_bits+10, 12); | ||||
| 	cur += 12; | ||||
| 
 | ||||
| 	/* Phase adjustment bits: hc1 to hc2 */ | ||||
| 	ha = cur; | ||||
| 	cur += 2; | ||||
| 
 | ||||
| 	/* Scrambled block 1 bits: bkn1(1) to bkn1(216) */ | ||||
| 	memcpy(cur, bkn1, 216); | ||||
| 	cur += 216; | ||||
| 
 | ||||
| 	/* Scrambled broadcast bits: bb(1) to bb(14) */ | ||||
| 	memcpy(cur, bb, 14); | ||||
| 	cur += 14; | ||||
| 
 | ||||
| 	/* Normal training sequence: n1 to n22 or p1 to p22 */ | ||||
| 	if (two_log_chan) | ||||
| 		memcpy(cur, p_bits, 22); | ||||
| 	else | ||||
| 		memcpy(cur, n_bits, 22); | ||||
| 	cur += 22; | ||||
| 
 | ||||
| 	/* Scrambled broadcast bits: bb(15) to bb(30) */ | ||||
| 	memcpy(cur, bb+14, 16); | ||||
| 	cur += 16; | ||||
| 
 | ||||
| 	/* Scrambled block2 bits: bkn2(1) to bkn2(216) */ | ||||
| 	memcpy(cur, bkn2, 216); | ||||
| 	cur += 216; | ||||
| 
 | ||||
| 	/* Phase adjustment bits: hd1 to hd2 */ | ||||
| 	hb = cur; | ||||
| 	cur += 2; | ||||
| 
 | ||||
| 	/* Normal training sequence 3: q1 to q10 */ | ||||
| 	memcpy(cur, q_bits, 10); | ||||
| 	cur += 10; | ||||
| 
 | ||||
| 	/* put in the phase adjustment bits */ | ||||
| 	put_phase_adj_bits(buf, HA, ha); | ||||
| 	put_phase_adj_bits(buf, HB, hb); | ||||
| 
 | ||||
| 	return cur - buf; | ||||
| } | ||||
| 
 | ||||
| int tetra_find_train_seq(const uint8_t *in, unsigned int end_of_in, | ||||
| 			 uint32_t mask_of_train_seq, unsigned int *offset) | ||||
| { | ||||
| 	const uint8_t *cur; | ||||
| 
 | ||||
| 	for (cur = in; cur < in + end_of_in; cur++) { | ||||
| 		int remain_len = (in + end_of_in) - cur; | ||||
| 
 | ||||
| 		if (mask_of_train_seq & (1 << TETRA_TRAIN_SYNC) && | ||||
| 		    remain_len >= sizeof(y_bits) && | ||||
| 		    !memcmp(cur, y_bits, sizeof(y_bits))) { | ||||
| 			*offset = (cur - in); | ||||
| 			return TETRA_TRAIN_SYNC; | ||||
| 		} | ||||
| 		if (mask_of_train_seq & (1 << TETRA_TRAIN_NORM_1) && | ||||
| 		    remain_len >= sizeof(n_bits) && | ||||
| 		    !memcmp(cur, n_bits, sizeof(n_bits))) { | ||||
| 			*offset = (cur - in); | ||||
| 			return TETRA_TRAIN_NORM_1; | ||||
| 		} | ||||
| 		if (mask_of_train_seq & (1 << TETRA_TRAIN_NORM_2) && | ||||
| 		    remain_len >= sizeof(p_bits) && | ||||
| 		    !memcmp(cur, p_bits, sizeof(p_bits))) { | ||||
| 			*offset = (cur - in); | ||||
| 			return TETRA_TRAIN_NORM_2; | ||||
| 		} | ||||
| 		if (mask_of_train_seq & (1 << TETRA_TRAIN_NORM_3) && | ||||
| 		    remain_len >= sizeof(q_bits) && | ||||
| 		    !memcmp(cur, q_bits, sizeof(q_bits))) { | ||||
| 			*offset = (cur - in); | ||||
| 			return TETRA_TRAIN_NORM_3; | ||||
| 		} | ||||
| 		if (mask_of_train_seq & (1 << TETRA_TRAIN_EXT) && | ||||
| 		    remain_len >= sizeof(x_bits) && | ||||
| 		    !memcmp(cur, x_bits, sizeof(x_bits))) { | ||||
| 			*offset = (cur - in); | ||||
| 			return TETRA_TRAIN_EXT; | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| void tetra_burst_rx_cb(const uint8_t *burst, unsigned int len, enum tetra_train_seq type, void *priv) | ||||
| { | ||||
| 	uint8_t bbk_buf[NDB_BBK_BITS]; | ||||
| 	uint8_t ndbf_buf[2*NDB_BLK_BITS]; | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 	case TETRA_TRAIN_SYNC: | ||||
| 		/* Split SB1, SB2 and Broadcast Block */ | ||||
| 		/* send three parts of the burst via TP-SAP into lower MAC */ | ||||
| 		tp_sap_udata_ind(TPSAP_T_SB1, burst+SB_BLK1_OFFSET, SB_BLK1_BITS, priv); | ||||
| 		tp_sap_udata_ind(TPSAP_T_BBK, burst+SB_BBK_OFFSET, SB_BBK_BITS, priv); | ||||
| 		tp_sap_udata_ind(TPSAP_T_SB2, burst+SB_BLK2_OFFSET, SB_BLK2_BITS, priv); | ||||
| 		break; | ||||
| 	case TETRA_TRAIN_NORM_2: | ||||
| 		/* re-combine the broadcast block */ | ||||
| 		memcpy(bbk_buf, burst+NDB_BBK1_OFFSET, NDB_BBK1_BITS); | ||||
| 		memcpy(bbk_buf+NDB_BBK1_BITS, burst+NDB_BBK2_OFFSET, NDB_BBK2_BITS); | ||||
| 		/* send three parts of the burst via TP-SAP into lower MAC */ | ||||
| 		tp_sap_udata_ind(TPSAP_T_BBK, bbk_buf, NDB_BBK_BITS, priv); | ||||
| 		tp_sap_udata_ind(TPSAP_T_NDB, burst+NDB_BLK1_OFFSET, NDB_BLK_BITS, priv); | ||||
| 		tp_sap_udata_ind(TPSAP_T_NDB, burst+NDB_BLK2_OFFSET, NDB_BLK_BITS, priv); | ||||
| 		break; | ||||
| 	case TETRA_TRAIN_NORM_1: | ||||
| 		/* re-combine the broadcast block */ | ||||
| 		memcpy(bbk_buf, burst+NDB_BBK1_OFFSET, NDB_BBK1_BITS); | ||||
| 		memcpy(bbk_buf+NDB_BBK1_BITS, burst+NDB_BBK2_OFFSET, NDB_BBK2_BITS); | ||||
| 		/* re-combine the two parts */ | ||||
| 		memcpy(ndbf_buf, burst+NDB_BLK1_OFFSET, NDB_BLK_BITS); | ||||
| 		memcpy(ndbf_buf+NDB_BLK_BITS, burst+NDB_BLK2_OFFSET, NDB_BLK_BITS); | ||||
| 		/* send two parts of the burst via TP-SAP into lower MAC */ | ||||
| 		tp_sap_udata_ind(TPSAP_T_BBK, bbk_buf, NDB_BBK_BITS, priv); | ||||
| 		tp_sap_udata_ind(TPSAP_T_SCH_F, ndbf_buf, 2*NDB_BLK_BITS, priv); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/phy/tetra_burst.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/phy/tetra_burst.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| #ifndef TETRA_BURST_H | ||||
| #define TETRA_BURST_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| enum tp_sap_data_type { | ||||
| 	TPSAP_T_SB1, | ||||
| 	TPSAP_T_SB2, | ||||
| 	TPSAP_T_NDB, | ||||
| 	TPSAP_T_BBK, | ||||
| 	TPSAP_T_SCH_HU, | ||||
| 	TPSAP_T_SCH_F, | ||||
| }; | ||||
| 
 | ||||
| extern void tp_sap_udata_ind(enum tp_sap_data_type type, const uint8_t *bits, unsigned int len, void *priv); | ||||
| 
 | ||||
| /* 9.4.4.2.6 Synchronization continuous downlink burst */ | ||||
| int build_sync_c_d_burst(uint8_t *buf, const uint8_t *sb, const uint8_t *bb, const uint8_t *bkn); | ||||
| 
 | ||||
| /* 9.4.4.2.5 Normal continuous downlink burst */ | ||||
| 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); | ||||
| 
 | ||||
| enum tetra_train_seq { | ||||
| 	TETRA_TRAIN_NORM_1, | ||||
| 	TETRA_TRAIN_NORM_2, | ||||
| 	TETRA_TRAIN_NORM_3, | ||||
| 	TETRA_TRAIN_SYNC, | ||||
| 	TETRA_TRAIN_EXT, | ||||
| }; | ||||
| 
 | ||||
| /* find a TETRA training sequence in the burst buffer indicated */ | ||||
| int tetra_find_train_seq(const uint8_t *in, unsigned int end_of_in, | ||||
| 			 uint32_t mask_of_train_seq, unsigned int *offset); | ||||
| 
 | ||||
| #endif /* TETRA_BURST_H */ | ||||
							
								
								
									
										141
									
								
								src/phy/tetra_burst_sync.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/phy/tetra_burst_sync.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | |||
| /* Implementation of TETRA burst synchronization */ | ||||
| 
 | ||||
| /* (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 <string.h> | ||||
| 
 | ||||
| #include <osmocore/utils.h> | ||||
| 
 | ||||
| //#define DEBUG
 | ||||
| 
 | ||||
| #include <tetra_common.h> | ||||
| #include <phy/tetra_burst.h> | ||||
| #include <tetra_tdma.h> | ||||
| #include <phy/tetra_burst_sync.h> | ||||
| 
 | ||||
| struct tetra_phy_state t_phy_state; | ||||
| 
 | ||||
| void tetra_burst_rx_cb(const uint8_t *burst, unsigned int len, enum tetra_train_seq type, void *priv); | ||||
| 
 | ||||
| /* input a raw bitstream into the tetra burst synchronizaer */ | ||||
| int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len) | ||||
| { | ||||
| 	int rc; | ||||
| 	unsigned int train_seq_offs; | ||||
| 	int cpy_len; | ||||
| 
 | ||||
| 	DEBUGP("burst_sync_in: %u bits, state %u\n", len, trs->state); | ||||
| 
 | ||||
| 	/* First: append the data to the bitbuf */ | ||||
| 	if (sizeof(trs->bitbuf) - trs->bits_in_buf < len) | ||||
| 		cpy_len = sizeof(trs->bitbuf) - trs->bits_in_buf; | ||||
| 	else | ||||
| 		cpy_len = len; | ||||
| 	memcpy(trs->bitbuf + trs->bits_in_buf, bits, cpy_len); | ||||
| 	trs->bits_in_buf += cpy_len; | ||||
| 
 | ||||
| 	switch (trs->state) { | ||||
| 	case RX_S_UNLOCKED: | ||||
| 		if (trs->bits_in_buf < TETRA_BITS_PER_TS*2) { | ||||
| 			/* wait for more bits to arrive */ | ||||
| 			DEBUGP("-> waiting for more bits to arrive\n"); | ||||
| 			return cpy_len; | ||||
| 		} | ||||
| 		DEBUGP("-> trying to find training sequence between bit %u and %u\n", | ||||
| 			trs->bitbuf_start_bitnum, trs->bits_in_buf); | ||||
| 		rc = tetra_find_train_seq(trs->bitbuf, trs->bits_in_buf, | ||||
| 					  (1 << TETRA_TRAIN_SYNC), &train_seq_offs); | ||||
| 		if (rc < 0) | ||||
| 			return rc; | ||||
| 		printf("found SYNC training sequence in bit #%u\n", train_seq_offs); | ||||
| 		trs->state = RX_S_KNOW_FSTART; | ||||
| 		trs->next_frame_start_bitnum = trs->bitbuf_start_bitnum + train_seq_offs + 296; | ||||
| #if 0 | ||||
| 		if (train_seq_offs < 214) { | ||||
| 			/* not enough leading bits for start of burst */ | ||||
| 			/* we just drop everything that we received so far */ | ||||
| 			trs->bitbuf_start_bitnum += trs->bits_in_buf; | ||||
| 			trs->bits_in_buf = 0; | ||||
| 		} | ||||
| #endif | ||||
| 		break; | ||||
| 	case RX_S_KNOW_FSTART: | ||||
| 		/* we are locked, i.e. already know when the next frame should start */ | ||||
| 		if (trs->bitbuf_start_bitnum + trs->bits_in_buf < trs->next_frame_start_bitnum) | ||||
| 			return 0; | ||||
| 		else { | ||||
| 			/* shift start of frame to start of bitbuf */ | ||||
| 			int offset = trs->next_frame_start_bitnum - trs->bitbuf_start_bitnum; | ||||
| 			int bits_remaining = trs->bits_in_buf - offset; | ||||
| 
 | ||||
| 			memmove(trs->bitbuf, trs->bitbuf+offset, bits_remaining); | ||||
| 			trs->bits_in_buf = bits_remaining; | ||||
| 			trs->bitbuf_start_bitnum += offset; | ||||
| 
 | ||||
| 			trs->next_frame_start_bitnum += TETRA_BITS_PER_TS; | ||||
| 			trs->state = RX_S_LOCKED; | ||||
| 		} | ||||
| 	case RX_S_LOCKED: | ||||
| 		if (trs->bits_in_buf < TETRA_BITS_PER_TS) { | ||||
| 			/* not sufficient data for the full frame yet */ | ||||
| 			return cpy_len; | ||||
| 		} else { | ||||
| 			/* we have successfully received (at least) one frame */ | ||||
| 			tetra_tdma_time_add_tn(&t_phy_state.time, 1); | ||||
| 			printf("\nBURST: %s\n", bitdump(trs->bitbuf, TETRA_BITS_PER_TS)); | ||||
| 			rc = tetra_find_train_seq(trs->bitbuf, trs->bits_in_buf, | ||||
| 						  (1 << TETRA_TRAIN_NORM_1)| | ||||
| 						  (1 << TETRA_TRAIN_NORM_2)| | ||||
| 						  (1 << TETRA_TRAIN_SYNC), &train_seq_offs); | ||||
| 			switch (rc) { | ||||
| 			case TETRA_TRAIN_SYNC: | ||||
| 				if (train_seq_offs == 214) | ||||
| 					tetra_burst_rx_cb(trs->bitbuf, TETRA_BITS_PER_TS, rc, trs->burst_cb_priv); | ||||
| 				else { | ||||
| 					fprintf(stderr, "#### SYNC burst at offset %u?!?\n", train_seq_offs); | ||||
| 					trs->state = RX_S_UNLOCKED; | ||||
| 				} | ||||
| 				break; | ||||
| 			case TETRA_TRAIN_NORM_1: | ||||
| 			case TETRA_TRAIN_NORM_2: | ||||
| 			case TETRA_TRAIN_NORM_3: | ||||
| 				if (train_seq_offs == 244) | ||||
| 					tetra_burst_rx_cb(trs->bitbuf, TETRA_BITS_PER_TS, rc, trs->burst_cb_priv); | ||||
| 				else | ||||
| 					fprintf(stderr, "#### SYNC burst at offset %u?!?\n", train_seq_offs); | ||||
| 				break; | ||||
| 			default: | ||||
| 				fprintf(stderr, "#### could not find successive burst training sequence\n"); | ||||
| 				trs->state = RX_S_UNLOCKED; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			/* move remainder to start of buffer */ | ||||
| 			trs->bits_in_buf -= TETRA_BITS_PER_TS; | ||||
| 			memmove(trs->bitbuf, trs->bitbuf+TETRA_BITS_PER_TS, trs->bits_in_buf); | ||||
| 			trs->bitbuf_start_bitnum += TETRA_BITS_PER_TS; | ||||
| 			trs->next_frame_start_bitnum += TETRA_BITS_PER_TS; | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	} | ||||
| 	return cpy_len; | ||||
| } | ||||
							
								
								
									
										26
									
								
								src/phy/tetra_burst_sync.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/phy/tetra_burst_sync.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| #ifndef TETRA_BURST_SYNC_H | ||||
| #define TETRA_BURST_SYNC_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| enum rx_state { | ||||
| 	RX_S_UNLOCKED,		/* we're completely unlocked */ | ||||
| 	RX_S_KNOW_FSTART,	/* we know the next frame start */ | ||||
| 	RX_S_LOCKED,		/* fully locked */ | ||||
| }; | ||||
| 
 | ||||
| struct tetra_rx_state { | ||||
| 	enum rx_state state; | ||||
| 	unsigned int bits_in_buf;		/* how many bits are currently in bitbuf */ | ||||
| 	uint8_t bitbuf[4096]; | ||||
| 	unsigned int bitbuf_start_bitnum;	/* bit number at first element in bitbuf */ | ||||
| 	unsigned int next_frame_start_bitnum;	/* frame start expected at this bitnum */ | ||||
| 
 | ||||
| 	void *burst_cb_priv; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* input a raw bitstream into the tetra burst synchronizaer */ | ||||
| int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len); | ||||
| 
 | ||||
| #endif /* TETRA_BURST_SYNC_H */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Harald Welte
						Harald Welte