mirror of
				https://github.com/brmlab/osmo-tetra.git
				synced 2025-10-30 06:53: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
				
			
		
							
								
								
									
										8
									
								
								src/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | *.o | ||||||
|  | *.a | ||||||
|  | *.pyc | ||||||
|  | .*.swp | ||||||
|  | conv_enc_test | ||||||
|  | burst_test | ||||||
|  | float_to_bits | ||||||
|  | crc_test | ||||||
							
								
								
									
										25
									
								
								src/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | CFLAGS=-g -O0 -Wall `pkg-config --cflags libosmocore 2> /dev/null` -I. | ||||||
|  | LDFLAGS=`pkg-config --libs libosmocore 2> /dev/null` -losmocore | ||||||
|  | 
 | ||||||
|  | all: conv_enc_test crc_test burst_test float_to_bits | ||||||
|  | 
 | ||||||
|  | %.o: %.c | ||||||
|  | 	$(CC) $(CFLAGS) -c $^ -o $@ | ||||||
|  | 
 | ||||||
|  | libosmo-tetra-phy.a: phy/tetra_burst_sync.o phy/tetra_burst.o | ||||||
|  | 	$(AR) r $@ $^ | ||||||
|  | 
 | ||||||
|  | libosmo-tetra-mac.a: lower_mac/tetra_conv_enc.o tetra_tdma.o lower_mac/tetra_scramb.o lower_mac/tetra_rm3014.o lower_mac/tetra_interleave.o lower_mac/crc_simple.o tetra_common.o lower_mac/viterbi.o lower_mac/viterbi_cch.o lower_mac/viterbi_tch.o lower_mac/tetra_lower_mac.o tetra_upper_mac.o tetra_mac_pdu.o tetra_mle_pdu.o tetra_mm_pdu.o tetra_gsmtap.o | ||||||
|  | 	$(AR) r $@ $^ | ||||||
|  | 
 | ||||||
|  | float_to_bits: float_to_bits.o | ||||||
|  | 
 | ||||||
|  | crc_test: crc_test.o tetra_common.o libosmo-tetra-mac.a | ||||||
|  | 
 | ||||||
|  | burst_test: burst_test.o libosmo-tetra-phy.a libosmo-tetra-mac.a | ||||||
|  | 
 | ||||||
|  | conv_enc_test: conv_enc_test.o testpdu.o libosmo-tetra-phy.a libosmo-tetra-mac.a | ||||||
|  | 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	@rm -f conv_enc_test crc_test burst_test float_to_bits *.o phy/*.o lower_mac/*.o *.a | ||||||
							
								
								
									
										74
									
								
								src/burst_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/burst_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | /* Test program for tetra burst synchronizer */ | ||||||
|  | 
 | ||||||
|  | /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | 
 | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include <phy/tetra_burst.h> | ||||||
|  | #include <phy/tetra_burst_sync.h> | ||||||
|  | #include "tetra_gsmtap.h" | ||||||
|  | 
 | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  | 	struct tetra_rx_state *trs; | ||||||
|  | 
 | ||||||
|  | 	if (argc < 2) { | ||||||
|  | 		fprintf(stderr, "Usage: %s <file_with_1_byte_per_bit>\n", argv[0]); | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fd = open(argv[1], O_RDONLY); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		perror("open"); | ||||||
|  | 		exit(2); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tetra_gsmtap_init("localhost", 0); | ||||||
|  | 
 | ||||||
|  | 	trs = calloc(1, sizeof(*trs)); | ||||||
|  | 
 | ||||||
|  | 	while (1) { | ||||||
|  | 		uint8_t buf[64]; | ||||||
|  | 		int len; | ||||||
|  | 
 | ||||||
|  | 		len = read(fd, buf, sizeof(buf)); | ||||||
|  | 		if (len < 0) { | ||||||
|  | 			perror("read"); | ||||||
|  | 			exit(1); | ||||||
|  | 		} else if (len == 0) { | ||||||
|  | 			printf("EOF"); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		tetra_burst_sync_in(trs, buf, len); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	free(trs); | ||||||
|  | 
 | ||||||
|  | 	exit(0); | ||||||
|  | } | ||||||
							
								
								
									
										351
									
								
								src/conv_enc_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								src/conv_enc_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,351 @@ | ||||||
|  | /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <time.h> | ||||||
|  | 
 | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | 
 | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | #include <osmocore/bits.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include <lower_mac/crc_simple.h> | ||||||
|  | #include <lower_mac/tetra_conv_enc.h> | ||||||
|  | #include <lower_mac/tetra_interleave.h> | ||||||
|  | #include <lower_mac/tetra_scramb.h> | ||||||
|  | #include <lower_mac/tetra_rm3014.h> | ||||||
|  | #include <lower_mac/viterbi.h> | ||||||
|  | #include <phy/tetra_burst.h> | ||||||
|  | #include "testpdu.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define swap16(x) ((x)<<8)|((x)>>8) | ||||||
|  | 
 | ||||||
|  | static unsigned int num_crc_err; | ||||||
|  | 
 | ||||||
|  | /* incoming TP-SAP UNITDATA.ind  from PHY into lower MAC */ | ||||||
|  | void tp_sap_udata_ind(enum tp_sap_data_type type, const uint8_t *bits, unsigned int len, void *priv) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void decode_schf(const uint8_t *bits) | ||||||
|  | { | ||||||
|  | 	uint8_t type4[1024]; | ||||||
|  | 	uint8_t type3dp[1024*4]; | ||||||
|  | 	uint8_t type3[1024]; | ||||||
|  | 	uint8_t type2[1024]; | ||||||
|  | 
 | ||||||
|  | 	printf("SCH/f type5: %s\n", bitdump(bits, 432)); | ||||||
|  | 	memcpy(type4, bits, 432); | ||||||
|  | 	tetra_scramb_bits(SCRAMB_INIT, type4, 432); | ||||||
|  | 	printf("SCH/F type4: %s\n", bitdump(type4, 432)); | ||||||
|  | 	/* Run (120,11) block deinterleaving: type-3 bits */ | ||||||
|  | 	block_deinterleave(432, 103, type4, type3); | ||||||
|  | 	printf("SCH/F type3: %s\n", bitdump(type3, 432)); | ||||||
|  | 	/* De-puncture */ | ||||||
|  | 	memset(type3dp, 0xff, sizeof(type3dp)); | ||||||
|  | 	tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 432, type3dp); | ||||||
|  | 	printf("SCH/F type3dp: %s\n", bitdump(type3dp, 288*4)); | ||||||
|  | 	viterbi_dec_sb1_wrapper(type3dp, type2, 288); | ||||||
|  | 	printf("SCH/F type2: %s\n", bitdump(type2, 288)); | ||||||
|  | 
 | ||||||
|  | 	{ | ||||||
|  | 		uint16_t crc; | ||||||
|  | 		crc = crc16_ccitt_bits(type2, 288-4); | ||||||
|  | 		printf("CRC COMP: 0x%04x ", crc); | ||||||
|  | 		if (crc == 0x1d0f) | ||||||
|  | 			printf("OK\n"); | ||||||
|  | 		else { | ||||||
|  | 			printf("WRONG\n"); | ||||||
|  | 			num_crc_err++; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	printf("SCH/F type1: %s\n", bitdump(type2, 268)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ | ||||||
|  | int build_ndb_schf() | ||||||
|  | { | ||||||
|  | 	/* input: 268 type-1 bits */ | ||||||
|  | 	uint8_t type2[284]; | ||||||
|  | 	uint8_t master[284*4]; | ||||||
|  | 	uint8_t type3[432]; | ||||||
|  | 	uint8_t type4[432]; | ||||||
|  | 	uint8_t type5[432]; | ||||||
|  | 	uint8_t bb_type5[30]; | ||||||
|  | 	uint8_t burst[255*2]; | ||||||
|  | 	uint16_t crc; | ||||||
|  | 	uint8_t *cur; | ||||||
|  | 	uint32_t bb_rm3014, bb_rm3014_be; | ||||||
|  | 
 | ||||||
|  | 	memset(type2, 0, sizeof(type2)); | ||||||
|  | 	cur = type2; | ||||||
|  | 
 | ||||||
|  | 	/* Use pdu_sync from testpdu.c */ | ||||||
|  | 	cur += osmo_pbit2ubit(type2, pdu_schf, 268); | ||||||
|  | 
 | ||||||
|  | 	crc = ~crc16_ccitt_bits(type2, 268); | ||||||
|  | 	crc = swap16(crc); | ||||||
|  | 	cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); | ||||||
|  | 
 | ||||||
|  | 	/* Append 4 tail bits: type-2 bits */ | ||||||
|  | 	cur += 4; | ||||||
|  | 
 | ||||||
|  | 	printf("SCH/F type2: %s\n", bitdump(type2, 288)); | ||||||
|  | 
 | ||||||
|  | 	/* Run rate 2/3 RCPC code: type-3 bits*/ | ||||||
|  | 	{ | ||||||
|  | 		struct conv_enc_state *ces = calloc(1, sizeof(*ces)); | ||||||
|  | 		conv_enc_init(ces); | ||||||
|  | 		conv_enc_input(ces, type2, 288, master); | ||||||
|  | 		get_punctured_rate(TETRA_RCPC_PUNCT_2_3, master, 432, type3); | ||||||
|  | 		free(ces); | ||||||
|  | 	} | ||||||
|  | 	printf("SCH/F type3: %s\n", bitdump(type3, 432)); | ||||||
|  | 
 | ||||||
|  | 	/* Run (432,103) block interleaving: type-4 bits */ | ||||||
|  | 	block_interleave(432, 103, type3, type4); | ||||||
|  | 	printf("SCH/F type4: %s\n", bitdump(type4, 432)); | ||||||
|  | 
 | ||||||
|  | 	/* Run scrambling (all-zero): type-5 bits */ | ||||||
|  | 	memcpy(type5, type4, 432); | ||||||
|  | 	tetra_scramb_bits(SCRAMB_INIT, type5, 432); | ||||||
|  | 	printf("SCH/F type5: %s\n", bitdump(type5, 432)); | ||||||
|  | 
 | ||||||
|  | 	decode_schf(type5); | ||||||
|  | 
 | ||||||
|  | 	/* Use pdu_acc_ass from testpdu.c */ | ||||||
|  | 	/* Run it through (30,14) RM code: type-2=3=4 bits */ | ||||||
|  | 	printf("AACH type-1: %s\n", bitdump(pdu_acc_ass, 2)); | ||||||
|  | 	bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); | ||||||
|  | 	printf("AACH RM3014: 0x0%x\n", bb_rm3014); | ||||||
|  | 		/* convert to big endian */ | ||||||
|  | 		bb_rm3014_be = htonl(bb_rm3014); | ||||||
|  | 		/* shift two bits left as it is only a 30 bit value */ | ||||||
|  | 		bb_rm3014_be <<= 2; | ||||||
|  | 	osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); | ||||||
|  | 	/* Run scrambling (all-zero): type-5 bits */ | ||||||
|  | 	printf("AACH type-5: %s\n", bitdump(bb_type5, 30)); | ||||||
|  | 
 | ||||||
|  | 	/* Finally, hand it into the physical layer */ | ||||||
|  | 	build_norm_c_d_burst(burst, type5, bb_type5, type5+216, 0); | ||||||
|  | 	printf("cont norm DL burst: %s\n", bitdump(burst, 255*2)); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void decode_sb1(const uint8_t *bits) | ||||||
|  | { | ||||||
|  | 	uint8_t type4[1024]; | ||||||
|  | 	uint8_t type3dp[1024*4]; | ||||||
|  | 	uint8_t type3[1024]; | ||||||
|  | 	uint8_t type2[1024]; | ||||||
|  | 
 | ||||||
|  | 	printf("SB1 type5: %s\n", bitdump(bits, 120)); | ||||||
|  | 	memcpy(type4, bits, 120); | ||||||
|  | 	tetra_scramb_bits(SCRAMB_INIT, type4, 120); | ||||||
|  | 	printf("SB1 type4: %s\n", bitdump(type4, 120)); | ||||||
|  | 	/* Run (120,11) block deinterleaving: type-3 bits */ | ||||||
|  | 	block_deinterleave(120, 11, type4, type3); | ||||||
|  | 	printf("SB1 type3: %s\n", bitdump(type3, 120)); | ||||||
|  | 	/* De-puncture */ | ||||||
|  | 	memset(type3dp, 0xff, sizeof(type3dp)); | ||||||
|  | 	tetra_rcpc_depunct(TETRA_RCPC_PUNCT_2_3, type3, 120, type3dp); | ||||||
|  | 	printf("SB1 type3dp: %s\n", bitdump(type3dp, 80*4)); | ||||||
|  | 	viterbi_dec_sb1_wrapper(type3dp, type2, 80); | ||||||
|  | 	printf("SB1 type2: %s\n", bitdump(type2, 80)); | ||||||
|  | 
 | ||||||
|  | 	{ | ||||||
|  | 		uint16_t crc; | ||||||
|  | 		crc = crc16_ccitt_bits(type2, 76); | ||||||
|  | 		printf("CRC COMP: 0x%04x ", crc); | ||||||
|  | 		if (crc == 0x1d0f) | ||||||
|  | 			printf("OK\n"); | ||||||
|  | 		else { | ||||||
|  | 			printf("WRONG\n"); | ||||||
|  | 			num_crc_err++; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	printf("TN %s ", bitdump(type2+10, 2)); | ||||||
|  | 	printf("MCC %s ", bitdump(type2+31, 10)); | ||||||
|  | 	printf("MNC %s\n", bitdump(type2+41, 14)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Build a full 'downlink continuous SYNC burst' from SYSINFO-PDU and SYNC-PDU */ | ||||||
|  | int build_sb() | ||||||
|  | { | ||||||
|  | 	uint8_t sb_type2[80]; | ||||||
|  | 	uint8_t sb_master[80*4]; | ||||||
|  | 	uint8_t sb_type3[120]; | ||||||
|  | 	uint8_t sb_type4[120]; | ||||||
|  | 	uint8_t sb_type5[120]; | ||||||
|  | 
 | ||||||
|  | 	uint8_t si_type2[140]; | ||||||
|  | 	uint8_t si_master[140*4]; | ||||||
|  | 	uint8_t si_type3[216]; | ||||||
|  | 	uint8_t si_type4[216]; | ||||||
|  | 	uint8_t si_type5[216]; | ||||||
|  | 
 | ||||||
|  | 	uint8_t bb_type5[30]; | ||||||
|  | 	uint8_t burst[255*2]; | ||||||
|  | 	uint16_t crc; | ||||||
|  | 	uint8_t *cur; | ||||||
|  | 	uint32_t bb_rm3014, bb_rm3014_be; | ||||||
|  | 
 | ||||||
|  | 	memset(sb_type2, 0, sizeof(sb_type2)); | ||||||
|  | 	cur = sb_type2; | ||||||
|  | 
 | ||||||
|  | 	/* Use pdu_sync from testpdu.c */ | ||||||
|  | 	cur += osmo_pbit2ubit(sb_type2, pdu_sync, 60); | ||||||
|  | 
 | ||||||
|  | 	crc = ~crc16_ccitt_bits(sb_type2, 60); | ||||||
|  | 	crc = swap16(crc); | ||||||
|  | 	cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); | ||||||
|  | 
 | ||||||
|  | 	/* Append 4 tail bits: type-2 bits */ | ||||||
|  | 	cur += 4; | ||||||
|  | 
 | ||||||
|  | 	printf("SYNC type2: %s\n", bitdump(sb_type2, 80)); | ||||||
|  | 
 | ||||||
|  | 	/* Run rate 2/3 RCPC code: type-3 bits*/ | ||||||
|  | 	{ | ||||||
|  | 		struct conv_enc_state *ces = calloc(1, sizeof(*ces)); | ||||||
|  | 		conv_enc_init(ces); | ||||||
|  | 		conv_enc_input(ces, sb_type2, 80, sb_master); | ||||||
|  | 		get_punctured_rate(TETRA_RCPC_PUNCT_2_3, sb_master, 120, sb_type3); | ||||||
|  | 		free(ces); | ||||||
|  | 	} | ||||||
|  | 	printf("SYNC type3: %s\n", bitdump(sb_type3, 120)); | ||||||
|  | 
 | ||||||
|  | 	/* Run (120,11) block interleaving: type-4 bits */ | ||||||
|  | 	block_interleave(120, 11, sb_type3, sb_type4); | ||||||
|  | 	printf("SYNC type4: %s\n", bitdump(sb_type4, 120)); | ||||||
|  | 
 | ||||||
|  | 	/* Run scrambling (all-zero): type-5 bits */ | ||||||
|  | 	memcpy(sb_type5, sb_type4, 120); | ||||||
|  | 	tetra_scramb_bits(SCRAMB_INIT, sb_type5, 120); | ||||||
|  | 	printf("SYNC type5: %s\n", bitdump(sb_type5, 120)); | ||||||
|  | 
 | ||||||
|  | 	decode_sb1(sb_type5); | ||||||
|  | 
 | ||||||
|  | 	/* Use pdu_sysinfo from testpdu.c */ | ||||||
|  | 	memset(si_type2, 0, sizeof(si_type2)); | ||||||
|  | 	cur = si_type2; | ||||||
|  | 	cur += osmo_pbit2ubit(si_type2, pdu_sysinfo, 124); | ||||||
|  | 
 | ||||||
|  | 	/* Run it through CRC16-CCITT */ | ||||||
|  | 	crc = ~crc16_ccitt_bits(si_type2, 124); | ||||||
|  | 	crc = swap16(crc); | ||||||
|  | 	cur += osmo_pbit2ubit(cur, (uint8_t *) &crc, 16); | ||||||
|  | 
 | ||||||
|  | 	/* Append 4 tail bits: type-2 bits */ | ||||||
|  | 	cur += 4; | ||||||
|  | 
 | ||||||
|  | 	printf("SI type2: %s\n", bitdump(si_type2, 140)); | ||||||
|  | 
 | ||||||
|  | 	/* Run rate 2/3 RCPC code: type-3 bits */ | ||||||
|  | 	{ | ||||||
|  | 		struct conv_enc_state *ces = calloc(1, sizeof(*ces)); | ||||||
|  | 		conv_enc_init(ces); | ||||||
|  | 		conv_enc_input(ces, sb_type2, 144, si_master); | ||||||
|  | 		get_punctured_rate(TETRA_RCPC_PUNCT_2_3, si_master, 216, si_type3); | ||||||
|  | 		free(ces); | ||||||
|  | 	} | ||||||
|  | 	printf("SI type3: %s\n", bitdump(si_type3, 216)); | ||||||
|  | 
 | ||||||
|  | 	/* Run (216,101) block interleaving: type-4 bits */ | ||||||
|  | 	block_interleave(216, 101, si_type3, si_type4); | ||||||
|  | 	printf("SI type4: %s\n", bitdump(si_type4, 216)); | ||||||
|  | 
 | ||||||
|  | 	/* Run scrambling (all-zero): type-5 bits */ | ||||||
|  | 	memcpy(si_type5, si_type4, 216); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	/* Use pdu_acc_ass from testpdu.c */ | ||||||
|  | 	/* Run it through (30,14) RM code: type-2=3=4 bits */ | ||||||
|  | 	printf("AACH type-1: %s\n", bitdump(pdu_acc_ass, 2)); | ||||||
|  | 	bb_rm3014 = tetra_rm3014_compute(*(uint16_t *)pdu_acc_ass); | ||||||
|  | 	printf("AACH RM3014: 0x0%x\n", bb_rm3014); | ||||||
|  | 		/* convert to big endian */ | ||||||
|  | 		bb_rm3014_be = htonl(bb_rm3014); | ||||||
|  | 		/* shift two bits left as it is only a 30 bit value */ | ||||||
|  | 		bb_rm3014_be <<= 2; | ||||||
|  | 	osmo_pbit2ubit(bb_type5, (uint8_t *) &bb_rm3014_be, 30); | ||||||
|  | 	/* Run scrambling (all-zero): type-5 bits */ | ||||||
|  | 	printf("AACH type-5: %s\n", bitdump(bb_type5, 30)); | ||||||
|  | 
 | ||||||
|  | 	/* Finally, hand it into the physical layer */ | ||||||
|  | 	build_sync_c_d_burst(burst, sb_type5, bb_type5, si_type5); | ||||||
|  | 	printf("cont sync DL burst: %s\n", bitdump(burst, 255*2)); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	int err, i; | ||||||
|  | 	uint16_t out; | ||||||
|  | 	uint32_t ret; | ||||||
|  | 
 | ||||||
|  | 	/* first: run some subsystem tests */ | ||||||
|  | 	ret = tetra_punct_test(); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		exit(1); | ||||||
|  | 
 | ||||||
|  | 	tetra_rm3014_init(); | ||||||
|  | #if 0 | ||||||
|  | 	ret = tetra_rm3014_compute(0x1001); | ||||||
|  | 	printf("RM3014: 0x%08x\n", ret); | ||||||
|  | 
 | ||||||
|  | 	err = tetra_rm3014_decode(ret, &out); | ||||||
|  | 	printf("RM3014: 0x%x error: %d\n", out, err); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	/* finally, build some test PDUs and encocde them */ | ||||||
|  | 	testpdu_init(); | ||||||
|  | #if 0 | ||||||
|  | 	build_sb(); | ||||||
|  | 
 | ||||||
|  | 	build_ndb_schf(); | ||||||
|  | #else | ||||||
|  | 	/* iterate over various random PDUs and throw them throguh the viterbi */ | ||||||
|  | 	srand(time(NULL)); | ||||||
|  | 	for (i = 0; i < 100; i++) { | ||||||
|  | 		uint32_t r = rand(); | ||||||
|  | 		osmo_pbit2ubit(pdu_sync, (uint8_t *) &r, 32); | ||||||
|  | 		osmo_pbit2ubit(pdu_sync+32, (uint8_t *)&r, 60-32); | ||||||
|  | 		//build_sb();
 | ||||||
|  | 
 | ||||||
|  | 		osmo_pbit2ubit(pdu_schf, (uint8_t *) &r, 32); | ||||||
|  | 		osmo_pbit2ubit(pdu_schf+32, (uint8_t *)&r, 60-32); | ||||||
|  | 		build_ndb_schf(); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	printf("total number of CRC Errors: %u\n", num_crc_err); | ||||||
|  | 
 | ||||||
|  | 	exit(0); | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								src/crc_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/crc_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | ||||||
|  | /* (C) 2011 by Holger Hans Peter Freyther
 | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include <lower_mac/crc_simple.h> | ||||||
|  | 
 | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	uint8_t input1[] = { 0x01 }; | ||||||
|  | 	uint16_t crc; | ||||||
|  | 
 | ||||||
|  | 	crc = crc16_itut_bytes(0x0, input1, 8); | ||||||
|  | 	printf("The CRC is now: %u/0x%x\n", crc, crc); | ||||||
|  | 
 | ||||||
|  | 	crc = crc16_itut_bits(0x0, input1, 1); | ||||||
|  | 	printf("The CRC is now: %d/0x%x\n", crc, crc); | ||||||
|  | 
 | ||||||
|  | 	crc = crc16_itut_bytes(0xFFFF, input1, 8); | ||||||
|  | 	printf("The CRC is now: %u/0x%x\n", crc, crc); | ||||||
|  | 
 | ||||||
|  | 	crc = crc16_itut_bits(0xFFFF, input1, 1); | ||||||
|  | 	printf("The CRC is now: %d/0x%x\n", crc, crc); | ||||||
|  | 
 | ||||||
|  | 	/* actual CRC-CCIT usage */ | ||||||
|  | 	uint8_t input2[] = { | ||||||
|  | 			0, 0, 0, 1, 0, 0, 0, 0, | ||||||
|  | 			1, 0, 1, 1, 0, 0, 0, 0, | ||||||
|  | 			1, 0, 1, 1, 1, 1, 1, 0, | ||||||
|  | 			0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 			1, 0, 0, 0, 0, 0, 1, 1, | ||||||
|  | 			0, 0, 0, 0, 0, 1, 1, 1, | ||||||
|  | 			1, 1, 0, 1, 0, 0, 1, 1, | ||||||
|  | 			0, 0, 1, 1, | ||||||
|  | 
 | ||||||
|  | 			/* checksum */ | ||||||
|  | 			1, 1, 0, 1, 1, 1, 1, 0, | ||||||
|  | 			1, 1, 1, 1, 0, 0, 0, 1, | ||||||
|  | 			/* tail bits */ | ||||||
|  | 			0, 0, 0, 0 }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	crc = ~crc16_itut_bits(0xffff, input2, 60); | ||||||
|  | 	printf("The CRC is now: %d/0x%x\n", crc, crc); | ||||||
|  | 	 | ||||||
|  | 	/* swap the bytes */ | ||||||
|  | 	crc = (crc << 8) | (crc >> 8); | ||||||
|  | 	osmo_pbit2ubit(&input2[60], (uint8_t *)&crc, 16); | ||||||
|  | 
 | ||||||
|  | 	crc = crc16_itut_bits(0xFFFF, input2, 60 + 16); | ||||||
|  | 	printf("The CRC is now: %d/0x%x\n", crc, crc); | ||||||
|  | 	if (crc == 0x1d0f) | ||||||
|  | 		printf("Decoded successfully.\n"); | ||||||
|  | 	else | ||||||
|  | 		printf("Failed to decode.\n"); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										117
									
								
								src/float_to_bits.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/float_to_bits.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | ||||||
|  | /* Convert floating point symbols in the range +3/-3 to hard bits,
 | ||||||
|  |  * in the 1-bit-per-byte format */ | ||||||
|  | 
 | ||||||
|  | /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <errno.h> | ||||||
|  | 
 | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | 
 | ||||||
|  | static int process_sym_fl(float offset, float fl) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	fl += offset; | ||||||
|  | 
 | ||||||
|  | 	/* very simplistic scheme */ | ||||||
|  | 	if (fl > 2) | ||||||
|  | 		ret = 3; | ||||||
|  | 	else if (fl > 0) | ||||||
|  | 		ret = 1; | ||||||
|  | 	else if (fl < -2) | ||||||
|  | 		ret = -3; | ||||||
|  | 	else | ||||||
|  | 		ret = -1; | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sym_int2bits(int sym, uint8_t *ret) | ||||||
|  | { | ||||||
|  | 	switch (sym) { | ||||||
|  | 	case -3: | ||||||
|  | 		ret[0] = 1; | ||||||
|  | 		ret[1] = 1; | ||||||
|  | 		break; | ||||||
|  | 	case 1: | ||||||
|  | 		ret[0] = 0; | ||||||
|  | 		ret[1] = 0; | ||||||
|  | 		break; | ||||||
|  | 	//case -1:
 | ||||||
|  | 	case 3: | ||||||
|  | 		ret[0] = 0; | ||||||
|  | 		ret[1] = 1; | ||||||
|  | 		break; | ||||||
|  | 	//case 3:
 | ||||||
|  | 	case -1: | ||||||
|  | 		ret[0] = 1; | ||||||
|  | 		ret[1] = 0; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char **argv) | ||||||
|  | { | ||||||
|  | 	int fd, fd_out; | ||||||
|  | 
 | ||||||
|  | 	if (argc < 3) { | ||||||
|  | 		fprintf(stderr, "Usage: %s <infile> <outfile>\n", argv[0]); | ||||||
|  | 		exit(2); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fd = open(argv[1], O_RDONLY); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		perror("open infile"); | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  | 	fd_out = creat(argv[2], 0660); | ||||||
|  | 	if (fd_out < 0) { | ||||||
|  | 		perror("open outfile"); | ||||||
|  | 		exit(1); | ||||||
|  | 	} | ||||||
|  | 	while (1) { | ||||||
|  | 		int rc; | ||||||
|  | 		float fl; | ||||||
|  | 		uint8_t bits[2]; | ||||||
|  | 		rc = read(fd, &fl, sizeof(fl)); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			perror("read"); | ||||||
|  | 			exit(1); | ||||||
|  | 		} else if (rc == 0) | ||||||
|  | 			break; | ||||||
|  | 		rc = process_sym_fl(0.3f, fl); | ||||||
|  | 		sym_int2bits(rc, bits); | ||||||
|  | 		//printf("%2d %1u %1u  %f\n", rc, bits[0], bits[1], fl);
 | ||||||
|  | 		//printf("%1u%1u", bits[0], bits[1]);
 | ||||||
|  | 
 | ||||||
|  | 		rc = write(fd_out, bits, 2); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			perror("write"); | ||||||
|  | 			exit(1); | ||||||
|  | 		} else if (rc == 0) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	exit(0); | ||||||
|  | } | ||||||
							
								
								
									
										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 */ | ||||||
							
								
								
									
										19
									
								
								src/osmo_prim.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/osmo_prim.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | #ifndef OSMO_PRIMITIVE_H | ||||||
|  | #define OSMO_PRIMITIVE_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | enum osmo_prim_operation { | ||||||
|  | 	PRIM_OP_REQUEST, | ||||||
|  | 	PRIM_OP_RESPONSE, | ||||||
|  | 	PRIM_OP_INDICATION, | ||||||
|  | 	PRIM_OP_CONFIRM, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct osmo_prim_hdr { | ||||||
|  | 	uint16_t sap; | ||||||
|  | 	uint16_t primitive; | ||||||
|  | 	enum osmo_prim_operation operation; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										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 */ | ||||||
							
								
								
									
										99
									
								
								src/testpdu.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/testpdu.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | ||||||
|  | /* (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 <string.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <osmocore/bitvec.h> | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | 
 | ||||||
|  | #include "testpdu.h" | ||||||
|  | 
 | ||||||
|  | uint8_t pdu_sync[8];		/* 60 bits */ | ||||||
|  | uint8_t pdu_sysinfo[16];	/* 124 bits */ | ||||||
|  | uint8_t pdu_acc_ass[2]; | ||||||
|  | uint8_t pdu_schf[268]; | ||||||
|  | 
 | ||||||
|  | void testpdu_init() | ||||||
|  | { | ||||||
|  | 	struct bitvec bv; | ||||||
|  | 
 | ||||||
|  | 	memset(&bv, 0, sizeof(bv)); | ||||||
|  | 	bv.data = pdu_sync; | ||||||
|  | 	bv.data_len = sizeof(pdu_sync); | ||||||
|  | 
 | ||||||
|  | 	//bitvec_set_uint(&bv, 0, 4);	/* alignment */
 | ||||||
|  | 	/* According to Table 21.73: SYNC PDU Contents */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 4);	/* System Code: ETS 300 392-2 ed. 1 */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 6);	/* Colour Code: Predefined Scrambling */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Timeslot number: TN 1 */ | ||||||
|  | 	bitvec_set_uint(&bv, 1, 5);	/* Frame number: FN 1 */ | ||||||
|  | 	bitvec_set_uint(&bv, 1, 6);	/* Multiframe number: MN 1 */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Sharing mode: continuous transmission */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 3);	/* TS reserved frames: 1 frame per 2 mfrm */ | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* No DTX */ | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* No Frame 18 extension */ | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* Reserved */ | ||||||
|  | 	/* As defined in Table 18.4.2.1: D-MLE-SYNC */ | ||||||
|  | 	bitvec_set_uint(&bv, 262, 10);	/* MCC */ | ||||||
|  | 	bitvec_set_uint(&bv, 42, 14);	/* MNC */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Neighbor cell boradcast: not supported */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Cell service level: unknown */	 | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* Late entry information */ | ||||||
|  | 	printf("SYNC PDU: %s\n", hexdump(pdu_sync, sizeof(pdu_sync))); | ||||||
|  | 
 | ||||||
|  | 	memset(&bv, 0, sizeof(bv)); | ||||||
|  | 	bv.data = pdu_sysinfo; | ||||||
|  | 	bv.data_len = sizeof(pdu_sysinfo); | ||||||
|  | 
 | ||||||
|  | 	//bitvec_set_uint(&bv, 0, 4);	/* alignment */
 | ||||||
|  | 	/* According to Table 21.4.4.1: SYSINFO PDU contents */ | ||||||
|  | 	bitvec_set_uint(&bv, 2, 2);	/* MAC PDU type: Broadcast */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* SYSINVO PDU */ | ||||||
|  | 	bitvec_set_uint(&bv, ((392775-300000)/25), 12);	/* Main carier */ | ||||||
|  | 	bitvec_set_uint(&bv, 3, 4);	/* Frequency band: 390/400 */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Offset: No offset */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 3);	/* Duplex Spacing: */ | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* Normal operation */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Number of CSCH: none */ | ||||||
|  | 	bitvec_set_uint(&bv, 1, 3);	/* MS_TXPWR_MAX_CELL: 15 dBm */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 4);	/* RXLEV_ACCESS_MIN: -125dBm */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 4);	/* ACCESS_PARAMETER: -53 dBm */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 4);	/* RADIO_DOWNLINK_TIMEOUT: Disable */ | ||||||
|  | 	bitvec_set_bit(&bv, 0);		/* Hyperframe number follows */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 16);	/* Hyperframe number */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* Optional field: Even multiframe */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 20);	/* TS_COMMON_FRAMES for even mframe */ | ||||||
|  | 	/* TM-SDU (42 bit), Section 18.4.2.2, Table 18.15 */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 14);	/* Location Area (18.5.9) */ | ||||||
|  | 	bitvec_set_uint(&bv, 0xFFFF, 16);	/* Subscriber Class (18.5.22) */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 12);	/* BS service details (18.5.2) */ | ||||||
|  | 	printf("SYSINFO PDU: %s\n", hexdump(pdu_sysinfo, sizeof(pdu_sysinfo))); | ||||||
|  | 
 | ||||||
|  | 	memset(&bv, 0, sizeof(bv)); | ||||||
|  | 	bv.data = pdu_acc_ass; | ||||||
|  | 	bv.data_len = sizeof(pdu_acc_ass); | ||||||
|  | 
 | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* alignment */ | ||||||
|  | 	/* According to Table 21.27: ACCESS-ASSIGN PDU */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 2);	/* DL/UL: common only */ | ||||||
|  | 	bitvec_set_uint(&bv, 0, 6); | ||||||
|  | 	bitvec_set_uint(&bv, 0, 6); | ||||||
|  | 	printf("ACCESS-ASSIGN PDU: %s\n", hexdump(pdu_acc_ass, sizeof(pdu_acc_ass))); | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/testpdu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/testpdu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | extern uint8_t pdu_sync[8];		/* 60 bits */ | ||||||
|  | extern uint8_t pdu_sysinfo[16];	/* 124 bits */ | ||||||
|  | extern uint8_t pdu_acc_ass[2]; | ||||||
|  | extern uint8_t pdu_schf[268]; | ||||||
|  | 
 | ||||||
|  | void testpdu_init(); | ||||||
|  | 
 | ||||||
							
								
								
									
										157
									
								
								src/tetra_common.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/tetra_common.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | ||||||
|  | /* Common routines for the TETRA PHY/MAC implementation */ | ||||||
|  | 
 | ||||||
|  | /* (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 <osmocore/utils.h> | ||||||
|  | #include <osmocore/bits.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include "tetra_prim.h" | ||||||
|  | 
 | ||||||
|  | uint32_t bits_to_uint(const uint8_t *bits, unsigned int len) | ||||||
|  | { | ||||||
|  | 	uint32_t ret = 0; | ||||||
|  | 
 | ||||||
|  | 	while (len--) | ||||||
|  | 		ret = (ret << 1) | (*bits++ & 1); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char *bitdump(const uint8_t *bits, unsigned int len) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	static char hexd_buff[4096]; | ||||||
|  | 
 | ||||||
|  | 	if (len > sizeof(hexd_buff)-1) | ||||||
|  | 		len = sizeof(hexd_buff)-1; | ||||||
|  | 	memset(hexd_buff, 0, sizeof(hexd_buff)); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < len; i++) { | ||||||
|  | 		char outch; | ||||||
|  | 		switch (bits[i]) { | ||||||
|  | 		case 0: | ||||||
|  | 			outch = '0'; | ||||||
|  | 			break; | ||||||
|  | 		case 0xff: | ||||||
|  | 			outch = '?'; | ||||||
|  | 			break; | ||||||
|  | 		case 1: | ||||||
|  | 			outch = '1'; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			outch = 'E'; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		hexd_buff[i] = outch; | ||||||
|  | 	} | ||||||
|  | 	hexd_buff[sizeof(hexd_buff)-1] = 0; | ||||||
|  | 	return hexd_buff; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline uint32_t tetra_band_base_hz(uint8_t band) | ||||||
|  | { | ||||||
|  | 	return (band * 100000000); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const int16_t tetra_carrier_offset[4] = { | ||||||
|  | 	[0] =	     0, | ||||||
|  | 	[1] =	  6250, | ||||||
|  | 	[2] =	 -6250, | ||||||
|  | 	[3] =	+12500, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | uint32_t tetra_dl_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset) | ||||||
|  | { | ||||||
|  | 	uint32_t freq = tetra_band_base_hz(band); | ||||||
|  | 	freq += carrier * 25000; | ||||||
|  | 	freq += tetra_carrier_offset[offset&3]; | ||||||
|  | 	return freq; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* TS 100 392-15, Table 2 */ | ||||||
|  | static const int16_t tetra_duplex_spacing[8][16] = { /* values are in kHz */ | ||||||
|  | 	[0] = { -1, 1600, 10000, 10000, 10000, 10000, 10000, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[1] = { -1, 4500, -1, 36000, 7000, -1, -1, -1, 45000, 45000, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[2] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | ||||||
|  | 	[3] = { -1, -1, -1, 8000, 8000, -1, -1, -1, 18000, 18000, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[4] = { -1, -1, -1, 18000, 5000, -1, 30000, 30000, -1, 39000, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[5] = { -1, -1, -1, -1, 9500, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[6] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | 	[7] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | uint32_t tetra_ul_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset, | ||||||
|  | 			     uint8_t duplex, uint8_t reverse) | ||||||
|  | { | ||||||
|  | 	uint32_t freq = tetra_dl_carrier_hz(band, carrier, offset); | ||||||
|  | 
 | ||||||
|  | 	uint32_t duplex_spacing = tetra_duplex_spacing[duplex & 7][band & 15]; | ||||||
|  | 
 | ||||||
|  | 	if (duplex_spacing < 0) /* reserved for future standardization */ | ||||||
|  | 	    return 0; | ||||||
|  | 
 | ||||||
|  | 	duplex_spacing *= 1000; // make Hz
 | ||||||
|  | 
 | ||||||
|  | 	if (reverse) | ||||||
|  | 	    freq += duplex_spacing; | ||||||
|  | 	else | ||||||
|  | 	    freq -= duplex_spacing; | ||||||
|  | 
 | ||||||
|  | 	return freq; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string tetra_sap_names[] = { | ||||||
|  | 	{ TETRA_SAP_TP,		"TP-SAP" }, | ||||||
|  | 	{ TETRA_SAP_TMV,	"TMV-SAP" }, | ||||||
|  | 	{ TETRA_SAP_TMA,	"TMA-SAP" }, | ||||||
|  | 	{ TETRA_SAP_TMB,	"TMB-SAP" }, | ||||||
|  | 	{ TETRA_SAP_TMD,	"TMD-SAP" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct value_string tetra_lchan_names[] = { | ||||||
|  | 	{ TETRA_LC_UNKNOWN,	"UNKNOWN" }, | ||||||
|  | 	{ TETRA_LC_SCH_F,	"SCH/F" }, | ||||||
|  | 	{ TETRA_LC_SCH_HD,	"SCH/HD" }, | ||||||
|  | 	{ TETRA_LC_SCH_HU,	"SCH/HU" }, | ||||||
|  | 	{ TETRA_LC_STCH,	"STCH" }, | ||||||
|  | 	{ TETRA_LC_SCH_P8_F,	"SCH-P8/F" }, | ||||||
|  | 	{ TETRA_LC_SCH_P8_HD,	"SCH-P8/HD" }, | ||||||
|  | 	{ TETRA_LC_SCH_P8_HU,	"SCH-P8/HU" }, | ||||||
|  | 	{ TETRA_LC_AACH,	"AACH" }, | ||||||
|  | 	{ TETRA_LC_TCH,		"TCH" }, | ||||||
|  | 	{ TETRA_LC_BSCH,	"BSCH" }, | ||||||
|  | 	{ TETRA_LC_BNCH,	"BNCH" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_lchan_name(enum tetra_log_chan lchan) | ||||||
|  | { | ||||||
|  | 	return get_value_string(tetra_lchan_names, lchan); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_sap_name(uint8_t sap) | ||||||
|  | { | ||||||
|  | 	return get_value_string(tetra_sap_names, sap); | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/tetra_common.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/tetra_common.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | #ifndef TETRA_COMMON_H | ||||||
|  | #define TETRA_COMMON_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG | ||||||
|  | #define DEBUGP(x, args...)	printf(x, ## args) | ||||||
|  | #else | ||||||
|  | #define DEBUGP(x, args...)	do { } while(0) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #define TETRA_SYM_PER_TS	255 | ||||||
|  | #define TETRA_BITS_PER_TS	(TETRA_SYM_PER_TS*2) | ||||||
|  | 
 | ||||||
|  | /* Chapter 22.2.x */ | ||||||
|  | enum tetra_log_chan { | ||||||
|  | 	TETRA_LC_UNKNOWN, | ||||||
|  | 	/* TMA SAP */ | ||||||
|  | 	TETRA_LC_SCH_F, | ||||||
|  | 	TETRA_LC_SCH_HD, | ||||||
|  | 	TETRA_LC_SCH_HU, | ||||||
|  | 	TETRA_LC_STCH, | ||||||
|  | 	TETRA_LC_SCH_P8_F, | ||||||
|  | 	TETRA_LC_SCH_P8_HD, | ||||||
|  | 	TETRA_LC_SCH_P8_HU, | ||||||
|  | 
 | ||||||
|  | 	TETRA_LC_AACH, | ||||||
|  | 	TETRA_LC_TCH, | ||||||
|  | 	TETRA_LC_BSCH, | ||||||
|  | 	TETRA_LC_BNCH, | ||||||
|  | 
 | ||||||
|  | 	/* FIXME: QAM */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *bitdump(const uint8_t *bits, unsigned int len); | ||||||
|  | uint32_t bits_to_uint(const uint8_t *bits, unsigned int len); | ||||||
|  | 
 | ||||||
|  | #include "tetra_tdma.h" | ||||||
|  | struct tetra_phy_state { | ||||||
|  | 	struct tetra_tdma_time time; | ||||||
|  | }; | ||||||
|  | extern struct tetra_phy_state t_phy_state; | ||||||
|  | 
 | ||||||
|  | #define TETRA_CRC_OK	0x1d0f | ||||||
|  | 
 | ||||||
|  | uint32_t tetra_dl_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset); | ||||||
|  | uint32_t tetra_ul_carrier_hz(uint8_t band, uint16_t carrier, uint8_t offset, | ||||||
|  | 			     uint8_t duplex, uint8_t reverse); | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_lchan_name(enum tetra_log_chan lchan); | ||||||
|  | const char *tetra_get_sap_name(uint8_t sap); | ||||||
|  | #endif | ||||||
							
								
								
									
										134
									
								
								src/tetra_gsmtap.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/tetra_gsmtap.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <osmocore/msgb.h> | ||||||
|  | #include <osmocore/gsmtap.h> | ||||||
|  | #include <osmocore/bits.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include "tetra_tdma.h" | ||||||
|  | 
 | ||||||
|  | static int gsmtap_fd = -1; | ||||||
|  | 
 | ||||||
|  | static const uint8_t lchan2gsmtap[] = { | ||||||
|  | 	[TETRA_LC_SCH_F]	= GSMTAP_TETRA_SCH_F, | ||||||
|  | 	[TETRA_LC_SCH_HD]	= GSMTAP_TETRA_SCH_HD, | ||||||
|  | 	[TETRA_LC_SCH_HU]	= GSMTAP_TETRA_SCH_HU, | ||||||
|  | 	[TETRA_LC_STCH]		= GSMTAP_TETRA_STCH, | ||||||
|  | 	[TETRA_LC_AACH]		= GSMTAP_TETRA_AACH, | ||||||
|  | 	[TETRA_LC_TCH]		= GSMTAP_TETRA_TCH_F, | ||||||
|  | 	[TETRA_LC_BSCH]		= GSMTAP_TETRA_BSCH, | ||||||
|  | 	[TETRA_LC_BNCH]		= GSMTAP_TETRA_BNCH, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct msgb *tetra_gsmtap_makemsg(struct tetra_tdma_time *tm, enum tetra_log_chan lchan, | ||||||
|  | 				  uint8_t ts, uint8_t ss, int8_t signal_dbm, | ||||||
|  | 				  uint8_t snr, const ubit_t *bitdata, unsigned int bitlen) | ||||||
|  | { | ||||||
|  | 	struct msgb *msg; | ||||||
|  | 	struct gsmtap_hdr *gh; | ||||||
|  | 	uint32_t fn = tetra_tdma_time2fn(tm); | ||||||
|  | 	unsigned int packed_len = osmo_pbit_bytesize(bitlen); | ||||||
|  | 	uint8_t *dst; | ||||||
|  | 
 | ||||||
|  | 	msg = msgb_alloc(sizeof(*gh) + packed_len, "tetra_gsmtap_tx"); | ||||||
|  | 	if (!msg) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); | ||||||
|  | 	gh->version = GSMTAP_VERSION; | ||||||
|  | 	gh->hdr_len = sizeof(*gh)/4; | ||||||
|  | 	gh->type = GSMTAP_TYPE_TETRA_I1; | ||||||
|  | 	gh->timeslot = ts; | ||||||
|  | 	gh->sub_slot = ss; | ||||||
|  | 	gh->snr_db = snr; | ||||||
|  | 	gh->signal_dbm = signal_dbm; | ||||||
|  | 	gh->frame_number = htonl(fn); | ||||||
|  | 	gh->sub_type = lchan2gsmtap[lchan]; | ||||||
|  | 	gh->antenna_nr = 0; | ||||||
|  | 
 | ||||||
|  | 	/* convert from 1bit-per-byte to compressed bits!!! */ | ||||||
|  | 	dst = msgb_put(msg, packed_len); | ||||||
|  | 	osmo_ubit2pbit(dst, bitdata, bitlen); | ||||||
|  | 
 | ||||||
|  | 	return msg; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int tetra_gsmtap_sendmsg(struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	if (gsmtap_fd != -1) | ||||||
|  | 		return write(gsmtap_fd, msg->data, msg->len); | ||||||
|  | 	else | ||||||
|  | 		return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* this block should move to libosmocore */ | ||||||
|  | 
 | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <netdb.h> | ||||||
|  | #include <errno.h> | ||||||
|  | 
 | ||||||
|  | int gsmtap_init(const char *host, uint16_t port) | ||||||
|  | { | ||||||
|  | 	struct addrinfo hints, *result, *rp; | ||||||
|  | 	int sfd, rc; | ||||||
|  | 	char portbuf[16]; | ||||||
|  | 
 | ||||||
|  | 	if (port == 0) | ||||||
|  | 		port = GSMTAP_UDP_PORT; | ||||||
|  | 	if (host == NULL) | ||||||
|  | 		host = "localhost"; | ||||||
|  | 
 | ||||||
|  | 	sprintf(portbuf, "%u", port); | ||||||
|  | 	memset(&hints, 0, sizeof(struct addrinfo)); | ||||||
|  | 	hints.ai_family = AF_UNSPEC; | ||||||
|  | 	hints.ai_socktype = SOCK_DGRAM; | ||||||
|  | 	hints.ai_flags = 0; | ||||||
|  | 	hints.ai_protocol = IPPROTO_UDP; | ||||||
|  | 
 | ||||||
|  | 	rc = getaddrinfo(host, portbuf, &hints, &result); | ||||||
|  | 	if (rc != 0) { | ||||||
|  | 		perror("getaddrinfo returned NULL"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (rp = result; rp != NULL; rp = rp->ai_next) { | ||||||
|  | 		printf("creating socket %u, %u, %u\n", rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||||||
|  | 		sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||||||
|  | 		if (sfd == -1) | ||||||
|  | 			continue; | ||||||
|  | 		if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) | ||||||
|  | 			break; | ||||||
|  | 		close(sfd); | ||||||
|  | 	} | ||||||
|  | 	freeaddrinfo(result); | ||||||
|  | 
 | ||||||
|  | 	if (rp == NULL) { | ||||||
|  | 		perror("unable to bind to socket"); | ||||||
|  | 		return -ENODEV; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* FIXME: if host == localhost, bind to another socket and discard data */ | ||||||
|  | 
 | ||||||
|  | 	return sfd; | ||||||
|  | } | ||||||
|  | /* end block for libosmocore */ | ||||||
|  | 
 | ||||||
|  | int tetra_gsmtap_init(const char *host, uint16_t port) | ||||||
|  | { | ||||||
|  | 	int fd; | ||||||
|  | 
 | ||||||
|  | 	fd = gsmtap_init(host, port); | ||||||
|  | 	if (fd < 0) | ||||||
|  | 		return fd; | ||||||
|  | 
 | ||||||
|  | 	gsmtap_fd = fd; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/tetra_gsmtap.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/tetra_gsmtap.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | #ifndef TETRA_GSMTAP_H | ||||||
|  | #define TETRA_GSMTAP_H | ||||||
|  | #include "tetra_common.h" | ||||||
|  | 
 | ||||||
|  | struct msgb *tetra_gsmtap_makemsg(struct tetra_tdma_time *tm, enum tetra_log_chan lchan, | ||||||
|  | 				  uint8_t ts, uint8_t ss, int8_t signal_dbm, | ||||||
|  | 				  uint8_t snr, const uint8_t *data, unsigned int len); | ||||||
|  | 
 | ||||||
|  | int tetra_gsmtap_sendmsg(struct msgb *msg); | ||||||
|  | 
 | ||||||
|  | int tetra_gsmtap_init(const char *host, uint16_t port); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										375
									
								
								src/tetra_mac_pdu.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								src/tetra_mac_pdu.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,375 @@ | ||||||
|  | /* Implementation of some PDU parsing of the TETRA upper MAC */ | ||||||
|  | 
 | ||||||
|  | /* (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | #include "tetra_mac_pdu.h" | ||||||
|  | 
 | ||||||
|  | static void decode_d_mle_sysinfo(struct tetra_mle_si_decoded *msid, const uint8_t *bits) | ||||||
|  | { | ||||||
|  | 	const uint8_t *cur = bits; | ||||||
|  | 
 | ||||||
|  | 	msid->la = bits_to_uint(cur, 14); cur += 14; | ||||||
|  | 	msid->subscr_class = bits_to_uint(cur, 16); cur += 16; | ||||||
|  | 	msid->bs_service_details = bits_to_uint(cur, 12); cur += 12; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void macpdu_decode_sysinfo(struct tetra_si_decoded *sid, const uint8_t *si_bits) | ||||||
|  | { | ||||||
|  | 	const uint8_t *cur = si_bits + 4; | ||||||
|  | 
 | ||||||
|  | 	sid->main_carrier = bits_to_uint(cur, 12); cur += 12; | ||||||
|  | 	sid->freq_band = bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 	sid->freq_offset = bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	sid->duplex_spacing = bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 	sid->reverse_operation = *cur++; | ||||||
|  | 	sid->num_of_csch = bits_to_uint(cur, 2); cur +=2; | ||||||
|  | 	sid->ms_txpwr_max_cell = bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 	sid->rxlev_access_min = bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 	sid->access_parameter = bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 	sid->radio_dl_timeout = bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 	sid->cck_valid_no_hf = *cur++; | ||||||
|  | 	if (sid->cck_valid_no_hf) | ||||||
|  | 		sid->cck_id = bits_to_uint(cur, 16); | ||||||
|  | 	else | ||||||
|  | 		sid->hyperframe_number = bits_to_uint(cur, 16); | ||||||
|  | 	cur += 16; | ||||||
|  | 	/* FIXME: more */ | ||||||
|  | 	decode_d_mle_sysinfo(&sid->mle_si, si_bits + 124-42); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const uint8_t addr_len_by_type[] = { | ||||||
|  | 	[ADDR_TYPE_SSI]		= 24, | ||||||
|  | 	[ADDR_TYPE_EVENT_LABEL]	= 10, | ||||||
|  | 	[ADDR_TYPE_USSI]	= 24, | ||||||
|  | 	[ADDR_TYPE_SMI]		= 24, | ||||||
|  | 	[ADDR_TYPE_SSI_EVENT]	= 34, | ||||||
|  | 	[ADDR_TYPE_SSI_USAGE]	= 30, | ||||||
|  | 	[ADDR_TYPE_SMI_EVENT]	= 34, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* 21.5.2 */ | ||||||
|  | static int decode_chan_alloc(struct tetra_chan_alloc_decoded *cad, const uint8_t *bits) | ||||||
|  | { | ||||||
|  | 	const uint8_t *cur = bits; | ||||||
|  | 
 | ||||||
|  | 	cad->type = 		bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	cad->timeslot = 	bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 	cad->ul_dl = 		bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	cad->clch_perm = 	*cur++; | ||||||
|  | 	cad->cell_chg_f = 	*cur++; | ||||||
|  | 	cad->carrier_nr = 	bits_to_uint(cur, 12); cur += 12; | ||||||
|  | 
 | ||||||
|  | 	cad->ext_carr_pres =	*cur++; | ||||||
|  | 	if (cad->ext_carr_pres) { | ||||||
|  | 		cad->ext_carr.freq_band =	bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 		cad->ext_carr.freq_offset =	bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 		cad->ext_carr.duplex_spc =	bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 		cad->ext_carr.reverse_oper =	bits_to_uint(cur, 1); cur += 1; | ||||||
|  | 	} | ||||||
|  | 	cad->monit_pattern =	bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	if (cad->monit_pattern == 0) | ||||||
|  | 		cad->monit_patt_f18 =	bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	if (cad->ul_dl == 0) { | ||||||
|  | 		cad->aug.ul_dl_ass =	bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 		cad->aug.bandwidth =	bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 		cad->aug.modulation =	bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 		cad->aug.max_ul_qam =	bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 		cur += 3; /* reserved */ | ||||||
|  | 		cad->aug.conf_chan_stat=bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 		cad->aug.bs_imbalance =	bits_to_uint(cur, 4); cur += 4; | ||||||
|  | 		cad->aug.bs_tx_rel =	bits_to_uint(cur, 5); cur += 5; | ||||||
|  | 		cad->aug.napping_sts =	bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 		if (cad->aug.napping_sts == 1) | ||||||
|  | 			cur += 11; /* napping info 21.5.2c */ | ||||||
|  | 		cur += 4; /* reserved */ | ||||||
|  | 		if (*cur++) | ||||||
|  | 			cur += 16; | ||||||
|  | 		if (*cur++) | ||||||
|  | 			cur += 16; | ||||||
|  | 		cur++; | ||||||
|  | 	} | ||||||
|  | 	return cur - bits; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Section 21.4.3.1 MAC-RESOURCE */ | ||||||
|  | int macpdu_decode_resource(struct tetra_resrc_decoded *rsd, const uint8_t *bits) | ||||||
|  | { | ||||||
|  | 	const uint8_t *cur = bits + 4; | ||||||
|  | 
 | ||||||
|  | 	rsd->encryption_mode = bits_to_uint(cur, 2); cur += 2; | ||||||
|  | 	rsd->rand_acc_flag = *cur++; | ||||||
|  | 	/* FIXME: Y2/... octet calculation */ | ||||||
|  | 	rsd->length_ind = bits_to_uint(cur, 6); cur += 6; | ||||||
|  | 	rsd->addr.type = bits_to_uint(cur, 3); cur += 3; | ||||||
|  | 	switch (rsd->addr.type) { | ||||||
|  | 	case ADDR_TYPE_NULL: | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_SSI: | ||||||
|  | 	case ADDR_TYPE_USSI: | ||||||
|  | 	case ADDR_TYPE_SMI: | ||||||
|  | 		rsd->addr.ssi = bits_to_uint(cur, 24); | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_EVENT_LABEL: | ||||||
|  | 		rsd->addr.event_label = bits_to_uint(cur, 10); | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_SSI_EVENT: | ||||||
|  | 	case ADDR_TYPE_SMI_EVENT: | ||||||
|  | 		rsd->addr.ssi = bits_to_uint(cur, 24); | ||||||
|  | 		rsd->addr.event_label = bits_to_uint(cur+24, 10); | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_SSI_USAGE: | ||||||
|  | 		rsd->addr.ssi = bits_to_uint(cur, 24); | ||||||
|  | 		rsd->addr.usage_marker = bits_to_uint(cur, 6); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	cur += addr_len_by_type[rsd->addr.type]; | ||||||
|  | 	/* no intermediate mapping in pi/4 */ | ||||||
|  | 	rsd->power_control_pres = *cur++; | ||||||
|  | 	if (rsd->power_control_pres) | ||||||
|  | 		cur += 4; | ||||||
|  | 	rsd->slot_granting_pres = *cur++; | ||||||
|  | 	if (rsd->slot_granting_pres) { | ||||||
|  | 		if (*cur++) | ||||||
|  | 			cur += 0; //FIXME;
 | ||||||
|  | 		else | ||||||
|  | 			cur += 8; | ||||||
|  | 	} | ||||||
|  | 	rsd->chan_alloc_pres = *cur++; | ||||||
|  | 	/* FIXME: If encryption is enabled, Channel Allocation is encrypted !!! */ | ||||||
|  | 	if (rsd->chan_alloc_pres) | ||||||
|  | 		cur += decode_chan_alloc(&rsd->cad, cur); | ||||||
|  | 	/* FIXME: TM-SDU */ | ||||||
|  | 
 | ||||||
|  | 	return cur - bits; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void decode_access_field(struct tetra_access_field *taf, uint8_t field) | ||||||
|  | { | ||||||
|  | 	field &= 0x3f; | ||||||
|  | 	taf->access_code = field >> 4; | ||||||
|  | 	taf->base_frame_len = field & 0xf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Section 21.4.7.2 ACCESS-ASSIGN PDU */ | ||||||
|  | void macpdu_decode_access_assign(struct tetra_acc_ass_decoded *aad, const uint8_t *bits, int f18) | ||||||
|  | { | ||||||
|  | 	uint8_t field1, field2; | ||||||
|  | 	aad->hdr = bits_to_uint(bits, 2); | ||||||
|  | 	field1 = bits_to_uint(bits+2, 6); | ||||||
|  | 	field2 = bits_to_uint(bits+8, 6); | ||||||
|  | 
 | ||||||
|  | 	if (f18 == 0) { | ||||||
|  | 		switch (aad->hdr) { | ||||||
|  | 		case TETRA_ACC_ASS_DLCC_ULCO: | ||||||
|  | 			/* Field 1 and Field2 are Access fields */ | ||||||
|  | 			decode_access_field(&aad->access[0], field1); | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_DLF1_ULCA: | ||||||
|  | 			/* Field1: DL usage marker */ | ||||||
|  | 			aad->dl_usage = field1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; | ||||||
|  | 			/* Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_DLF1_ULAO: | ||||||
|  | 			/* Field1: DL usage marker */ | ||||||
|  | 			aad->dl_usage = field1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; | ||||||
|  | 			/* Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_DLF1_ULF1: | ||||||
|  | 			/* Field1: DL usage marker */ | ||||||
|  | 			aad->dl_usage = field1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_DL_USAGE; | ||||||
|  | 			/* Field2: UL usage marker */ | ||||||
|  | 			aad->ul_usage = field2; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_UL_USAGE; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		switch (aad->hdr) { | ||||||
|  | 		case TETRA_ACC_ASS_ULCO: | ||||||
|  | 			/* Field1 and Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[0], field1); | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_ULCA: | ||||||
|  | 			/* Field1 and Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[0], field1); | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_ULAO: | ||||||
|  | 			/* Field1 and Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[0], field1); | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS1; | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		case TETRA_ACC_ASS_ULCA2: | ||||||
|  | 			/* Field1: Traffic usage marker (UMt) */ | ||||||
|  | 			/* FIXME */ | ||||||
|  | 			/* Field2: Access field */ | ||||||
|  | 			decode_access_field(&aad->access[1], field2); | ||||||
|  | 			aad->pres |= TETRA_ACC_ASS_PRES_ACCESS2; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string tetra_macpdu_t_names[5] = { | ||||||
|  | 	{ TETRA_PDU_T_MAC_RESOURCE,	"RESOURCE" }, | ||||||
|  | 	{ TETRA_PDU_T_MAC_FRAG_END,	"FRAG/END" }, | ||||||
|  | 	{ TETRA_PDU_T_BROADCAST,	"BROADCAST" }, | ||||||
|  | 	{ TETRA_PDU_T_MAC_SUPPL,	"SUPPLEMENTARY" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_macpdu_name(uint8_t pdu_type) | ||||||
|  | { | ||||||
|  | 	return get_value_string(tetra_macpdu_t_names, pdu_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string serv_det_names[] = { | ||||||
|  | 	{ BS_SERVDET_REG_RQD,		"Registration mandatory" }, | ||||||
|  | 	{ BS_SERVDET_DEREG_RQD,		"De-registration mandatory" }, | ||||||
|  | 	{ BS_SERVDET_PRIO_CELL,		"Priority cell" }, | ||||||
|  | 	{ BS_SERVDET_MIN_MODE,		"Cell never uses minimum mode" }, | ||||||
|  | 	{ BS_SERVDET_MIGRATION,		"Migration supported" }, | ||||||
|  | 	{ BS_SERVDET_SYS_W_SERV,	"Normal mode" }, | ||||||
|  | 	{ BS_SERVDET_VOICE_SERV,	"Voice service" }, | ||||||
|  | 	{ BS_SERVDET_CSD_SERV,		"Circuit data" }, | ||||||
|  | 	{ BS_SERVDET_SNDCP_SERV,	"SNDCP data" }, | ||||||
|  | 	{ BS_SERVDET_AIR_ENCR,		"Air encryption" }, | ||||||
|  | 	{ BS_SERVDET_ADV_LINK,		"Advanced link" }, | ||||||
|  | 	{ 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_bs_serv_det_name(uint32_t pdu_type) | ||||||
|  | { | ||||||
|  | 	return get_value_string(serv_det_names, pdu_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string dl_usage_names[] = { | ||||||
|  | 	{ TETRA_DL_US_UNALLOC,	"Unallocated" }, | ||||||
|  | 	{ TETRA_DL_US_ASS_CTRL,	"Assigned control" }, | ||||||
|  | 	{ TETRA_DL_US_COM_CTRL,	"Common control" }, | ||||||
|  | 	{ TETRA_DL_US_RESERVED,	"Reserved" }, | ||||||
|  | 	{ 0, NULL }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_dl_usage_name(uint8_t num) | ||||||
|  | { | ||||||
|  | 	if (num <= 3) | ||||||
|  | 		return get_value_string(dl_usage_names, num); | ||||||
|  | 	return "Traffic"; | ||||||
|  | } | ||||||
|  | const char *tetra_get_ul_usage_name(uint8_t num) | ||||||
|  | { | ||||||
|  | 	if (num == 0) | ||||||
|  | 		return "Unallocated"; | ||||||
|  | 	return "Traffic"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string addr_type_names[] = { | ||||||
|  | 	{ ADDR_TYPE_NULL,	"Null PDU" }, | ||||||
|  | 	{ ADDR_TYPE_SSI,	"SSI" }, | ||||||
|  | 	{ ADDR_TYPE_EVENT_LABEL,"Event Label" }, | ||||||
|  | 	{ ADDR_TYPE_USSI,	"USSI (migrading MS un-exchanged)" }, | ||||||
|  | 	{ ADDR_TYPE_SMI,	"SMI (management)" }, | ||||||
|  | 	{ ADDR_TYPE_SSI_EVENT,	"SSI + Event Label" }, | ||||||
|  | 	{ ADDR_TYPE_SSI_USAGE,	"SSI + Usage Marker" }, | ||||||
|  | 	{ ADDR_TYPE_SMI_EVENT,	"SMI + Event Label" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | const char *tetra_get_addr_t_name(uint8_t addrt) | ||||||
|  | { | ||||||
|  | 	return get_value_string(addr_type_names, addrt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string alloc_type_names[] = { | ||||||
|  | 	{ TMAC_ALLOC_T_REPLACE,		"Replace" }, | ||||||
|  | 	{ TMAC_ALLOC_T_ADDITIONAL,	"Additional" }, | ||||||
|  | 	{ TMAC_ALLOC_T_QUIT_GO,		"Quit and go" }, | ||||||
|  | 	{ TMAC_ALLOC_T_REPL_SLOT1,	"Replace + Slot1" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | const char *tetra_get_alloc_t_name(uint8_t alloct) | ||||||
|  | { | ||||||
|  | 	return get_value_string(alloc_type_names, alloct); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char *tetra_addr_dump(const struct tetra_addr *addr) | ||||||
|  | { | ||||||
|  | 	static char buf[64]; | ||||||
|  | 	char *cur = buf; | ||||||
|  | 
 | ||||||
|  | 	memset(buf, 0, sizeof(buf)); | ||||||
|  | 	cur += sprintf(cur, "%s(", tetra_get_addr_t_name(addr->type)); | ||||||
|  | 	switch (addr->type) { | ||||||
|  | 	case ADDR_TYPE_NULL: | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_SSI: | ||||||
|  | 	case ADDR_TYPE_USSI: | ||||||
|  | 	case ADDR_TYPE_SMI: | ||||||
|  | 		cur += sprintf(cur, "%u", addr->ssi); | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_EVENT_LABEL: | ||||||
|  | 	case ADDR_TYPE_SSI_EVENT: | ||||||
|  | 	case ADDR_TYPE_SMI_EVENT: | ||||||
|  | 		cur += sprintf(cur, "%u/E%u", addr->ssi, addr->event_label); | ||||||
|  | 		break; | ||||||
|  | 	case ADDR_TYPE_SSI_USAGE: | ||||||
|  | 		cur += sprintf(cur, "%u/U%u", addr->ssi, addr->usage_marker); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	cur += sprintf(cur, ")"); | ||||||
|  | 
 | ||||||
|  | 	return buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct value_string ul_dl_names[] = { | ||||||
|  | 	{ 0, "Augmented" }, | ||||||
|  | 	{ 1, "Downlink only" }, | ||||||
|  | 	{ 2, "Uplink only" }, | ||||||
|  | 	{ 3, "Uplink + Downlink" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | const char *tetra_get_ul_dl_name(uint8_t ul_dl) | ||||||
|  | { | ||||||
|  | 	return get_value_string(ul_dl_names, ul_dl); | ||||||
|  | } | ||||||
							
								
								
									
										221
									
								
								src/tetra_mac_pdu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/tetra_mac_pdu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,221 @@ | ||||||
|  | #ifndef TETRA_MAC_PDU | ||||||
|  | #define TETRA_MAC_PDU | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_pdu_types { | ||||||
|  | 	TETRA_PDU_T_MAC_RESOURCE = 0, | ||||||
|  | 	TETRA_PDU_T_MAC_FRAG_END = 1, | ||||||
|  | 	TETRA_PDU_T_BROADCAST = 2, | ||||||
|  | 	TETRA_PDU_T_MAC_SUPPL = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_frage_pdu_types { | ||||||
|  | 	TETRA_MAC_FRAGE_FRAG = 0, | ||||||
|  | 	TETRA_MAC_FRAGE_END = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_bcast_pdu_types { | ||||||
|  | 	TETRA_MAC_BC_SYSINFO = 0, | ||||||
|  | 	TETRA_MAC_BC_ACCESS_DEFINE = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_supp_pdu_types { | ||||||
|  | 	TETRA_MAC_SUPP_D_BLCK = 0, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_bs_serv_details { | ||||||
|  | 	BS_SERVDET_REG_RQD	= (1 << 11), | ||||||
|  | 	BS_SERVDET_DEREG_RQD	= (1 << 10), | ||||||
|  | 	BS_SERVDET_PRIO_CELL	= (1 << 9), | ||||||
|  | 	BS_SERVDET_MIN_MODE	= (1 << 8), | ||||||
|  | 	BS_SERVDET_MIGRATION	= (1 << 7), | ||||||
|  | 	BS_SERVDET_SYS_W_SERV	= (1 << 6), | ||||||
|  | 	BS_SERVDET_VOICE_SERV	= (1 << 5), | ||||||
|  | 	BS_SERVDET_CSD_SERV	= (1 << 4), | ||||||
|  | 	BS_SERVDET_SNDCP_SERV	= (1 << 2), | ||||||
|  | 	BS_SERVDET_AIR_ENCR	= (1 << 1), | ||||||
|  | 	BS_SERVDET_ADV_LINK	= (1 << 0), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_bs_serv_det_name(uint32_t pdu_type); | ||||||
|  | 
 | ||||||
|  | struct tetra_mle_si_decoded { | ||||||
|  | 	uint16_t la; | ||||||
|  | 	uint16_t subscr_class; | ||||||
|  | 	uint16_t bs_service_details; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_si_decoded { | ||||||
|  | 	uint16_t main_carrier; | ||||||
|  | 	uint8_t freq_band; | ||||||
|  | 	uint8_t freq_offset; | ||||||
|  | 	uint8_t duplex_spacing; | ||||||
|  | 	uint8_t reverse_operation; | ||||||
|  | 	uint8_t num_of_csch; | ||||||
|  | 	uint8_t ms_txpwr_max_cell; | ||||||
|  | 	uint8_t rxlev_access_min; | ||||||
|  | 	uint8_t access_parameter; | ||||||
|  | 	uint8_t radio_dl_timeout; | ||||||
|  | 	int cck_valid_no_hf; | ||||||
|  | 	union { | ||||||
|  | 		uint16_t cck_id; | ||||||
|  | 		uint16_t hyperframe_number; | ||||||
|  | 	}; | ||||||
|  | 	struct tetra_mle_si_decoded mle_si; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_macpdu_name(uint8_t pdu_type); | ||||||
|  | 
 | ||||||
|  | void macpdu_decode_sysinfo(struct tetra_si_decoded *sid, const uint8_t *si_bits); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* Section 21.4.7.2 ACCESS-ASSIGN PDU */ | ||||||
|  | enum tetra_acc_ass_hdr { | ||||||
|  | 	TETRA_ACC_ASS_DLCC_ULCO, | ||||||
|  | 	TETRA_ACC_ASS_DLF1_ULCA, | ||||||
|  | 	TETRA_ACC_ASS_DLF1_ULAO, | ||||||
|  | 	TETRA_ACC_ASS_DLF1_ULF1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_acc_ass_hdr_f18 { | ||||||
|  | 	TETRA_ACC_ASS_ULCO, | ||||||
|  | 	TETRA_ACC_ASS_ULCA, | ||||||
|  | 	TETRA_ACC_ASS_ULAO, | ||||||
|  | 	TETRA_ACC_ASS_ULCA2, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_dl_usage { | ||||||
|  | 	TETRA_DL_US_UNALLOC	= 0, | ||||||
|  | 	TETRA_DL_US_ASS_CTRL	= 1, | ||||||
|  | 	TETRA_DL_US_COM_CTRL	= 2, | ||||||
|  | 	TETRA_DL_US_RESERVED	= 3, | ||||||
|  | 	TETRA_DL_US_TRAFFIC, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_ul_usage { | ||||||
|  | 	TETRA_UL_US_UNALLOC	= 0, | ||||||
|  | 	TETRA_UL_US_TRAFFIC, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Section 21.5.1 */ | ||||||
|  | enum tetra_access_field_bf_len { | ||||||
|  | 	TETRA_ACC_BFL_RES_SUBS	= 0, | ||||||
|  | 	TETRA_ACC_BFL_CLCH_SUBS	= 1, | ||||||
|  | 	TETRA_ACC_BFL_ONGOING	= 2, | ||||||
|  | 	TETRA_ACC_BFL_1		= 3, | ||||||
|  | 	TETRA_ACC_BFL_2		= 4, | ||||||
|  | 	TETRA_ACC_BFL_3		= 5, | ||||||
|  | 	TETRA_ACC_BFL_4		= 7, | ||||||
|  | 	TETRA_ACC_BFL_5		= 6, | ||||||
|  | 	TETRA_ACC_BFL_6		= 8, | ||||||
|  | 	TETRA_ACC_BFL_8		= 9, | ||||||
|  | 	TETRA_ACC_BFL_10	= 0xa, | ||||||
|  | 	TETRA_ACC_BFL_12	= 0xb, | ||||||
|  | 	TETRA_ACC_BFL_16	= 0xc, | ||||||
|  | 	TETRA_ACC_BFL_20	= 0xd, | ||||||
|  | 	TETRA_ACC_BFL_24	= 0xe, | ||||||
|  | 	TETRA_ACC_BFL_32	= 0xf, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_access_field { | ||||||
|  | 	uint8_t access_code; | ||||||
|  | 	enum tetra_access_field_bf_len base_frame_len; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum tetra_acc_ass_pres { | ||||||
|  | 	TETRA_ACC_ASS_PRES_ACCESS1	= (1 << 0), | ||||||
|  | 	TETRA_ACC_ASS_PRES_ACCESS2	= (1 << 1), | ||||||
|  | 	TETRA_ACC_ASS_PRES_DL_USAGE	= (1 << 2), | ||||||
|  | 	TETRA_ACC_ASS_PRES_UL_USAGE	= (1 << 3), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_acc_ass_decoded { | ||||||
|  | 	uint8_t hdr; | ||||||
|  | 	uint32_t pres;	/* which of the fields below are present */ | ||||||
|  | 
 | ||||||
|  | 	enum tetra_ul_usage ul_usage; | ||||||
|  | 	enum tetra_dl_usage dl_usage; | ||||||
|  | 	struct tetra_access_field access[2]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void macpdu_decode_access_assign(struct tetra_acc_ass_decoded *aad, const uint8_t *bits, int f18); | ||||||
|  | const char *tetra_get_dl_usage_name(uint8_t num); | ||||||
|  | const char *tetra_get_ul_usage_name(uint8_t num); | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_res_addr_type { | ||||||
|  | 	ADDR_TYPE_NULL	= 0, | ||||||
|  | 	ADDR_TYPE_SSI	= 1, | ||||||
|  | 	ADDR_TYPE_EVENT_LABEL 	= 2, | ||||||
|  | 	ADDR_TYPE_USSI		= 3, | ||||||
|  | 	ADDR_TYPE_SMI		= 4, | ||||||
|  | 	ADDR_TYPE_SSI_EVENT	= 5, | ||||||
|  | 	ADDR_TYPE_SSI_USAGE	= 6, | ||||||
|  | 	ADDR_TYPE_SMI_EVENT	= 7, | ||||||
|  | }; | ||||||
|  | const char *tetra_get_addr_t_name(uint8_t addrt); | ||||||
|  | 
 | ||||||
|  | enum tetra_mac_alloc_type { | ||||||
|  | 	TMAC_ALLOC_T_REPLACE	= 0, | ||||||
|  | 	TMAC_ALLOC_T_ADDITIONAL	= 1, | ||||||
|  | 	TMAC_ALLOC_T_QUIT_GO	= 2, | ||||||
|  | 	TMAC_ALLOC_T_REPL_SLOT1	= 3, | ||||||
|  | }; | ||||||
|  | const char *tetra_get_alloc_t_name(uint8_t alloct); | ||||||
|  | 
 | ||||||
|  | struct tetra_chan_alloc_decoded { | ||||||
|  | 	uint8_t type; | ||||||
|  | 	uint8_t timeslot; | ||||||
|  | 	uint8_t ul_dl; | ||||||
|  | 	uint8_t clch_perm; | ||||||
|  | 	uint8_t cell_chg_f; | ||||||
|  | 	uint16_t carrier_nr; | ||||||
|  | 	uint8_t ext_carr_pres; | ||||||
|  | 	struct { | ||||||
|  | 		uint8_t freq_band; | ||||||
|  | 		uint8_t freq_offset; | ||||||
|  | 		uint8_t duplex_spc; | ||||||
|  | 		uint8_t reverse_oper; | ||||||
|  | 	} ext_carr; | ||||||
|  | 	uint8_t monit_pattern; | ||||||
|  | 	uint8_t monit_patt_f18; | ||||||
|  | 	struct { | ||||||
|  | 		uint8_t ul_dl_ass; | ||||||
|  | 		uint8_t bandwidth; | ||||||
|  | 		uint8_t modulation; | ||||||
|  | 		uint8_t max_ul_qam; | ||||||
|  | 		uint8_t conf_chan_stat; | ||||||
|  | 		uint8_t bs_imbalance; | ||||||
|  | 		uint8_t bs_tx_rel; | ||||||
|  | 		uint8_t napping_sts; | ||||||
|  | 	} aug; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_addr { | ||||||
|  | 	uint8_t type; | ||||||
|  | 	uint16_t mcc; | ||||||
|  | 	uint16_t mnc; | ||||||
|  | 	uint32_t ssi; | ||||||
|  | 
 | ||||||
|  | 	uint16_t event_label; | ||||||
|  | 	uint8_t usage_marker; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_resrc_decoded { | ||||||
|  | 	uint8_t encryption_mode; | ||||||
|  | 	uint8_t rand_acc_flag; | ||||||
|  | 	uint8_t length_ind; | ||||||
|  | 	struct tetra_addr addr; | ||||||
|  | 
 | ||||||
|  | 	uint8_t power_control_pres; | ||||||
|  | 
 | ||||||
|  | 	uint8_t slot_granting_pres; | ||||||
|  | 
 | ||||||
|  | 	uint8_t chan_alloc_pres; | ||||||
|  | 	struct tetra_chan_alloc_decoded cad; | ||||||
|  | }; | ||||||
|  | int macpdu_decode_resource(struct tetra_resrc_decoded *rsd, const uint8_t *bits); | ||||||
|  | 
 | ||||||
|  | const char *tetra_addr_dump(const struct tetra_addr *addr); | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_ul_dl_name(uint8_t ul_dl); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										38
									
								
								src/tetra_mle_pdu.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/tetra_mle_pdu.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | /* Implementation of TETRA MLE PDU parsing */ | ||||||
|  | 
 | ||||||
|  | /* (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 <unistd.h> | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_mle_pdu.h" | ||||||
|  | 
 | ||||||
|  | static const struct value_string mle_pdisc_names[] = { | ||||||
|  | 	{ TMLE_PDISC_MM,	"MM" }, | ||||||
|  | 	{ TMLE_PDISC_CMCE,	"CMCE" }, | ||||||
|  | 	{ TMLE_PDISC_SNDCP,	"SNDCP" }, | ||||||
|  | 	{ TMLE_PDUSC_MLE,	"MLE" }, | ||||||
|  | 	{ TMLE_PDISC_MGMT,	"MGMT" }, | ||||||
|  | 	{ TMLE_PDISC_TEST,	"TEST" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | const char *tetra_get_mle_pdisc_name(uint8_t pdisc) | ||||||
|  | { | ||||||
|  | 	return get_value_string(mle_pdisc_names, pdisc); | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								src/tetra_mle_pdu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/tetra_mle_pdu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | #ifndef TETRA_MLE_PDU_H | ||||||
|  | #define TETRA_MLE_PDU_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | /* 18.5.21 */ | ||||||
|  | enum tetra_mle_pdisc { | ||||||
|  | 	TMLE_PDISC_MM		= 1, | ||||||
|  | 	TMLE_PDISC_CMCE		= 2, | ||||||
|  | 	TMLE_PDISC_SNDCP	= 4, | ||||||
|  | 	TMLE_PDUSC_MLE		= 5, | ||||||
|  | 	TMLE_PDISC_MGMT		= 6, | ||||||
|  | 	TMLE_PDISC_TEST		= 7, | ||||||
|  | }; | ||||||
|  | const char *tetra_get_mle_pdisc_name(uint8_t pdisc); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										46
									
								
								src/tetra_mm_pdu.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/tetra_mm_pdu.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | /* Implementation of TETRA MM PDU parsing */ | ||||||
|  | 
 | ||||||
|  | /* (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 <unistd.h> | ||||||
|  | #include <osmocore/utils.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_mm_pdu.h" | ||||||
|  | 
 | ||||||
|  | static const struct value_string mm_pdut_d_names[] = { | ||||||
|  | 	{ TMM_PDU_T_D_OTAR, 		"D-OTAR" }, | ||||||
|  | 	{ TMM_PDU_T_D_AUTH,		"D-AUTHENTICATION" }, | ||||||
|  | 	{ TMM_PDU_T_D_CK_CHG_DEM,	"D-CK CHANGE DEMAND" }, | ||||||
|  | 	{ TMM_PDU_T_D_DISABLE,		"D-DISABLE" }, | ||||||
|  | 	{ TMM_PDU_T_D_ENABLE,		"D-ENABLE" }, | ||||||
|  | 	{ TMM_PDU_T_D_LOC_UPD_ACC,	"D-LOCATION UPDATE ACCEPT" }, | ||||||
|  | 	{ TMM_PDU_T_D_LOC_UPD_CMD,	"D-LOCATION UPDATE COMMAND" }, | ||||||
|  | 	{ TMM_PDU_T_D_LOC_UPD_REJ,	"D-LOCATION UPDATE REJECT" }, | ||||||
|  | 	{ TMM_PDU_T_D_LOC_UPD_PROC,	"D-LOCATION UPDATE PROCEEDING" }, | ||||||
|  | 	{ TMM_PDU_T_D_ATT_DET_GRP,	"D-ATTACH/DETACH GROUP ID" }, | ||||||
|  | 	{ TMM_PDU_T_D_ATT_DET_GRP_ACK,	"D-ATTACH/DETACH GROUP ID ACK" }, | ||||||
|  | 	{ TMM_PDU_T_D_MM_STATUS,	"D-MM STATUS" }, | ||||||
|  | 	{ TMM_PDU_T_D_MM_PDU_NOTSUPP,	"MM PDU/FUNCTION NOT SUPPORTED" }, | ||||||
|  | 	{ 0, NULL } | ||||||
|  | }; | ||||||
|  | const char *tetra_get_mm_pdut_name(uint8_t pdut, int uplink) | ||||||
|  | { | ||||||
|  | 	/* FIXME: uplink */ | ||||||
|  | 	return get_value_string(mm_pdut_d_names, pdut); | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								src/tetra_mm_pdu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/tetra_mm_pdu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | #ifndef TETRA_MM_PDU_H | ||||||
|  | #define TETRA_MM_PDU_H | ||||||
|  | 
 | ||||||
|  | /* 16.10.39 PDU Type */ | ||||||
|  | enum tetra_mm_pdu_type_d { | ||||||
|  | 	TMM_PDU_T_D_OTAR 	= 0x0, | ||||||
|  | 	TMM_PDU_T_D_AUTH	= 0x1, | ||||||
|  | 	TMM_PDU_T_D_CK_CHG_DEM	= 0x2, | ||||||
|  | 	TMM_PDU_T_D_DISABLE	= 0x3, | ||||||
|  | 	TMM_PDU_T_D_ENABLE	= 0x4, | ||||||
|  | 	TMM_PDU_T_D_LOC_UPD_ACC	= 0x5, | ||||||
|  | 	TMM_PDU_T_D_LOC_UPD_CMD	= 0x6, | ||||||
|  | 	TMM_PDU_T_D_LOC_UPD_REJ	= 0x7, | ||||||
|  | 	/* RES */ | ||||||
|  | 	TMM_PDU_T_D_LOC_UPD_PROC	= 0x9, | ||||||
|  | 	TMM_PDU_T_D_ATT_DET_GRP		= 0xa, | ||||||
|  | 	TMM_PDU_T_D_ATT_DET_GRP_ACK	= 0xb, | ||||||
|  | 	TMM_PDU_T_D_MM_STATUS		= 0xc, | ||||||
|  | 	/* RES */ | ||||||
|  | 	/* RES */ | ||||||
|  | 	TMM_PDU_T_D_MM_PDU_NOTSUPP	= 0xf | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const char *tetra_get_mm_pdut_name(uint8_t pdut, int uplink); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										48
									
								
								src/tetra_prim.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/tetra_prim.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | #ifndef TETRA_PRIM_H | ||||||
|  | #define TETRA_PRIM_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #include "osmo_prim.h" | ||||||
|  | 
 | ||||||
|  | #include "tetra_common.h" | ||||||
|  | 
 | ||||||
|  | enum tetra_saps { | ||||||
|  | 	TETRA_SAP_TP,	/* between PHY and lower MAC */ | ||||||
|  | 	TETRA_SAP_TMV,	/* beetween lower and upper MAC */ | ||||||
|  | 	TETRA_SAP_TMA, | ||||||
|  | 	TETRA_SAP_TMB, | ||||||
|  | 	TETRA_SAP_TMD, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Table 23.1 */ | ||||||
|  | enum tmv_sap_prim { | ||||||
|  | 	PRIM_TMV_UNITDATA, | ||||||
|  | 	PRIM_TMV_CONFIGURE, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Table 23.2 */ | ||||||
|  | struct tmv_unitdata_param { | ||||||
|  | 	uint32_t mac_block_len; | ||||||
|  | 	enum tetra_log_chan lchan; | ||||||
|  | 	int crc_ok; | ||||||
|  | 	uint32_t scrambling_code; | ||||||
|  | 	struct tetra_tdma_time tdma_time; | ||||||
|  | 	uint8_t mac_block[412];		/* maximum length of bits in a non-QAM chan */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Table 23.3 */ | ||||||
|  | struct tmv_configure_param { | ||||||
|  | 	/* FIXME */ | ||||||
|  | 	uint32_t scrambling_rx; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tetra_tmvsap_prim { | ||||||
|  | 	struct osmo_prim_hdr oph; | ||||||
|  | 	union { | ||||||
|  | 		struct tmv_unitdata_param unitdata; | ||||||
|  | 		struct tmv_configure_param configure; | ||||||
|  | 	} u; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										100
									
								
								src/tetra_tdma.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/tetra_tdma.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | ||||||
|  | /* TETRA TDMA time functions, see Section 7.3 of EN 300 392-2 */ | ||||||
|  | 
 | ||||||
|  | /* (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 <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include "tetra_tdma.h" | ||||||
|  | 
 | ||||||
|  | static void normalize_mn(struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	if (tm->mn > 60) | ||||||
|  | 		tm->mn = (tm->mn%60); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void normalize_fn(struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	uint32_t mn_delta; | ||||||
|  | 
 | ||||||
|  | 	if (tm->fn > 18) { | ||||||
|  | 		mn_delta = tm->fn/60; | ||||||
|  | 		tm->fn = (tm->fn%18); | ||||||
|  | 		tm->mn += mn_delta; | ||||||
|  | 	} | ||||||
|  | 	normalize_mn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void normalize_tn(struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	uint32_t fn_delta; | ||||||
|  | 
 | ||||||
|  | 	if (tm->tn > 4) { | ||||||
|  | 		fn_delta = tm->tn/4; | ||||||
|  | 		tm->tn = (tm->tn%4); | ||||||
|  | 		tm->fn += fn_delta; | ||||||
|  | 	} | ||||||
|  | 	normalize_fn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void normalize_sn(struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	uint32_t tn_delta; | ||||||
|  | 
 | ||||||
|  | 	if (tm->sn > 255) { | ||||||
|  | 		tn_delta = (tm->sn/255); | ||||||
|  | 		tm->sn = (tm->sn % 255) + 1; | ||||||
|  | 		tm->tn += tn_delta; | ||||||
|  | 	} | ||||||
|  | 	normalize_tn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void tetra_tdma_time_add_sym(struct tetra_tdma_time *tm, uint32_t sym_count) | ||||||
|  | { | ||||||
|  | 	tm->sn += sym_count; | ||||||
|  | 	normalize_sn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void tetra_tdma_time_add_tn(struct tetra_tdma_time *tm, uint32_t tn_count) | ||||||
|  | { | ||||||
|  | 	tm->tn += tn_count; | ||||||
|  | 	normalize_tn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void tetra_tdma_time_add_fn(struct tetra_tdma_time *tm, uint32_t fn_count) | ||||||
|  | { | ||||||
|  | 	tm->fn += fn_count; | ||||||
|  | 	normalize_fn(tm); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char *tetra_tdma_time_dump(const struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	static char buf[256]; | ||||||
|  | 
 | ||||||
|  | 	snprintf(buf, sizeof(buf), "%02u/%02u/%u/%03u", tm->mn, tm->fn, tm->tn, tm->sn); | ||||||
|  | 
 | ||||||
|  | 	return buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t tetra_tdma_time2fn(struct tetra_tdma_time *tm) | ||||||
|  | { | ||||||
|  | 	/* FIXME: add hyperframe number !! */ | ||||||
|  | 	return (tm->mn *18) + tm->fn; | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								src/tetra_tdma.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/tetra_tdma.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | #ifndef TETRA_TDMA_H | ||||||
|  | #define TETRA_TDMA_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | struct tetra_tdma_time { | ||||||
|  | 	uint32_t sn;	/* symbol number (1 ... 255) */ | ||||||
|  | 	uint32_t tn;	/* timeslot number (1 .. 4) */ | ||||||
|  | 	uint32_t fn;	/* frame number (1 .. 18) */ | ||||||
|  | 	uint32_t mn;	/* multiframe number (1 .. 60) */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void tetra_tdma_time_add_sym(struct tetra_tdma_time *tm, uint32_t sym_count); | ||||||
|  | void tetra_tdma_time_add_tn(struct tetra_tdma_time *tm, uint32_t tn_count); | ||||||
|  | void tetra_tdma_time_add_fn(struct tetra_tdma_time *tm, uint32_t fn_count); | ||||||
|  | char *tetra_tdma_time_dump(const struct tetra_tdma_time *tm); | ||||||
|  | 
 | ||||||
|  | uint32_t tetra_tdma_time2fn(struct tetra_tdma_time *tm); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										222
									
								
								src/tetra_upper_mac.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								src/tetra_upper_mac.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,222 @@ | ||||||
|  | /* TETRA upper MAC layer main routine, above 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_prim.h" | ||||||
|  | #include "tetra_upper_mac.h" | ||||||
|  | #include "tetra_mac_pdu.h" | ||||||
|  | #include "tetra_mle_pdu.h" | ||||||
|  | #include "tetra_mm_pdu.h" | ||||||
|  | #include "tetra_gsmtap.h" | ||||||
|  | 
 | ||||||
|  | static void rx_bcast(struct tetra_tmvsap_prim *tmvp) | ||||||
|  | { | ||||||
|  | 	struct tmv_unitdata_param *tup = &tmvp->u.unitdata; | ||||||
|  | 	struct tetra_si_decoded sid; | ||||||
|  | 	uint32_t dl_freq, ul_freq; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	memset(&sid, 0, sizeof(sid)); | ||||||
|  | 	macpdu_decode_sysinfo(&sid, tup->mac_block); | ||||||
|  | 
 | ||||||
|  | 	dl_freq = tetra_dl_carrier_hz(sid.freq_band, | ||||||
|  | 				      sid.main_carrier, | ||||||
|  | 				      sid.freq_offset); | ||||||
|  | 
 | ||||||
|  | 	ul_freq = tetra_ul_carrier_hz(sid.freq_band, | ||||||
|  | 				      sid.main_carrier, | ||||||
|  | 				      sid.freq_offset, | ||||||
|  | 				      sid.duplex_spacing, | ||||||
|  | 				      sid.reverse_operation); | ||||||
|  | 
 | ||||||
|  | 	printf("BNCH SYSINFO (DL %u Hz, UL %u Hz), service_details 0x%04x ", | ||||||
|  | 		dl_freq, ul_freq, sid.mle_si.bs_service_details); | ||||||
|  | 	if (sid.cck_valid_no_hf) | ||||||
|  | 		printf("CCK ID %u", sid.cck_id); | ||||||
|  | 	else | ||||||
|  | 		printf("Hyperframe %u", sid.hyperframe_number); | ||||||
|  | 	printf("\n"); | ||||||
|  | 	for (i = 0; i < 12; i++) | ||||||
|  | 		printf("\t%s: %u\n", tetra_get_bs_serv_det_name(1 << i), | ||||||
|  | 			sid.mle_si.bs_service_details & (1 << i) ? 1 : 0); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const char *tetra_alloc_dump(const struct tetra_chan_alloc_decoded *cad) | ||||||
|  | { | ||||||
|  | 	static char buf[64]; | ||||||
|  | 	char *cur = buf; | ||||||
|  | 
 | ||||||
|  | 	cur += sprintf(cur, "%s (TN%u/%s/%uHz)", | ||||||
|  | 		tetra_get_alloc_t_name(cad->type), cad->timeslot, | ||||||
|  | 		tetra_get_ul_dl_name(cad->ul_dl), | ||||||
|  | 		tetra_dl_carrier_hz(cad->ext_carr.freq_band, cad->carrier_nr, | ||||||
|  | 				    cad->ext_carr.freq_offset)); | ||||||
|  | 
 | ||||||
|  | 	return buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rx_tm_sdu(uint8_t *bits, unsigned int len) | ||||||
|  | { | ||||||
|  | 	uint8_t mle_pdisc = bits_to_uint(bits, 3); | ||||||
|  | 
 | ||||||
|  | 	printf("TM-SDU(%s): %s", tetra_get_mle_pdisc_name(mle_pdisc), | ||||||
|  | 		bitdump(bits, len)); | ||||||
|  | 	switch (mle_pdisc) { | ||||||
|  | 	case TMLE_PDISC_MM: | ||||||
|  | 		printf(" %s", tetra_get_mm_pdut_name(bits_to_uint(bits+3, 4), 0)); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rx_resrc(struct tetra_tmvsap_prim *tmvp) | ||||||
|  | { | ||||||
|  | 	struct tmv_unitdata_param *tup = &tmvp->u.unitdata; | ||||||
|  | 	struct tetra_resrc_decoded rsd; | ||||||
|  | 	int tmpdu_offset; | ||||||
|  | 
 | ||||||
|  | 	memset(&rsd, 0, sizeof(rsd)); | ||||||
|  | 	tmpdu_offset = macpdu_decode_resource(&rsd, tup->mac_block); | ||||||
|  | 
 | ||||||
|  | 	printf("RESOURCE Encr=%u, Length_ind=%u Addr=%s ", | ||||||
|  | 		rsd.encryption_mode, rsd.length_ind, | ||||||
|  | 		tetra_addr_dump(&rsd.addr)); | ||||||
|  | 
 | ||||||
|  | 	if (rsd.chan_alloc_pres) | ||||||
|  | 		printf("ChanAlloc=%s ", tetra_alloc_dump(&rsd.cad)); | ||||||
|  | 
 | ||||||
|  | 	if (rsd.length_ind && rsd.encryption_mode == 0) { | ||||||
|  | 		int len_bits = rsd.length_ind*8; | ||||||
|  | 		if (tup->mac_block + tmpdu_offset + len_bits > | ||||||
|  | 					tup->mac_block + tup->mac_block_len) | ||||||
|  | 			len_bits = tup->mac_block_len - tmpdu_offset; | ||||||
|  | 		rx_tm_sdu(tup->mac_block + tmpdu_offset, len_bits); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	printf("\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dump_access(struct tetra_access_field *acc, unsigned int num) | ||||||
|  | { | ||||||
|  | 	printf("ACCESS%u: %c/%u ", num, 'A'+acc->access_code, acc->base_frame_len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rx_aach(struct tetra_tmvsap_prim *tmvp) | ||||||
|  | { | ||||||
|  | 	struct tmv_unitdata_param *tup = &tmvp->u.unitdata; | ||||||
|  | 	struct tetra_acc_ass_decoded aad; | ||||||
|  | 
 | ||||||
|  | 	printf("ACCESS-ASSIGN PDU: "); | ||||||
|  | 
 | ||||||
|  | 	memset(&aad, 0, sizeof(aad)); | ||||||
|  | 	macpdu_decode_access_assign(&aad, tup->mac_block, | ||||||
|  | 				    tup->tdma_time.fn == 18 ? 1 : 0); | ||||||
|  | 
 | ||||||
|  | 	if (aad.pres & TETRA_ACC_ASS_PRES_ACCESS1) | ||||||
|  | 		dump_access(&aad.access[0], 1); | ||||||
|  | 	if (aad.pres & TETRA_ACC_ASS_PRES_ACCESS2) | ||||||
|  | 		dump_access(&aad.access[1], 2); | ||||||
|  | 	if (aad.pres & TETRA_ACC_ASS_PRES_DL_USAGE) | ||||||
|  | 		printf("DL_USAGE: %s ", tetra_get_dl_usage_name(aad.dl_usage)); | ||||||
|  | 	if (aad.pres & TETRA_ACC_ASS_PRES_UL_USAGE) | ||||||
|  | 		printf("UL_USAGE: %s ", tetra_get_ul_usage_name(aad.ul_usage)); | ||||||
|  | 
 | ||||||
|  | 	printf("\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int rx_tmv_unitdata_ind(struct tetra_tmvsap_prim *tmvp) | ||||||
|  | { | ||||||
|  | 	struct tmv_unitdata_param *tup = &tmvp->u.unitdata; | ||||||
|  | 	uint8_t pdu_type = bits_to_uint(tup->mac_block, 2); | ||||||
|  | 	const char *pdu_name; | ||||||
|  | 	struct msgb *gsmtap_msg; | ||||||
|  | 
 | ||||||
|  | 	if (tup->lchan == TETRA_LC_BSCH) | ||||||
|  | 		pdu_name = "SYNC"; | ||||||
|  | 	else if (tup->lchan == TETRA_LC_AACH) | ||||||
|  | 		pdu_name = "ACCESS-ASSIGN"; | ||||||
|  | 	else { | ||||||
|  | 		pdu_type = bits_to_uint(tup->mac_block, 2); | ||||||
|  | 		pdu_name = tetra_get_macpdu_name(pdu_type); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	printf("TMV-UNITDATA.ind %s %s CRC=%u %s\n", | ||||||
|  | 		tetra_tdma_time_dump(&tup->tdma_time), | ||||||
|  | 		tetra_get_lchan_name(tup->lchan), | ||||||
|  | 		tup->crc_ok, pdu_name); | ||||||
|  | 
 | ||||||
|  | 	if (!tup->crc_ok) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	gsmtap_msg = tetra_gsmtap_makemsg(&tup->tdma_time, tup->lchan, tup->tdma_time.tn, | ||||||
|  | 					  /* FIXME: */ 0, 0, 0, tup->mac_block, tup->mac_block_len); | ||||||
|  | 	if (gsmtap_msg) | ||||||
|  | 		tetra_gsmtap_sendmsg(gsmtap_msg); | ||||||
|  | 
 | ||||||
|  | 	switch (tup->lchan) { | ||||||
|  | 	case TETRA_LC_AACH: | ||||||
|  | 		rx_aach(tmvp); | ||||||
|  | 		break; | ||||||
|  | 	case TETRA_LC_BNCH: | ||||||
|  | 	case TETRA_LC_UNKNOWN: | ||||||
|  | 	case TETRA_LC_SCH_F: | ||||||
|  | 		if (pdu_type == TETRA_PDU_T_BROADCAST) | ||||||
|  | 			rx_bcast(tmvp); | ||||||
|  | 		if (pdu_type == TETRA_PDU_T_MAC_RESOURCE) | ||||||
|  | 			rx_resrc(tmvp); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	if (pdu_type == TETRA_PDU_T_MAC_FRAG_END) { | ||||||
|  | 		if (tup->mac_block[3] == TETRA_MAC_FRAGE_FRAG) | ||||||
|  | 			rx_tm_sdu(tup->mac_block+4, 100 /*FIXME*/); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int upper_mac_prim_recv(struct osmo_prim_hdr *op, void *priv) | ||||||
|  | { | ||||||
|  | 	struct tetra_tmvsap_prim *tmvp; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	switch (op->sap) { | ||||||
|  | 	case TETRA_SAP_TMV: | ||||||
|  | 		tmvp = (struct tetra_tmvsap_prim *) op; | ||||||
|  | 		rc = rx_tmv_unitdata_ind(tmvp); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		printf("primitive on unknown sap\n"); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	free(op); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								src/tetra_upper_mac.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/tetra_upper_mac.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | #ifndef TETRA_UPPER_MAC_H | ||||||
|  | #define TETRA_UPPER_MAC_H | ||||||
|  | 
 | ||||||
|  | #include "tetra_prim.h" | ||||||
|  | 
 | ||||||
|  | int upper_mac_prim_recv(struct osmo_prim_hdr *op, void *priv); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Harald Welte
						Harald Welte