## -*- python -*- from __future__ import print_function import numpy as np import common ## ---- 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), [0,1,3,2,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, +0.500000-0.866025j, 0.000000-1.000000j, 0.866025-0.500000j, 0.258819-0.258819j, -0.866025-0.500000j, -0.500000-0.866025j, -1.000000+0.000000j, -0.258819-0.258819j], range(16)), common.CONST_DTYPE) QAM32=np.array( zip([+0.866380+0.499386j, 0.984849+0.173415j, 0.499386+0.866380j, 0.173415+0.984849j, +0.520246+0.520246j, 0.520246+0.173415j, 0.173415+0.520246j, 0.173415+0.173415j, -0.866380+0.499386j, -0.984849+0.173415j, -0.499386+0.866380j, -0.173415+0.984849j, -0.520246+0.520246j, -0.520246+0.173415j, -0.173415+0.520246j, -0.173415+0.173415j, +0.866380-0.499386j, 0.984849-0.173415j, 0.499386-0.866380j, 0.173415-0.984849j, +0.520246-0.520246j, 0.520246-0.173415j, 0.173415-0.520246j, 0.173415-0.173415j, -0.866380-0.499386j, -0.984849-0.173415j, -0.499386-0.866380j, -0.173415-0.984849j, -0.520246-0.520246j, -0.520246-0.173415j, -0.173415-0.520246j, -0.173415-0.173415j], range(32)), common.CONST_DTYPE) QAM64=np.array( zip([+1.000000+0.000000j, 0.822878+0.568218j, 0.821137+0.152996j, 0.932897+0.360142j, +0.000000-1.000000j, 0.822878-0.568218j, 0.821137-0.152996j, 0.932897-0.360142j, +0.568218+0.822878j, 0.588429+0.588429j, 0.588429+0.117686j, 0.588429+0.353057j, +0.568218-0.822878j, 0.588429-0.588429j, 0.588429-0.117686j, 0.588429-0.353057j, +0.152996+0.821137j, 0.117686+0.588429j, 0.117686+0.117686j, 0.117686+0.353057j, +0.152996-0.821137j, 0.117686-0.588429j, 0.117686-0.117686j, 0.117686-0.353057j, +0.360142+0.932897j, 0.353057+0.588429j, 0.353057+0.117686j, 0.353057+0.353057j, +0.360142-0.932897j, 0.353057-0.588429j, 0.353057-0.117686j, 0.353057-0.353057j, +0.000000+1.000000j, -0.822878+0.568218j, -0.821137+0.152996j, -0.932897+0.360142j, -1.000000+0.000000j, -0.822878-0.568218j, -0.821137-0.152996j, -0.932897-0.360142j, -0.568218+0.822878j, -0.588429+0.588429j, -0.588429+0.117686j, -0.588429+0.353057j, -0.568218-0.822878j, -0.588429-0.588429j, -0.588429-0.117686j, -0.588429-0.353057j, -0.152996+0.821137j, -0.117686+0.588429j, -0.117686+0.117686j, -0.117686+0.353057j, -0.152996-0.821137j, -0.117686-0.588429j, -0.117686-0.117686j, -0.117686-0.353057j, -0.360142+0.932897j, -0.353057+0.588429j, -0.353057+0.117686j, -0.353057+0.353057j, -0.360142-0.932897j, -0.353057-0.588429j, -0.353057-0.117686j, -0.353057-0.353057j], range(64)), common.CONST_DTYPE) ## for test #QAM64 = QAM64[(7,3,24,56,35,39,60,28),] #QAM64['symbols'] = [1, 0, 2, 6, 4, 5, 7, 3] ## ---- constellation indices --------------------------------------------------- MODE_BPSK = 0 MODE_QPSK = 1 MODE_8PSK = 2 MODE_16QAM = 3 MODE_32QAM = 4 MODE_64QAM = 5 ## ---- data scrambler ----------------------------------------------------------- class ScrambleData(object): """data scrambling sequence generator""" def __init__(self): self.reset() def reset(self): self._state = np.array([0,0,0,0,0,0,0,0,1], dtype=np.bool) self._taps = np.array([0,0,0,0,1,0,0,0,1], dtype=np.bool) def next(self, num_bits): r = np.packbits(self._state[1:])[0]&((1<= 0: ## ---- data frames got_reinserted_preamble = self._frame_counter == 0 self._frame_counter += 1 if got_reinserted_preamble: success = self.decode_reinserted_preamble(symbols) else: success = self.get_data_frame_quality(symbols) return [self.make_data_frame(success),self._constellation_index,success,not got_reinserted_preamble] def get_doppler(self, iq_samples): """quality check and doppler estimation for preamble""" success,doppler = True,0 if len(iq_samples) != 0: sps = self._sps m = 23*sps idx = np.arange(m) idx2 = np.arange(m+23*sps) _,zp = self.get_preamble_z() n = len(zp) cc = np.correlate(iq_samples, zp) imax = np.argmax(np.abs(cc[0:23*sps])) print('imax=', imax, len(iq_samples)) pks = [np.correlate(iq_samples[imax+i*m+idx], zp[i*m+idx])[0] for i in range(n//m)] val = [np.mean(np.abs(np.correlate(iq_samples[imax+i*m+idx2], zp[i*m+idx])[11*sps+np.arange(-2*sps,2*sps)])) for i in range((n//m)-1)] tests = np.abs(pks[0:-1])/val success = np.median(tests) > 2.0 print('test:', np.abs(pks), tests) if success: print('doppler apks', np.abs(pks)) print('doppler ppks', np.angle(pks), np.diff(np.unwrap(np.angle(pks)))/m, np.mean(np.diff(np.unwrap(np.angle(pks)))/m)) doppler = common.freq_est(pks)/m; print('success=', success, 'doppler=', doppler) return success,doppler def set_mode(self, mode): pass def get_preamble_quality(self, symbols): print('get_preamble_quality', np.abs(np.mean(symbols[-32:])), symbols[-32:]) return np.abs(np.mean(symbols[-32:])) > 0.5 def get_data_frame_quality(self, symbols): print('get_data_frame_quality', symbols[-31:]) return np.abs(np.mean(symbols[-31:])) > 0.5 def decode_reinserted_preamble(self, symbols): ## decode D0,D1,D2 z = np.array([np.mean(symbols[-71+i*13:-71+(i+1)*13]) for i in range(3)]) print('decode_reinserted_preamble', symbols[0:-71], symbols[-71:-71+3*13], symbols[-71+4*13:], z) d0d1d2 = map(np.uint8, np.mod(np.round(np.angle(z)/np.pi*2),4)) dibits = [TO_DIBIT[idx] for idx in d0d1d2] self._mode = {'rate': tuple([x[0] for x in dibits]), 'interleaver': tuple([x[1] for x in dibits])} print('======== rate,interleaver:', TO_RATE[self._mode['rate']], TO_INTERLEAVER[self._mode['interleaver']]) self._interleaver_length = TO_INTERLEAVER[self._mode['interleaver']]['frames'] rate_info = TO_RATE[self._mode['rate']] print('rate_info', rate_info) self._constellation_index = rate_info['ci'] print('constellation index', self._constellation_index) scr = ScrambleData() iscr = [scr.next(rate_info['bits_per_symbol']) for _ in range(256)] if rate_info['ci'] > MODE_8PSK: self._data_scramble = np.ones(256, dtype=np.complex64) else: constell = self._constellations[rate_info['ci']] self._data_scramble = constell[iscr]['points'] success = True ## TODO return success def make_reinserted_preamble(self, offset, success): """ offset= 0 -> 1st reinserted preamble offset=-72 -> all following reinserted preambles""" a=np.array(zip(REINSERTED_PREAMBLE[offset:], REINSERTED_PREAMBLE[offset:]), common.SYMB_SCRAMBLE_DTYPE) a['symb'][-71:-71+3*13] = 0 ## D0,D1,D2 print('make_reinserted_preamble', offset, success, len(a['symb']), a['symb'], a['scramble']) if not success: self._frame_counter = -1 return a def make_data_frame(self, success): self._preamble_offset = -72 ## all following reinserted preambles start at index -72 a = np.zeros(256+31, common.SYMB_SCRAMBLE_DTYPE) a['scramble'][:256] = self._data_scramble n = (self._frame_counter-1)%72 if self._frame_counter == 72: self._frame_counter = -1 m = n%18 if m == 0: cnt = n//18 self._mp = (1,1,1,1,1,1,1,0)+self._mode['rate']+self._mode['interleaver']+MP_COUNTER[cnt]+(0,) print('new mini-probe signs n=',n,'m=',m, 'cnt=',cnt, self._mp) print('make_data_frame', m, self._mp[m]) a['symb'][256:] = MINI_PROBE[self._mp[m]] a['scramble'][256:] = MINI_PROBE[self._mp[m]] if not success: self._frame_counter = -1 return a def decode_soft_dec(self, soft_dec): return soft_dec @staticmethod def get_preamble(): """preamble symbols + scrambler""" return np.array(zip(PREAMBLE, PREAMBLE), common.SYMB_SCRAMBLE_DTYPE) def get_preamble_z(self): """preamble symbols for preamble correlation""" return 2,np.array([z for z in PREAMBLE for _ in range(self._sps)]) if __name__ == '__main__': print(PREAMBLE) z = common.n_psk(8,PREAMBLE) cc = [np.sum(z[0:23]*np.conj(z[23*i:23*i+23])) for i in range(6)] print(np.abs(cc)) print(np.angle(cc)/np.pi*4) print(all(z==PhysicalLayer.get_preamble()['symb'])) print(len(PhysicalLayer.get_preamble()['symb'])) s = ScrambleData() print([s.next(1) for _ in range(511)]) print([s.next(1) for _ in range(511)] == [s.next(1) for _ in range(511)]) #print(QAM64) #print(QAM32) #print(QAM16) #print(PSK8) #print(QPSK) #print(BPSK) #print(MINI_PROBE_PLUS) #print(MINI_PROBE_MINUS) #print(MINI_PROBE_PLUS*MINI_PROBE_MINUS) #for i in range(len(QAM64)): # print(QAM64['points'][i]) print([s.next(6) for _ in range(256)])