From 976526913d2df073d67455ca76119e9df2561b83 Mon Sep 17 00:00:00 2001 From: cmayer Date: Fri, 10 May 2019 11:48:56 +0200 Subject: [PATCH] added deinterleaving (intermediate) --- examples/test_188-110A.grc | 126 ++++++- examples/test_s4285.grc | 398 ++-------------------- grc/digitalhf_physical_layer_driver.xml | 6 + python/msg_proxy.py | 19 +- python/physical_layer/MIL_STD_188_110A.py | 61 ++-- python/physical_layer/MIL_STD_188_110D.py | 73 +++- python/physical_layer/STANAG_4285.py | 52 ++- python/physical_layer/common.py | 46 +++ python/physical_layer_driver.py | 5 +- 9 files changed, 338 insertions(+), 448 deletions(-) diff --git a/examples/test_188-110A.grc b/examples/test_188-110A.grc index b37ab02..ffc0d90 100644 --- a/examples/test_188-110A.grc +++ b/examples/test_188-110A.grc @@ -437,7 +437,7 @@ _coordinate - (426, 368) + (426, 442) _rotation @@ -460,6 +460,57 @@ 1 + + blocks_file_sink + + append + False + + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + file + /Users/chm/Software/gr-digitalhf/examples/test_bits.bin + + + _coordinate + (149, 293) + + + _rotation + 180 + + + id + blocks_file_sink_0 + + + type + byte + + + unbuffered + True + + + vlen + 1 + + blocks_float_to_complex @@ -527,7 +578,7 @@ _coordinate - (714, 261) + (704, 261) _rotation @@ -550,6 +601,53 @@ (1+(nB+nF)*sps) + + blocks_pdu_to_tagged_stream + + alias + + + + comment + + + + affinity + + + + _enabled + 1 + + + _coordinate + (405, 304) + + + _rotation + 180 + + + id + blocks_pdu_to_tagged_stream_0 + + + type + byte + + + tag + packet_len + + + maxoutbuf + 0 + + + minoutbuf + 0 + + blocks_tag_debug @@ -708,7 +806,7 @@ repeat - True + False @@ -735,7 +833,7 @@ _coordinate - (661, 325) + (704, 346) _rotation @@ -814,7 +912,7 @@ _coordinate - (394, 464) + (352, 528) gui_hint @@ -1169,7 +1267,7 @@ _coordinate - (181, 357) + (181, 432) gui_hint @@ -1536,7 +1634,7 @@ _coordinate - (426, 293) + (448, 368) gui_hint @@ -1840,7 +1938,7 @@ tr_tag - "soft_dec" + "packet_len" type @@ -2108,6 +2206,12 @@ 0 0 + + blocks_pdu_to_tagged_stream_0 + blocks_file_sink_0 + 0 + 0 + blocks_throttle_0 digitalhf_physical_layer_driver_0 @@ -2132,6 +2236,12 @@ 1 1 + + digitalhf_physical_layer_driver_0 + blocks_pdu_to_tagged_stream_0 + bits + pdus + digitalhf_physical_layer_driver_0 blocks_complex_to_mag_0 diff --git a/examples/test_s4285.grc b/examples/test_s4285.grc index 2c69eeb..8841afe 100644 --- a/examples/test_s4285.grc +++ b/examples/test_s4285.grc @@ -93,77 +93,6 @@ (0,0) - - variable_cc_decoder_def - - padding - False - - - comment - - - - k - 7 - - - dim1 - 1 - - - dim2 - 1 - - - _enabled - True - - - state_end - -1 - - - framebits - 30*8 - - - _coordinate - (32, 341) - - - _rotation - 0 - - - id - CCSDS_27 - - - value - "ok" - - - ndim - 0 - - - polys - [109,79] - - - rate - 2 - - - state_start - 0 - - - mode - fec.CC_STREAMING - - variable_qtgui_range @@ -235,7 +164,7 @@ value - 0 + '600/L' _enabled @@ -287,7 +216,7 @@ num_opts - 3 + 0 option0 @@ -311,7 +240,7 @@ options - [0, 1, 2] + ['2400/L', '1200/L', '600/L', '300/L', '150/L', '75/L'] orient @@ -524,49 +453,6 @@ 5 - - variable_dummy_decoder_def - - comment - - - - dim1 - 1 - - - dim2 - 1 - - - _enabled - True - - - framebits - 2048 - - - _coordinate - (106, 634) - - - _rotation - 0 - - - id - variable_dummy_decoder_def_0 - - - value - "ok" - - - ndim - 0 - - analog_agc2_xx @@ -650,7 +536,7 @@ _coordinate - (533, 368) + (490, 464) _rotation @@ -701,11 +587,11 @@ _coordinate - (1109, 581) + (192, 304) _rotation - 0 + 180 id @@ -724,57 +610,6 @@ 1 - - blocks_file_sink - - append - False - - - alias - - - - comment - - - - affinity - - - - _enabled - True - - - file - /Users/chm/Software/gr-digitalhf/examples/soft_dec_test.bin - - - _coordinate - (864, 506) - - - _rotation - 0 - - - id - blocks_file_sink_0_0 - - - type - float - - - unbuffered - False - - - vlen - 1 - - blocks_float_to_complex @@ -842,11 +677,11 @@ _coordinate - (586, 314) + (778, 250) _rotation - 180 + 0 id @@ -881,15 +716,15 @@ _enabled - True + 1 _coordinate - (576, 592) + (448, 314) _rotation - 0 + 180 id @@ -897,7 +732,7 @@ type - float + byte tag @@ -967,53 +802,6 @@ 1 - - blocks_tagged_stream_to_pdu - - alias - - - - comment - - - - affinity - - - - _enabled - 0 - - - _coordinate - (490, 730) - - - _rotation - 0 - - - id - blocks_tagged_stream_to_pdu_0 - - - type - byte - - - tag - packet_len - - - maxoutbuf - 0 - - - minoutbuf - 0 - - blocks_throttle @@ -1144,7 +932,7 @@ _coordinate - (736, 314) + (768, 325) _rotation @@ -1195,104 +983,6 @@ samp_rate - - fec_decode_ccsds_27_fb - - alias - - - - comment - - - - affinity - - - - _enabled - 0 - - - _coordinate - (853, 805) - - - _rotation - 0 - - - id - fec_decode_ccsds_27_fb_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - - fec_extended_decoder - - ann - None - - - alias - - - - comment - - - - affinity - - - - decoder_list - CCSDS_27 - - - _enabled - True - - - _coordinate - (853, 618) - - - _rotation - 0 - - - id - fec_extended_decoder_0 - - - maxoutbuf - 0 - - - minoutbuf - 0 - - - puncpat - '11' - - - threadtype - capillary - - - value - fec_extended_decoder - - qtgui_const_sink_x @@ -1321,7 +1011,7 @@ _coordinate - (320, 442) + (437, 549) gui_hint @@ -1676,7 +1366,7 @@ _coordinate - (320, 357) + (245, 453) gui_hint @@ -2043,7 +1733,7 @@ _coordinate - (320, 272) + (490, 389) gui_hint @@ -2351,7 +2041,7 @@ type - float + msg_float update_time @@ -2617,25 +2307,7 @@ blocks_pdu_to_tagged_stream_0 - blocks_file_sink_0_0 - 0 - 0 - - - blocks_pdu_to_tagged_stream_0 - fec_decode_ccsds_27_fb_0 - 0 - 0 - - - blocks_pdu_to_tagged_stream_0 - fec_extended_decoder_0 - 0 - 0 - - - blocks_pdu_to_tagged_stream_0 - qtgui_time_sink_x_1 + blocks_file_sink_0 0 0 @@ -2663,6 +2335,12 @@ 1 1 + + digitalhf_physical_layer_driver_0 + blocks_pdu_to_tagged_stream_0 + bits + pdus + digitalhf_physical_layer_driver_0 blocks_complex_to_mag_0 @@ -2677,9 +2355,9 @@ digitalhf_physical_layer_driver_0 - blocks_pdu_to_tagged_stream_0 + qtgui_time_sink_x_1 soft_dec - pdus + in digitalhf_physical_layer_driver_0 @@ -2687,28 +2365,4 @@ 2 0 - - fec_decode_ccsds_27_fb_0 - blocks_file_sink_0 - 0 - 0 - - - fec_decode_ccsds_27_fb_0 - blocks_tag_debug_0 - 0 - 0 - - - fec_extended_decoder_0 - blocks_file_sink_0 - 0 - 0 - - - fec_extended_decoder_0 - blocks_tag_debug_0 - 0 - 0 - diff --git a/grc/digitalhf_physical_layer_driver.xml b/grc/digitalhf_physical_layer_driver.xml index 904cbbd..ed13c8b 100644 --- a/grc/digitalhf_physical_layer_driver.xml +++ b/grc/digitalhf_physical_layer_driver.xml @@ -74,6 +74,12 @@ 1 + + bits + message + 1 + + taps complex diff --git a/python/msg_proxy.py b/python/msg_proxy.py index 731f1a8..cf1b5e6 100644 --- a/python/msg_proxy.py +++ b/python/msg_proxy.py @@ -44,8 +44,8 @@ class msg_proxy(gr.basic_block): self.message_port_register_out(self._port_frame_info) self.set_msg_handler(self._port_frame_info, self.msg_handler_frame) - self._port_soft_dec = pmt.intern("soft_dec") - self.message_port_register_out(self._port_soft_dec) + self._port_bits = pmt.intern("bits") + self.message_port_register_out(self._port_bits) def msg_handler_doppler(self, msg_in): ## print('-------------------- msg_handler_doppler --------------------') @@ -60,19 +60,16 @@ class msg_proxy(gr.basic_block): def msg_handler_frame(self, msg_in): ## print('-------------------- msg_handler_frame --------------------') ## print(msg_in) - symbols = pmt.to_python(pmt.dict_ref(msg_in, pmt.intern('symbols'), pmt.PMT_NIL)) + symbols = pmt.to_python(pmt.dict_ref(msg_in, pmt.intern('symbols'), pmt.PMT_NIL)) soft_dec = pmt.to_python(pmt.dict_ref(msg_in, pmt.intern('soft_dec'), pmt.PMT_NIL)) symb,constellation_idx,do_continue,save_soft_dec = self._obj.get_next_frame(symbols) if do_continue and len(soft_dec) != 0: - d = self._obj.decode_soft_dec(soft_dec) + bits = np.array(self._obj.decode_soft_dec(soft_dec), dtype=np.uint8) msg_out = pmt.make_dict() - msg_out = pmt.dict_add(msg_out, pmt.intern('packet_len'), pmt.to_pmt(len(d))) - d = np.array(d, dtype=np.float32) - d[abs(d)==np.Inf] = 0 - vv = pmt.to_pmt(d) - msg = pmt.cons(msg_out, vv) - self.message_port_pub(self._port_soft_dec, msg) - ## TODO: publish the bits if success + msg_out = pmt.dict_add(msg_out, pmt.intern('packet_len'), pmt.to_pmt(len(bits))) + msg = pmt.cons(msg_out, pmt.to_pmt(bits)) + self.message_port_pub(self._port_bits, msg) + ##print('symb=', symb, symb['symb'], symb['scramble']) msg_out = pmt.make_dict() msg_out = pmt.dict_add(msg_out, pmt.intern('symb'), pmt.to_pmt(symb['symb'])) diff --git a/python/physical_layer/MIL_STD_188_110A.py b/python/physical_layer/MIL_STD_188_110A.py index f145cfb..9858492 100644 --- a/python/physical_layer/MIL_STD_188_110A.py +++ b/python/physical_layer/MIL_STD_188_110A.py @@ -3,6 +3,7 @@ from __future__ import print_function import numpy as np import common +from digitalhf.digitalhf_swig import viterbi27 ## ---- Walsh-4 codes ----------------------------------------------------------- WALSH = np.array([[0,0,0,0, 0,0,0,0], # 0 - 000 @@ -75,26 +76,26 @@ MODE_8PSK=2 ## ---- mode definitions -------------------------------------------------------- MODE = [[{} for _ in range(8)] for _ in range(8)] -MODE[7][6] = {'bit_rate':4800, 'ci':MODE_8PSK, 'interleaver':['N', 1, 1], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate': -1 } -MODE[7][7] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['N', 1, 1], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate':1./2} +MODE[7][6] = {'bit_rate':4800, 'ci':MODE_8PSK, 'interleaver':['N', 1, 1], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate': 'n/a', 'repeat': 1} +MODE[7][7] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['N', 1, 1], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} -MODE[6][4] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['S', 40, 72], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate':1./2} -MODE[4][4] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['L', 40,576], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate':1./2} +MODE[6][4] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['S', 40, 72], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} +MODE[4][4] = {'bit_rate':2400, 'ci':MODE_8PSK, 'interleaver':['L', 40,576], 'unknown':32,'known':16, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} -MODE[6][5] = {'bit_rate':1200, 'ci':MODE_QPSK, 'interleaver':['S', 40, 36], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./2} -MODE[4][5] = {'bit_rate':1200, 'ci':MODE_QPSK, 'interleaver':['L', 40,288], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./2} +MODE[6][5] = {'bit_rate':1200, 'ci':MODE_QPSK, 'interleaver':['S', 40, 36], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} +MODE[4][5] = {'bit_rate':1200, 'ci':MODE_QPSK, 'interleaver':['L', 40,288], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} -MODE[6][6] = {'bit_rate': 600, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./2} -MODE[4][6] = {'bit_rate': 600, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./2} +MODE[6][6] = {'bit_rate': 600, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} +MODE[4][6] = {'bit_rate': 600, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/2', 'repeat': 1} -MODE[6][7] = {'bit_rate': 300, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./4} -MODE[4][7] = {'bit_rate': 300, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./4} +MODE[6][7] = {'bit_rate': 300, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/4', 'repeat': 2} +MODE[4][7] = {'bit_rate': 300, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/4', 'repeat': 2} -MODE[7][4] = {'bit_rate': 150, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./8} -MODE[5][4] = {'bit_rate': 150, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate':1./8} +MODE[7][4] = {'bit_rate': 150, 'ci':MODE_BPSK, 'interleaver':['S', 40, 18], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/8', 'repeat': 4} +MODE[5][4] = {'bit_rate': 150, 'ci':MODE_BPSK, 'interleaver':['L', 40,144], 'unknown':20,'known':20, 'nsymb': 1, 'coding_rate': '1/8', 'repeat': 4} -MODE[7][5] = {'bit_rate': 75, 'ci':MODE_QPSK, 'interleaver':['S', 10, 9], 'unknown':-1,'known': 0, 'nsymb':32, 'coding_rate':1./2} -MODE[5][4] = {'bit_rate': 75, 'ci':MODE_QPSK, 'interleaver':['L', 20, 36], 'unknown':-1,'known': 0, 'nsymb':32, 'coding_rate':1./2} +MODE[7][5] = {'bit_rate': 75, 'ci':MODE_QPSK, 'interleaver':['S', 10, 9], 'unknown':-1,'known': 0, 'nsymb':32, 'coding_rate': '1/2', 'repeat': 1} +MODE[5][4] = {'bit_rate': 75, 'ci':MODE_QPSK, 'interleaver':['L', 20, 36], 'unknown':-1,'known': 0, 'nsymb':32, 'coding_rate': '1/2', 'repeat': 1} ## ---- deinterleaver ----------------------------------------------------------- @@ -172,7 +173,7 @@ class PhysicalLayer(object): return [self.get_next_data_frame(success),self._mode['ci'],success,success] else: ## data mode self._frame_counter += 1 - print('test:', symbols[self._mode['unknown']:], np.mean(np.real(symbols[self._mode['unknown']:]))) + ##print('test:', symbols[self._mode['unknown']:], np.mean(np.real(symbols[self._mode['unknown']:]))) if self._frame_counter < self._num_frames_per_block-2: success = np.mean(np.real(symbols[self._mode['unknown']:])) > 0.7 return [self.get_next_data_frame(success),self._mode['ci'],success,success] @@ -205,8 +206,9 @@ class PhysicalLayer(object): for _ in range(sps)], dtype=np.complex64) ## find starting point _,_zp = self.get_preamble_z() - cc = np.correlate(iq_samples, _zp) ##zp[0:3*32*sps]) + cc = np.correlate(iq_samples, zp[0:3*32*sps]) imax = np.argmax(np.abs(cc[0:2*32*sps])) + print('imax=', imax, len(iq_samples), len(cc)) apks = np.abs(cc[(imax, imax+3*32*sps),]) tpks = np.abs(cc[imax+3*16*sps:imax+5*16*sps]) print('imax=', imax, 'apks=',apks, @@ -232,12 +234,14 @@ class PhysicalLayer(object): print('data=',data) self._pre_counter = sum([(x&3)*(1<<2*y) for (x,y) in zip(data[11:14][::-1], range(3))]) self._d1d2 = data[9:11] - self._mode = MODE[data[9]][data[10]] - self._block_len = 11520 if self._mode['interleaver'][0] == 'L' else 1440 - self._frame_len = self._mode['known'] + self._mode['unknown'] + self._mode = mode = MODE[data[9]][data[10]] + self._block_len = 11520 if mode['interleaver'][0] == 'L' else 1440 + self._frame_len = mode['known'] + mode['unknown'] self._num_frames_per_block = self._block_len/self._frame_len; - self._deinterleaver = Deinterleaver(self._mode['interleaver'][1], self._mode['interleaver'][2]) - print(self._d1d2, self._mode, self._frame_len) + self._deinterleaver = Deinterleaver(mode['interleaver'][1], mode['interleaver'][2]) + self._depuncturer = common.Depuncturer(repeat=mode['repeat']) + self._viterbi_decoder = viterbi27(0x6d, 0x4f) + print(self._d1d2, mode, self._frame_len) return True def set_mode(self, _): @@ -247,10 +251,17 @@ class PhysicalLayer(object): print('decode_soft_dec', len(soft_dec), soft_dec.dtype) r = self._deinterleaver.load(soft_dec) print('decode_soft_dec r=', r.shape) - if r.shape[0] != 0: - for i in range(r.shape[0]//4): - print('BB:', r[4*i]<0, r[4*i+2]<0, '|', r[4*i+1]<0, r[4*i+3]<0) - return soft_dec ## TODO + if r.shape[0] == 0: + return [] + ##for i in range(r.shape[0]//4): + ## print('BB:', r[4*i]<0, r[4*i+2]<0, '|', r[4*i+1]<0, r[4*i+3]<0) + + rd = self._depuncturer.process(r) + self._viterbi_decoder.reset() + decoded_bits = self._viterbi_decoder.udpate(rd) + print('bits=', decoded_bits) + print('quality={}%'.format(100.0*self._viterbi_decoder.quality()/(2*len(decoded_bits)))) + return decoded_bits @staticmethod def get_preamble(): diff --git a/python/physical_layer/MIL_STD_188_110D.py b/python/physical_layer/MIL_STD_188_110D.py index 99bf2db..2065820 100644 --- a/python/physical_layer/MIL_STD_188_110D.py +++ b/python/physical_layer/MIL_STD_188_110D.py @@ -3,11 +3,12 @@ from __future__ import print_function import numpy as np import common +from digitalhf.digitalhf_swig import viterbi27, viterbi29 ## ---- constellations --------------------------------------------------------- BPSK=np.array(zip(np.exp(2j*np.pi*np.arange(2)/2), [0,1]), common.CONST_DTYPE) QPSK=np.array(zip(np.exp(2j*np.pi*np.arange(4)/4), [0,1,3,2]), common.CONST_DTYPE) -PSK8=np.array(zip(np.exp(2j*np.pi*np.arange(8)/8), [1,0,2,3,6,7,5,4]), common.CONST_DTYPE) +PSK8=np.array(zip(np.exp(2j*np.pi*np.arange(8)/8), [1,0,2,3,7,6,4,5]), common.CONST_DTYPE) QAM16=np.array( zip([+0.866025+0.500000j, +0.500000+0.866025j, +1.000000+0.000000j, +0.258819+0.258819j, -0.500000+0.866025j, +0.000000+1.000000j, -0.866025+0.500000j, -0.258819+0.258819j, @@ -580,7 +581,7 @@ CODE_RATE_PUNCT = { # [code rate][K] -> punct pattern, [code rate]['Rep'] -> # r '5/6' : { 'K=7': [ '11010', '10101'], 'K=9': [ '10110', '11001'], 'Rep': 1 }, '4/5' : { 'K=7': [ '1111', '1000'], 'K=9': [ '1101', '1010'], 'Rep': 1 }, '3/4' : { 'K=7': [ '110', '101'], 'K=9': [ '111', '100'], 'Rep': 1 }, - '2/3' : { 'K=7': [ '11', '10'], 'K=9': [ '11', '10'], 'Rep': 1 }, + '2/3' : { 'K=7': [ '11', '10'], 'K=9': [ '11', '10'], 'Rep': 1 }, '4/7' : { 'K=7': [ '1111', '0111'], 'K=9': [ '1111', '0111'], 'Rep': 1 }, '9/16': { 'K=7': ['111101111','111111011'], 'K=9': ['111101111','111111011'], 'Rep': 1 }, '1/2' : { 'K=7': [ '1', '1'], 'K=9': [ '1', '1'], 'Rep': 1 }, @@ -594,6 +595,28 @@ CODE_RATE_PUNCT = { # [code rate][K] -> punct pattern, [code rate]['Rep'] -> # r '1/16': { 'K=7': [ '1', '1'], 'K=9': [ '1', '1'], 'Rep': 8 } } +## ---- deinterleaver ----------------------------------------------------------- +class Deinterleaver(object): + """deinterleave""" + def __init__(self, length, incr): + self._length = length + self._array = np.zeros(length, dtype=np.float32) + self._incr = incr + self._idx = np.mod(incr*np.arange(length), length) + self._i = 0 + + def fetch(self): + self._i = 0 + return self._array[self._idx] + + def load(self, a): + print('deinterleaver load', len(a), self._i, self._incr, self._length) + input_len = len(a) + assert(self._i+input_len <= self._length) + self._array[self._i:self._i+input_len] = a + self._i += input_len + return (self._i == self._length) + ## ---- Walsh-4 codes ---------------------------------------------------------- WALSH = np.array([[0,0,0,0], # 0 - 00 [0,1,0,1], # 1 - 01 @@ -806,18 +829,26 @@ class PhysicalLayer(object): b = np.flip(b) self._wid = np.packbits(b[0:4])[0]>>4 self._intl_type = INTERLEAVERS[np.packbits(b[4:6])[0]>>6] - self._constraint_length = b[6] + self._constraint_length = 'K=7' if b[6] == 0 else 'K=9' self._data_mode = WID_MODE[self._wid] self._unknown = BW_UNKNOWN[self._bw][self._wid] self._known = BW_KNOWN[self._bw][self._wid] mp_info = MP_LEN_BASE_SHIFT[self._known] self._mp = make_mp(self._known, mp_info['base_len'], 0) self._mp_shifted = make_mp(self._known, mp_info['base_len'], mp_info['base_shift']) - self._intl_info = BW_INTL[self._bw][self._wid][self._intl_type] - self._intl_incr = BW_INTL_INCR[self._bw][self._wid][self._intl_type] - self._intl_frames = self._intl_info[0] - self._intl_bits = self._intl_info[1] - print('b=', b, success, self._wid, self._intl_type, self._intl_frames, self._intl_incr, self._constraint_length, + intl_info = BW_INTL[self._bw][self._wid][self._intl_type] + self._intl_frames = intl_info[0] + + code_rate = BW_CODE_RATE[self._bw][self._wid] + punct = CODE_RATE_PUNCT[code_rate] + + self._deinterleaver = Deinterleaver(length = intl_info[1], + incr = BW_INTL_INCR[self._bw][self._wid][self._intl_type]) + self._depuncturer = common.Depuncturer(repeat = punct['Rep'], + puncture_pattern = punct[self._constraint_length]) + self._viterbi_decoder = viterbi27(0x6d, 0x4f) if self._constraint_length == 'K=7' else viterbi29() + + print('b=', b, success, self._wid, self._intl_type, self._intl_frames, self._constraint_length, self._known, self._unknown) return success @@ -851,9 +882,19 @@ class PhysicalLayer(object): def decode_soft_dec(self, soft_dec): print('decode_soft_dec', len(soft_dec), soft_dec.dtype) - return soft_dec ## TODO + is_full = self._deinterleaver.load(soft_dec) + if not is_full: + return [] - def get_preamble(): + r = self._deinterleaver.fetch() + rd = self._depuncturer.process(r) + self._viterbi_decoder.reset() + decoded_bits = self._viterbi_decoder.udpate(rd) + print('quality={}% num_bits={}'.format(100.0*self._viterbi_decoder.quality()/(2*len(decoded_bits)), + len(decoded_bits))) + return decoded_bits + + def get_preamble(self): """fixed symbols + scrambler""" return self._fixed_s @@ -865,9 +906,9 @@ class PhysicalLayer(object): if __name__ == '__main__': p = PhysicalLayer(5) p.set_mode('24 kHz') - s = ScrambleData(3) - for i in range(10): - print(i, s.next()) - print(np.real(make_mp(24,13,0))) - print(np.real(make_mp(24,13,6))) - #print(make_mp(72,36,0)) + #s = ScrambleData(3) + #for i in range(10): + # print(i, s.next()) + # print(np.real(make_mp(24,13,0))) + # print(np.real(make_mp(24,13,6))) + print(mp_base(196)) diff --git a/python/physical_layer/STANAG_4285.py b/python/physical_layer/STANAG_4285.py index 9ea87cd..bfc1a26 100644 --- a/python/physical_layer/STANAG_4285.py +++ b/python/physical_layer/STANAG_4285.py @@ -2,6 +2,9 @@ import numpy as np +import common +from digitalhf.digitalhf_swig import viterbi27 + class Deinterleaver(object): "S4285 deinterleaver" def __init__(self, incr): @@ -18,17 +21,29 @@ class Deinterleaver(object): def fetch(self): return np.array([self._buf[(9*i)%32][0] for i in range(32)]) + +MODE_BPSK=0 +MODE_QPSK=1 +MODE_8PSK=2 + +MODES = { ## [BPS]['const'] [BPS]['punct'] [BPS]['repeat'] + '2400': {'const': MODE_8PSK, 'punct': ['11', '10'] , 'repeat': 1}, + '1200': {'const': MODE_QPSK, 'punct': [ '1', '1'] , 'repeat': 1}, + '600': {'const': MODE_BPSK, 'punct': [ '1', '1'] , 'repeat': 1}, + '300': {'const': MODE_BPSK, 'punct': [ '1', '1'] , 'repeat': 2}, + '150': {'const': MODE_BPSK, 'punct': [ '1', '1'] , 'repeat': 4}, + '75': {'const': MODE_BPSK, 'punct': [ '1', '1'] , 'repeat': 8} +} + +DEINTERLEAVER_INCR = { 'S': 1, 'L': 12 } + class PhysicalLayer(object): """Physical layer description for STANAG 4285""" - MODE_BPSK=0 - MODE_QPSK=1 - MODE_8PSK=2 - def __init__(self, sps): """intialization""" self._sps = sps - self._mode = self.MODE_QPSK + ##self._mode = self.MODE_QPSK self._frame_counter = 0 self._is_first_frame = True self._constellations = [self.make_psk(2, [0,1]), @@ -36,12 +51,16 @@ class PhysicalLayer(object): self.make_psk(8, [1,0,2,3,6,7,5,4])] self._preamble = self.get_preamble() self._data = self.get_data() - self._deinterleaver = Deinterleaver(12) ## for now BPSK L fixed + self._viterbi_decoder = viterbi27(0x6d, 0x4f) def set_mode(self, mode): - """set phase modultation type""" + """set phase modultation type: 'BPS/S' or 'BPS/L'""" print('set_mode', mode) - self._mode = int(mode) + bps,intl = mode.split('/') + self._mode = MODES[bps]['const'] + self._deinterleaver = Deinterleaver(DEINTERLEAVER_INCR[intl]) + self._depuncturer = common.Depuncturer(repeat = MODES[bps]['repeat'], + puncture_pattern = MODES[bps]['punct']) def get_constellations(self): return self._constellations @@ -58,7 +77,7 @@ class PhysicalLayer(object): success,frame_description = True,[] if (self._frame_counter%2) == 0: - frame_description = [self._preamble,self.MODE_BPSK,success,False] + frame_description = [self._preamble,MODE_BPSK,success,False] else: idx = range(30,80) z = symbols[idx]*np.conj(self._preamble['symb'][idx]) @@ -106,13 +125,16 @@ class PhysicalLayer(object): return 2,np.array([z for z in a['symb'][0:31] for _ in range(self._sps)]) def decode_soft_dec(self, soft_dec): - assert(len(soft_dec) == 128) - print('decode_soft_dec: ', len(soft_dec)) - res = [] - for i in range(0,128,32): + n = len(soft_dec) + r = [] + for i in range(0,n,32): self._deinterleaver.push(soft_dec[i:i+32]) - res.extend(self._deinterleaver.fetch().tolist()) - return res + r.extend(self._deinterleaver.fetch().tolist()) + rd = self._depuncturer.process(np.array(r, dtype=np.float32)) + decoded_bits = self._viterbi_decoder.udpate(rd) + print('bits=', decoded_bits) + print('quality={}%'.format(100.0*self._viterbi_decoder.quality()/(2*len(decoded_bits)))) + return decoded_bits @staticmethod def get_preamble(): diff --git a/python/physical_layer/common.py b/python/physical_layer/common.py index 93e5dad..eac06e3 100644 --- a/python/physical_layer/common.py +++ b/python/physical_layer/common.py @@ -26,7 +26,53 @@ def freq_est(z): mod_2pi = lambda x : np.mod(x-np.pi, 2*np.pi) - np.pi return np.sum(w[1:] * mod_2pi(np.diff(np.angle(R)))) ## eq (8) +class Depuncturer(object): + def __init__(self, repeat=1, puncture_pattern=['1','1']): + assert(repeat >= 1) + self._repeat = repeat + self._num_patterns = num_patterns = len(puncture_pattern) + assert(num_patterns >= 2) + assert(all([len(puncture_pattern[0]) == len(p) for p in puncture_pattern[1:]])) + m = np.array([x=='1' for y in puncture_pattern for x in y]) + self._num_unpacked = len(m) + self._num_packed = np.sum(m) + self._pattern = m.reshape(num_patterns, self._num_unpacked//num_patterns).transpose().reshape(1, self._num_unpacked)[0] + self._range_packed = np.arange(self._num_packed) + self._range_unpacked = np.arange(self._num_unpacked) + + def process(self, x): + n = len(x) + assert(n%(self._num_packed * self._repeat) == 0) + ## (1) unpack + xd = np.zeros(n * self._num_unpacked // self._num_packed, dtype=np.float64) + i = 0 + j = 0 + while i < len(xd): + xd[(i + self._range_unpacked)[self._pattern]] += x[j + self._range_packed] + j += self._num_packed + i += self._num_unpacked + assert(j == n) + assert(i == len(xd)) + if self._repeat == 1: + return xd + + ## (2) combine repeated data + xu = np.zeros(len(xd) // self._repeat, dtype=np.float64) + i = 0 + j = 0 + m = self._num_patterns + r = np.arange(m) + while i < len(xu): + for k in range(self._repeat): + xu[i + r] += xd[j + r] + j += m + i += m + assert(i == len(xu)) + assert(j == len(xd)) + return xu + if __name__ == '__main__': idx=np.arange(3) z=np.exp(1j*idx*0.056+1j) print(freq_est(z)/0.056) + diff --git a/python/physical_layer_driver.py b/python/physical_layer_driver.py index 03b409d..8f9e28a 100644 --- a/python/physical_layer_driver.py +++ b/python/physical_layer_driver.py @@ -54,6 +54,7 @@ class physical_layer_driver(gr.hier_block2): ## TODO: get rrc tap information from physical layer description self._rrc_taps = filter.firdes.root_raised_cosine(1.0, samp_rate, samp_rate/sps, 0.35, 11*sps) preamble_offset,preamble_samples = self._physical_layer_driver_description.get_preamble_z() + preamble_length = self._sps * len(self._physical_layer_driver_description.get_preamble()) ## len(preamble_samples) preamble_length = len(preamble_samples) self._rrc_filter = filter.fir_filter_ccc(1, (self._rrc_taps)) self._corr_est = digital.corr_est_cc(symbols = (preamble_samples.tolist()), @@ -88,7 +89,9 @@ class physical_layer_driver(gr.hier_block2): self.message_port_register_hier_out('soft_dec') self.msg_connect((self._adaptive_filter, 'soft_dec'), (self, 'soft_dec')) - self.msg_connect((self._msg_proxy, 'soft_dec'), (self, 'soft_dec')) + + self.message_port_register_hier_out('bits') + self.msg_connect((self._msg_proxy, 'bits'), (self, 'bits')) def set_mu(self, mu): self._adaptive_filter.set_mu(mu)