mirror of
https://github.com/hb9fxq/gr-digitalhf
synced 2024-12-22 15:10:00 +00:00
WN0 orthogonal WALSH mode for 110D added
This commit is contained in:
parent
6cf9752275
commit
8ff2165443
|
@ -618,15 +618,35 @@ class Deinterleaver(object):
|
||||||
return (self._i == self._length)
|
return (self._i == self._length)
|
||||||
|
|
||||||
## ---- Walsh-4 codes ----------------------------------------------------------
|
## ---- Walsh-4 codes ----------------------------------------------------------
|
||||||
WALSH = np.array([[0,0,0,0], # 0 - 00
|
WALSH4 = np.array([[0,0,0,0], # 0 - 00
|
||||||
[0,1,0,1], # 1 - 01
|
[0,1,0,1], # 1 - 01
|
||||||
[0,0,1,1], # 2 - 10
|
[0,0,1,1], # 2 - 10
|
||||||
[0,1,1,0]], # 3 - 11
|
[0,1,1,0]], # 3 - 11
|
||||||
dtype=np.uint8)
|
dtype=np.uint8)
|
||||||
|
FROM_WALSH4 = -np.ones(256, dtype=np.int8)
|
||||||
FROM_WALSH = -np.ones(256, dtype=np.int8)
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
FROM_WALSH[np.packbits(WALSH[i][:])[0]] = i
|
FROM_WALSH4[np.packbits(WALSH4[i][:])[0]] = i
|
||||||
|
|
||||||
|
WALSH16 = np.array([[0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0], # 0 - 0000
|
||||||
|
[0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1], # 1 - 0001
|
||||||
|
[0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1], # 2 - 0010
|
||||||
|
[0,1,1,0, 0,1,1,0, 0,1,1,0, 0,1,1,0], # 3 - 0011
|
||||||
|
[0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1], # 4 - 0100
|
||||||
|
[0,1,0,1, 1,0,1,0, 0,1,0,1, 1,0,1,0], # 5 - 0101
|
||||||
|
[0,0,1,1, 1,1,0,0, 0,0,1,1, 1,1,0,0], # 6 - 0110
|
||||||
|
[0,1,1,0, 1,0,0,1, 0,1,1,0, 1,0,0,1], # 7 - 0111
|
||||||
|
[0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1], # 8 - 1000
|
||||||
|
[0,1,0,1, 0,1,0,1, 1,0,1,0, 1,0,1,0], # 9 - 1001
|
||||||
|
[0,0,1,1, 0,0,1,1, 1,1,0,0, 1,1,0,0], # 10 - 1010
|
||||||
|
[0,1,1,0, 0,1,1,0, 1,0,0,1, 1,0,0,1], # 11 - 1011
|
||||||
|
[0,0,0,0, 1,1,1,1, 1,1,1,1, 0,0,0,0], # 12 - 1100
|
||||||
|
[0,1,0,1, 1,0,1,0, 1,0,1,0, 0,1,0,1], # 13 - 1101
|
||||||
|
[0,0,1,1, 1,1,0,0, 1,1,0,0, 0,0,1,1], # 14 - 1110
|
||||||
|
[0,1,1,0, 1,0,0,1, 1,0,0,1, 0,1,1,0]], # 15 - 1111
|
||||||
|
dtype=np.uint8)
|
||||||
|
FROM_WALSH16 = -np.ones(256, dtype=np.int8)
|
||||||
|
for i in range(16):
|
||||||
|
FROM_WALSH16[np.packbits(WALSH16[i][:])[0]] = i
|
||||||
|
|
||||||
## ---- band width - Walsh lengths table ---------------------------------------
|
## ---- band width - Walsh lengths table ---------------------------------------
|
||||||
WALSH_BW_LENGTHS = {
|
WALSH_BW_LENGTHS = {
|
||||||
|
@ -701,6 +721,35 @@ class ScrambleData(object):
|
||||||
new_state.extend(self._state[:-1])
|
new_state.extend(self._state[:-1])
|
||||||
self._state = new_state
|
self._state = new_state
|
||||||
|
|
||||||
|
class ScrambleWalsh(object):
|
||||||
|
## Trinomial(159,31)
|
||||||
|
def __init__(self):
|
||||||
|
self.reset()
|
||||||
|
def reset(self):
|
||||||
|
self._counter = 0
|
||||||
|
self._state = np.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1,
|
||||||
|
1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0,
|
||||||
|
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||||
|
1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1,
|
||||||
|
0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1,
|
||||||
|
1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0,
|
||||||
|
1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||||
|
1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1], dtype=np.bool)
|
||||||
|
def next(self):
|
||||||
|
if self._counter == 2048:
|
||||||
|
self._reset()
|
||||||
|
self._advance()
|
||||||
|
self._counter += 1
|
||||||
|
return 4*self._state[2] + 2*self._state[1] + self._state[0]
|
||||||
|
|
||||||
|
def _advance(self):
|
||||||
|
for i in range(16):
|
||||||
|
new_bit = self._state[31] ^ self._state[158]
|
||||||
|
self._state = np.roll(self._state,1)
|
||||||
|
self._state[0] = new_bit
|
||||||
|
|
||||||
## ---- physcal layer class -----------------------------------------------------
|
## ---- physcal layer class -----------------------------------------------------
|
||||||
class PhysicalLayer(object):
|
class PhysicalLayer(object):
|
||||||
"""Physical layer description for MIL-STD-188-110 Appendix D"""
|
"""Physical layer description for MIL-STD-188-110 Appendix D"""
|
||||||
|
@ -711,7 +760,7 @@ class PhysicalLayer(object):
|
||||||
self._bw = 3
|
self._bw = 3
|
||||||
self._frame_counter = -1
|
self._frame_counter = -1
|
||||||
self._constellations = [BPSK, QPSK, PSK8, QAM16, QAM32, QAM64, QAM256]
|
self._constellations = [BPSK, QPSK, PSK8, QAM16, QAM32, QAM64, QAM256]
|
||||||
self._scr_data = ScrambleData(3) ## TODO
|
self._scr_data = ScrambleData(3) ## TODO
|
||||||
|
|
||||||
def get_constellations(self):
|
def get_constellations(self):
|
||||||
return self._constellations
|
return self._constellations
|
||||||
|
@ -746,16 +795,35 @@ class PhysicalLayer(object):
|
||||||
return [self._fixed_s,MODE_BPSK,success,False]
|
return [self._fixed_s,MODE_BPSK,success,False]
|
||||||
else: ## TODO
|
else: ## TODO
|
||||||
self._frame_counter = 0
|
self._frame_counter = 0
|
||||||
self._scr_data.reset()
|
if self._wid != MODE_WALSH:
|
||||||
self._state = 'MP'
|
self._scr_data.reset()
|
||||||
[mode,a] = self.get_next_data_frame(success)
|
self._state = 'MP'
|
||||||
return [a,mode,success,False]
|
[mode,a] = self.get_next_data_frame(success)
|
||||||
|
return [a,mode,success,False]
|
||||||
|
else: ## WID0
|
||||||
|
self._frame_counter += 1
|
||||||
|
return [self._walsh_s,MODE_BPSK,success,True]
|
||||||
else: ## data mode
|
else: ## data mode
|
||||||
print('XXXXX {} frame_counter={}'.format(self._state, self._frame_counter))
|
print('XXXXX {} frame_counter={}'.format(self._state, self._frame_counter))
|
||||||
[mode,a] = self.get_next_data_frame(success)
|
if self._wid != MODE_WALSH:
|
||||||
self._frame_counter += (self._state == 'MP')
|
[mode,a] = self.get_next_data_frame(success)
|
||||||
success = np.abs(np.real(np.mean(symbols[::2]))) > 0.7 if self._state == 'MP' else True
|
self._frame_counter += (self._state == 'MP')
|
||||||
return [a,mode,success,success]
|
success = np.abs(np.real(np.mean(symbols[::2]))) > 0.7 if self._state == 'MP' else True
|
||||||
|
return [a,mode,success,success]
|
||||||
|
else: ## WID0
|
||||||
|
self._frame_counter += 1
|
||||||
|
m = len(symbols)//32
|
||||||
|
z = np.zeros(8*m, dtype=np.complex64)
|
||||||
|
for i,s in enumerate(symbols.reshape(m, 32)):
|
||||||
|
# print('WALSH test:', i,
|
||||||
|
# np.mean(s.reshape(8,4)[0::2], 0)<0,
|
||||||
|
# np.mean(s.reshape(8,4)[1::2], 0)<0)
|
||||||
|
z[8*i :8*i+4] = np.mean(s.reshape(8,4)[0::2], 0)
|
||||||
|
z[8*i+4:8*i+8] = np.mean(s.reshape(8,4)[1::2], 0)
|
||||||
|
|
||||||
|
success = [np.mean(np.abs(np.real(z))) > 0.5, np.mean(np.abs(np.imag(z))) < 0.5]
|
||||||
|
print('WALSH test:',z[-8:], success)
|
||||||
|
return [self._walsh_s,MODE_BPSK,all(success),True]
|
||||||
|
|
||||||
def get_next_data_frame(self, success):
|
def get_next_data_frame(self, success):
|
||||||
## TODO
|
## TODO
|
||||||
|
@ -790,17 +858,17 @@ class PhysicalLayer(object):
|
||||||
|
|
||||||
def decode_walsh(self, symbols):
|
def decode_walsh(self, symbols):
|
||||||
wlen = self._wlen
|
wlen = self._wlen
|
||||||
return np.array([FROM_WALSH[np.packbits
|
return np.array([FROM_WALSH4[np.packbits
|
||||||
(np.real
|
(np.real
|
||||||
(np.sum
|
(np.sum
|
||||||
(symbols[wlen*i:wlen*(i+1)].reshape((wlen//4,4)),0))<0)[0]]
|
(symbols[wlen*i:wlen*(i+1)].reshape((wlen//4,4)),0))<0)[0]]
|
||||||
for i in range(len(symbols)//wlen)],
|
for i in range(len(symbols)//wlen)],
|
||||||
dtype=np.uint8)
|
dtype=np.uint8)
|
||||||
|
|
||||||
def decode_fixed(self, symbols):
|
def decode_fixed(self, symbols):
|
||||||
print('decode_fixed: ', len(symbols))
|
print('decode_fixed: ', len(symbols))
|
||||||
data = self.decode_walsh(symbols)
|
data = self.decode_walsh(symbols)
|
||||||
success = np.all(data == FIXED_WALSH_SYMBOLS)
|
success = np.all(data[2:] == FIXED_WALSH_SYMBOLS[2:])
|
||||||
print('data=', data, success)
|
print('data=', data, success)
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
@ -832,15 +900,19 @@ class PhysicalLayer(object):
|
||||||
b[0] ^= b[5] ^ b[4] ^ b[3]
|
b[0] ^= b[5] ^ b[4] ^ b[3]
|
||||||
success = np.all(b[0:3] == 0)
|
success = np.all(b[0:3] == 0)
|
||||||
b = np.flip(b)
|
b = np.flip(b)
|
||||||
self._wid = np.packbits(b[0:4])[0]>>4
|
self._wid = wid = np.packbits(b[0:4])[0]>>4
|
||||||
self._intl_type = INTERLEAVERS[np.packbits(b[4:6])[0]>>6]
|
self._intl_type = INTERLEAVERS[np.packbits(b[4:6])[0]>>6]
|
||||||
self._constraint_length = 'K=7' if b[6] == 0 else 'K=9'
|
self._constraint_length = 'K=7' if b[6] == 0 else 'K=9'
|
||||||
self._data_mode = WID_MODE[self._wid]
|
self._data_mode = WID_MODE[self._wid]
|
||||||
self._unknown = BW_UNKNOWN[self._bw][self._wid]
|
print('WID:', self._wid, self._intl_type, self._constraint_length,self._data_mode)
|
||||||
self._known = BW_KNOWN[self._bw][self._wid]
|
self._unknown = -1
|
||||||
mp_info = MP_LEN_BASE_SHIFT[self._known]
|
self._known = 0
|
||||||
self._mp = make_mp(self._known, mp_info['base_len'], 0)
|
if wid != MODE_WALSH:
|
||||||
self._mp_shifted = make_mp(self._known, mp_info['base_len'], mp_info['base_shift'])
|
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'])
|
||||||
intl_info = BW_INTL[self._bw][self._wid][self._intl_type]
|
intl_info = BW_INTL[self._bw][self._wid][self._intl_type]
|
||||||
self._intl_frames = intl_info[0]
|
self._intl_frames = intl_info[0]
|
||||||
|
|
||||||
|
@ -863,34 +935,52 @@ class PhysicalLayer(object):
|
||||||
self._wlen = wlen = WALSH_BW_LENGTHS[bw]
|
self._wlen = wlen = WALSH_BW_LENGTHS[bw]
|
||||||
DIBIT = np.zeros((4,wlen), dtype=np.uint8)
|
DIBIT = np.zeros((4,wlen), dtype=np.uint8)
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
DIBIT[i][:] = np.concatenate([WALSH[i][:] for _ in range(wlen//4)])
|
DIBIT[i][:] = np.concatenate([WALSH4[i][:] for _ in range(wlen//4)])
|
||||||
## ---- preamble symbols ---------------------------------------------------------
|
## ---- preamble symbols ---------------------------------------------------------
|
||||||
SYNC_SYMB = common.n_psk(2, np.concatenate([DIBIT[i][:]
|
SYNC_SYMB = common.n_psk(2, np.concatenate([DIBIT[i][:]
|
||||||
for i in FIXED_WALSH_SYMBOLS]))
|
for i in FIXED_WALSH_SYMBOLS]))
|
||||||
CNT_SYMB = np.zeros(4*wlen, dtype=np.complex64)
|
CNT_SYMB = np.zeros(4*wlen, dtype=np.complex64)
|
||||||
WID_SYMB = np.zeros(5*wlen, dtype=np.complex64)
|
WID_SYMB = np.zeros(5*wlen, dtype=np.complex64)
|
||||||
## ---- preamble scramble symbols ------------------------------------------------
|
## ---- preamble scramble symbols ------------------------------------------------
|
||||||
SYNC_SCR = common.n_psk(8, np.concatenate([FIXED_PN for _ in range(9)]))[:len(SYNC_SYMB)]
|
SYNC_SCR = common.n_psk(8, np.concatenate([FIXED_PN[:wlen] for _ in range(9)]))
|
||||||
CNT_SCR = common.n_psk(8, np.concatenate([CNT_PN for _ in range(4)]))[:len(CNT_SYMB)]
|
CNT_SCR = common.n_psk(8, np.concatenate([CNT_PN[:wlen] for _ in range(4)]))
|
||||||
WID_SCR = common.n_psk(8, np.concatenate([WID_PN for _ in range(5)]))[:len(WID_SYMB)]
|
WID_SCR = common.n_psk(8, np.concatenate([WID_PN[:wlen] for _ in range(5)]))
|
||||||
|
|
||||||
self._fixed_s = common.make_scr(SYNC_SCR*SYNC_SYMB,
|
self._fixed_s = common.make_scr(SYNC_SCR*SYNC_SYMB,
|
||||||
SYNC_SCR)
|
SYNC_SCR)
|
||||||
|
print('FIXED: ', np.round(np.mod(np.angle(self._fixed_s['symb'])/np.pi*4, 8)))
|
||||||
self._cnt_s = common.make_scr(CNT_SCR*CNT_SYMB,
|
self._cnt_s = common.make_scr(CNT_SCR*CNT_SYMB,
|
||||||
CNT_SCR)
|
CNT_SCR)
|
||||||
self._wid_s = common.make_scr(WID_SCR*WID_SYMB,
|
self._wid_s = common.make_scr(WID_SCR*WID_SYMB,
|
||||||
WID_SCR)
|
WID_SCR)
|
||||||
|
self._walsh_s = np.zeros(2048, dtype=common.SYMB_SCRAMBLE_DTYPE)
|
||||||
|
scr_walsh = ScrambleWalsh()
|
||||||
|
self._walsh_s['scramble'] = common.n_psk(8, np.array([scr_walsh.next() for _ in range(2048)], dtype=np.uint8))
|
||||||
|
|
||||||
def decode_soft_dec(self, soft_dec):
|
def decode_soft_dec(self, soft_dec):
|
||||||
print('decode_soft_dec', len(soft_dec), soft_dec.dtype)
|
print('decode_soft_dec', len(soft_dec), soft_dec.dtype)
|
||||||
is_full = self._deinterleaver.load(soft_dec)
|
interleaver_is_full = False
|
||||||
if not is_full:
|
if self._wid == MODE_WALSH: ## TODO
|
||||||
|
n = len(soft_dec) // 32
|
||||||
|
soft_bits = np.zeros(2*n, dtype=np.float32)
|
||||||
|
for i in range(n):
|
||||||
|
w = np.sum(soft_dec[32*i:32*(i+1)].reshape(4,8),0)
|
||||||
|
b = FROM_WALSH4[np.packbits(w[0:4]>0)[0]]
|
||||||
|
print('WALSH', i, w, b)
|
||||||
|
abs_soft_dec = np.mean(np.abs(w))
|
||||||
|
soft_bits[2*i] = abs_soft_dec*(2*(b>>1)-1)
|
||||||
|
soft_bits[2*i+1] = abs_soft_dec*(2*(b &1)-1)
|
||||||
|
#print('WALSH soft_bits=', soft_bits)
|
||||||
|
interleaver_is_full = self._deinterleaver.load(soft_bits)
|
||||||
|
else:
|
||||||
|
interleaver_is_full = self._deinterleaver.load(soft_dec)
|
||||||
|
if not interleaver_is_full:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
r = self._deinterleaver.fetch()
|
r = self._deinterleaver.fetch()
|
||||||
rd = self._depuncturer.process(r)
|
rd = self._depuncturer.process(r)
|
||||||
self._viterbi_decoder.reset()
|
self._viterbi_decoder.reset()
|
||||||
decoded_bits = self._viterbi_decoder.udpate(rd)
|
decoded_bits = np.roll(self._viterbi_decoder.udpate(rd), 7)
|
||||||
print('quality={}% num_bits={}'.format(100.0*self._viterbi_decoder.quality()/(2*len(decoded_bits)),
|
print('quality={}% num_bits={}'.format(100.0*self._viterbi_decoder.quality()/(2*len(decoded_bits)),
|
||||||
len(decoded_bits)))
|
len(decoded_bits)))
|
||||||
return decoded_bits
|
return decoded_bits
|
||||||
|
@ -901,7 +991,7 @@ class PhysicalLayer(object):
|
||||||
|
|
||||||
def get_preamble_z(self):
|
def get_preamble_z(self):
|
||||||
"""preamble symbols for preamble correlation"""
|
"""preamble symbols for preamble correlation"""
|
||||||
return 2,np.array([z for z in self._fixed_s['symb'][0:3*self._wlen]
|
return 2,np.array([z for z in self._fixed_s['symb']
|
||||||
for _ in range(self._sps)])
|
for _ in range(self._sps)])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -913,3 +1003,7 @@ if __name__ == '__main__':
|
||||||
# print(np.real(make_mp(24,13,0)))
|
# print(np.real(make_mp(24,13,0)))
|
||||||
# print(np.real(make_mp(24,13,6)))
|
# print(np.real(make_mp(24,13,6)))
|
||||||
print(mp_base(196))
|
print(mp_base(196))
|
||||||
|
s = ScrambleWalsh()
|
||||||
|
x = [s.next() for _ in range(32)]
|
||||||
|
print(x)
|
||||||
|
print(x == [5, 6, 2, 1, 7, 3, 1, 1, 6, 0, 5, 4, 0, 7, 7, 0, 5, 3, 1, 3, 3, 2, 2, 5, 5, 4, 7, 3, 5, 4, 3, 0])
|
||||||
|
|
Loading…
Reference in a new issue