#!/usr/bin/env python import os import numpy as np import cmath import collections import itertools import array import math from cStringIO import StringIO import commpy.channelcoding.convcode as cc from wltrace import dot11 LONG_PREAMBLE_TXT =\ """ 0 -0.078 0.000 40 0.098 -0.026 80 0.062 0.062 120 -0.035 -0.151 1 0.012 -0.098 41 0.053 0.004 81 0.119 0.004 121 -0.122 -0.017 2 0.092 -0.106 42 0.001 -0.115 82 -0.022 -0.161 122 -0.127 -0.021 3 -0.092 -0.115 43 -0.137 -0.047 83 0.059 0.015 123 0.075 -0.074 4 -0.003 -0.054 44 0.024 -0.059 84 0.024 0.059 124 -0.003 0.054 5 0.075 0.074 45 0.059 -0.015 85 -0.137 0.047 125 -0.092 0.115 6 -0.127 0.021 46 -0.022 0.161 86 0.001 0.115 126 0.092 0.106 7 -0.122 0.017 47 0.119 -0.004 87 0.053 -0.004 127 0.012 0.098 8 -0.035 0.151 48 0.062 -0.062 88 0.098 0.026 128 -0.156 0.000 9 -0.056 0.022 49 0.037 0.098 89 -0.038 0.106 129 0.012 -0.098 10 -0.060 -0.081 50 -0.057 0.039 90 -0.115 0.055 130 0.092 -0.106 11 0.070 -0.014 51 -0.131 0.065 91 0.060 0.088 131 -0.092 -0.115 12 0.082 -0.092 52 0.082 0.092 92 0.021 -0.028 132 -0.003 -0.054 13 -0.131 -0.065 53 0.070 0.014 93 0.097 -0.083 133 0.075 0.074 14 -0.057 -0.039 54 -0.060 0.081 94 0.040 0.111 134 -0.127 0.021 15 0.037 -0.098 55 -0.056 -0.022 95 -0.005 0.120 135 -0.122 0.017 16 0.062 0.062 56 -0.035 -0.151 96 0.156 0.000 136 -0.035 0.151 17 0.119 0.004 57 -0.122 -0.017 97 -0.005 -0.120 137 -0.056 0.022 18 -0.022 -0.161 58 -0.127 -0.021 98 0.040 -0.111 138 -0.060 -0.081 19 0.059 0.015 59 0.075 -0.074 99 0.097 0.083 139 0.070 -0.014 20 0.024 0.059 60 -0.003 0.054 100 0.021 0.028 140 0.082 -0.092 21 -0.137 0.047 61 -0.092 0.115 101 0.060 -0.088 141 -0.131 -0.065 22 0.001 0.115 62 0.092 0.106 102 -0.115 -0.055 142 -0.057 -0.039 23 0.053 -0.004 63 0.012 0.098 103 -0.038 -0.106 143 0.037 -0.098 24 0.098 0.026 64 -0.156 0.000 104 0.098 -0.026 144 0.062 0.062 25 -0.038 0.106 65 0.012 -0.098 105 0.053 0.004 145 0.119 0.004 26 -0.115 0.055 66 0.092 -0.106 106 0.001 -0.115 146 -0.022 -0.161 27 0.060 0.088 67 -0.092 -0.115 107 -0.137 -0.047 147 0.059 0.015 28 0.021 -0.028 68 -0.003 -0.054 108 0.024 -0.059 148 0.024 0.059 29 0.097 -0.083 69 0.075 0.074 109 0.059 -0.015 149 -0.137 0.047 30 0.040 0.111 70 -0.127 0.021 110 -0.022 0.161 150 0.001 0.115 31 -0.005 0.120 71 -0.122 0.017 111 0.119 -0.004 151 0.053 -0.004 32 0.156 0.000 72 -0.035 0.151 112 0.062 -0.062 152 0.098 0.026 33 -0.005 -0.120 73 -0.056 0.022 113 0.037 0.098 153 -0.038 0.106 34 0.040 -0.111 74 -0.060 -0.081 114 -0.057 0.039 154 -0.115 0.055 35 0.097 0.083 75 0.070 -0.014 115 -0.131 0.065 155 0.060 0.088 36 0.021 0.028 76 0.082 -0.092 116 0.082 0.092 156 0.021 -0.028 37 0.060 -0.088 77 -0.131 -0.065 117 0.070 0.014 157 0.097 -0.083 38 -0.115 -0.055 78 -0.057 -0.039 118 -0.060 0.081 158 0.040 0.111 39 -0.038 -0.106 79 0.037 -0.098 119 -0.056 -0.022 159 -0.005 0.120 """ SHORT_PREAMBLE_TXT =\ """ 16 0.046 0.046 56 0.046 0.046 96 0.046 0.046 136 0.046 0.046 17 -0.132 0.002 57 0.002 -0.132 97 -0.132 0.002 137 0.002 -0.132 18 -0.013 -0.079 58 -0.079 -0.013 98 -0.013 -0.079 138 -0.079 -0.013 19 0.143 -0.013 59 -0.013 0.143 99 0.143 -0.013 139 -0.013 0.143 20 0.092 0.000 60 0.000 0.092 100 0.092 0.000 140 0.000 0.092 21 0.143 -0.013 61 -0.013 0.143 101 0.143 -0.013 141 -0.013 0.143 22 -0.013 -0.079 62 -0.079 -0.013 102 -0.013 -0.079 142 -0.079 -0.013 23 -0.132 0.002 63 0.002 -0.132 103 -0.132 0.002 143 0.002 -0.132 24 0.046 0.046 64 0.046 0.046 104 0.046 0.046 144 0.046 0.046 25 0.002 -0.132 65 -0.132 0.002 105 0.002 -0.132 145 -0.132 0.002 26 -0.079 -0.013 66 -0.013 -0.079 106 -0.079 -0.013 146 -0.013 -0.079 27 -0.013 0.143 67 0.143 -0.013 107 -0.013 0.143 147 0.143 -0.013 28 0.000 0.092 68 0.092 0.000 108 0.000 0.092 148 0.092 0.000 29 -0.013 0.143 69 0.143 -0.013 109 -0.013 0.143 149 0.143 -0.013 30 -0.079 -0.013 70 -0.013 -0.079 110 -0.079 -0.013 150 -0.013 -0.079 31 0.002 -0.132 """ LONG_PREAMBLE = dict() for idx, i, q in zip(*(iter(LONG_PREAMBLE_TXT.split()),)*3): LONG_PREAMBLE[int(idx)] = complex(float(i), float(q)) LONG_PREAMBLE = [LONG_PREAMBLE[i] for i in sorted(LONG_PREAMBLE.keys())] SHORT_PREAMBLE = dict() for idx, i, q in zip(*(iter(SHORT_PREAMBLE_TXT.split()),)*3): idx = int(idx) if idx >= 16 and idx <= 31: SHORT_PREAMBLE[idx-16] = complex(float(i), float(q)) SHORT_PREAMBLE = [SHORT_PREAMBLE[i] for i in sorted(SHORT_PREAMBLE.keys())] # Table 78 - Rate-dependent parameters RATE_PARAMETERS = { # rate : (n_bpsc, n_cbps, n_dbps), 6: (1, 48, 24), 9: (1, 48, 36), 12: (2, 96, 48), 18: (2, 96, 72), 24: (4, 192, 96), 36: (4, 192, 144), 48: (6, 288, 192), 54: (6, 288, 216), } HT_MCS_PARAMETERS = { 0: (1, 52, 26), 1: (2, 104, 52), 2: (2, 104, 78), 3: (4, 208, 104), 4: (4, 208, 156), 5: (6, 312, 208), 6: (6, 312, 234), 7: (6, 312, 260), } PILOT_SUBCARRIES = [-21, -7, 7, 21] PHASE_SCALE = 256 # 802.11a/g SUBCARRIERS = range(-26, 0) + range(1, 27) FFT_MAPPING = dict((c, c if c > 0 else 64 + c) for c in SUBCARRIERS) LTS_REF = dict( zip(SUBCARRIERS, [1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1])) polarity = [1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1] RATE_BITS = { '1101': 6, '1111': 9, '0101': 12, '0111': 18, '1001': 24, '1011': 36, '0001': 48, '0011': 54, } # 802.11n HT_SUBCARRIERS = range(-28, 0) + range(1, 29) HT_FFT_MAPPING = dict((c, c if c > 0 else 64 + c) for c in HT_SUBCARRIERS) # append 1 for negative sub carriers, -1 for positive sub carriers HT_LTS_REF = dict( zip(HT_SUBCARRIERS, [1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1])) def to_hex(n, n_bits): return hex((n + (1< (%d, %d) phase: %f (diff: %d)' %\ (i, int(self.samples[192+i].real), int(self.samples[192+i].imag), int(self.lts_samples[i].real), int(self.lts_samples[i].imag), cmath.phase(self.lts_samples[i]), int((cmath.phase(self.lts_samples[i])-cmath.phase(samples[192+i]))*PHASE_SCALE), ) self.gain = dict((c, (lts1[c]+lts2[c])/2*LTS_REF[c]) for c in self.subcarriers) self.idx = 0 self.polarity = itertools.cycle(polarity) def next_symbol(self): if self.short_gi: symbol = self.do_fft(self.data_samples[self.idx+8:self.idx+72]) self.idx += 72 else: symbol = self.do_fft(self.data_samples[self.idx+16:self.idx+80]) self.idx += 80 # remove randomness of pilot carriers p = self.polarity.next() if not self.ht: polarity = [p, p, p, -p] else: polarity = [p*p1 for p1 in self.ht_polarity] self.ht_polarity.rotate(-1) # print '[POLARITY] %d: %s' % (p, polarity) for c, p in zip(PILOT_SUBCARRIES, polarity): symbol[c] *= p # Correct Residual Carrier Frequency Offset prod_sum = 0 for c in PILOT_SUBCARRIES: prod = symbol[c].conjugate()*self.gain[c] prod_sum += prod beta = cmath.phase(prod_sum) print "[PILOT OFFSET] %f (%d)" % (beta, int(beta*PHASE_SCALE)) carriers = [] for c in self.subcarriers: if c in PILOT_SUBCARRIES: continue symbol[c] *= cmath.exp(complex(0, beta)) symbol[c] /= self.gain[c] carriers.append(symbol[c]) return carriers def switch_ht(self): """ Switch to HT channel estimator. """ self.ht = True self.subcarriers = HT_SUBCARRIERS self.fft_mapping = HT_FFT_MAPPING ht_sts = self.data_samples[self.idx:self.idx+80] self.idx += 80 ht_offset = cmath.phase(sum([ht_sts[i].conjugate()*ht_sts[i+16] for i in range(len(ht_sts)-16)]))/16 print '[HT OFFSET] %f (%d)' % (ht_offset, int(ht_offset*PHASE_SCALE)) ht_offset = 0 self.data_samples = [c*cmath.exp(complex(0, -n*ht_offset)) for n, c in enumerate(self.data_samples[self.idx:])] self.idx = 0 ht_lts = self.do_fft(self.data_samples[self.idx+16:self.idx+80]) self.idx += 80 self.gain = dict((c, ht_lts[c]*HT_LTS_REF[c]) for c in self.subcarriers) self.ht_polarity = collections.deque([1, 1, 1, -1]) def do_fft(self, samples): assert len(samples) == 64, len(samples) freq = np.fft.fft(samples) return dict((c, freq[self.fft_mapping[c]]) for c in self.subcarriers) def fix_freq_offset(self): sts = self.samples[80:160] lts = self.samples[160+32:160+160] coarse_offset = cmath.phase(sum([sts[i]*sts[i+16].conjugate() for i in range(len(sts)-16)]))/16 print '[COARSE OFFSET] %f (%d)' % (coarse_offset, int(coarse_offset*PHASE_SCALE)) # coarse_offset = 0 # coarse correction lts = [c*cmath.exp(complex(0, n*coarse_offset)) for n, c in enumerate(lts)] fine_offset = cmath.phase(sum([lts[i]*lts[i+64].conjugate() for i in range(len(lts)-64)]))/64 print '[FINE OFFSET] %f (%d)' % (fine_offset, int(fine_offset*PHASE_SCALE)) fine_offset = 0 self.lts_samples = [c*cmath.exp(complex(0, n*fine_offset)) for n, c in enumerate(lts)] self.freq_offset = coarse_offset + fine_offset print '[FREQ OFFSET] %f (%d)' % (self.freq_offset, int(self.freq_offset*PHASE_SCALE)) self.data_samples = [c*cmath.exp(complex(0, n*self.freq_offset)) for n, c in enumerate(self.samples[320:], start=128)] class Demodulator(object): QAM16_MAPPING = { 0b00: -3, 0b01: -1, 0b11: 1, 0b10: 3, } QAM64_MAPPING = { 0b000: -7, 0b001: -5, 0b011: -3, 0b010: -1, 0b110: 1, 0b111: 3, 0b101: 5, 0b100: 7, } def __init__(self, rate=6, mcs=0, ht=False): if (not ht and rate in [6, 9]) or (ht and mcs == 0): # BPSK self.scale = 1 self.bits_per_sym = 1 self.cons_points = np.array([complex(-1, 0), complex(1, 0)]) elif (not ht and rate in [12, 18]) or (ht and mcs in [1, 2]): # QPSK self.scale = 1 self.bits_per_sym = 2 self.cons_points = np.array( [complex(-1, -1), complex(-1, 1), complex(1, -1), complex(1, 1)] ) elif (not ht and rate in [24, 36]) or (ht and mcs in [3, 4]): # 16-QAM self.scale = 3 self.bits_per_sym = 4 self.cons_points = np.array( [complex(self.QAM16_MAPPING[i >> 2], self.QAM16_MAPPING[i & 0b11]) for i in range(16)]) elif (not ht and rate in [48, 54]) or (ht and mcs in [5, 6, 7]): # 64-QAM self.scale = 7 self.bits_per_sym = 6 self.cons_points = np.array( [complex(self.QAM64_MAPPING[i >> 3], self.QAM64_MAPPING[i & 0b111]) for i in range(64)]) def demodulate(self, carriers): bits = [] for sym in carriers: idx = np.argmin(abs(sym*self.scale - self.cons_points)) bits.extend([int(b) for b in ('{0:0%db}' % (self.bits_per_sym)).format(idx)]) return bits class Signal(object): def __init__(self, bits): assert len(bits) == 24 str_bits = ''.join([str(b) for b in bits]) self.rate_bits = str_bits[:4] self.rsvd = str_bits[4] self.len_bits = str_bits[5:17] self.parity_bits = str_bits[17] self.tail_bits = str_bits[18:] self.mcs = 0 self.rate = RATE_BITS.get(self.rate_bits, 0) self.length = int(self.len_bits[::-1], 2) self.parity_ok = sum(bits[:18]) % 2 == 0 self.ht = False self.mcs = 0 class HTSignal(object): def __init__(self, bits): assert len(bits) == 48 str_bits = ''.join([str(b) for b in bits]) self.mcs_bits = str_bits[:6] self.cbw = str_bits[7] self.len_bits = str_bits[8:24] self.smoothing = str_bits[24] self.not_sounding = str_bits[25] self.rsvd = str_bits[26] self.aggregation = str_bits[27] self.stbc = str_bits[28:30] self.fec = str_bits[30] self.short_gi = str_bits[31] self.num_ext_stream = str_bits[32:34] self.crc = str_bits[34:42] self.tail_bits = str_bits[42:48] self.mcs = int(self.mcs_bits[::-1], 2) try: self.rate = dot11.mcs_to_rate(self.mcs) except: self.rate = 0 self.length = int(self.len_bits[::-1], 2) self.expected_crc = ''.join(['%d' % c for c in self.calc_crc(bits[:34])]) self.crc_ok = self.expected_crc == self.crc self.ht = True def calc_crc(self, bits): c = [1] * 8 for b in bits: next_c = [0] * 8 next_c[0] = b ^ c[7] next_c[1] = b ^ c[7] ^ c[0] next_c[2] = b ^ c[7] ^ c[1] next_c[3] = c[2] next_c[4] = c[3] next_c[5] = c[4] next_c[6] = c[5] next_c[7] = c[6] c = next_c return [1-b for b in c[::-1]] class Decoder(object): def __init__(self, path, power_thres=200, skip=0, window=80): if path is not None: self.fh = open(path, 'rb') size = os.path.getsize(path) if skip*4 < size: self.fh.seek(skip*4) else: print "[WARN] try to seek beyond end of file %d/%d" % (skip*4, size) self.power_thres = 200 self.window = window self.count = skip def decode_next(self, *args, **kwargs): trigger = False samples = [] glbl_index = 0 while True: chunk = array.array('h', self.fh.read(self.window*4)) chunk = [complex(i, q) for i, q in zip(chunk[::2], chunk[1::2])] if not trigger and any([abs(c) > self.power_thres for c in chunk]): trigger = True samples = [] print "Power trigger at %d" % (self.count) glbl_index = self.count self.count += self.window if trigger: samples.extend(chunk) if trigger and all([abs(c) < self.power_thres for c in chunk]): start = self.find_pkt(samples) if start is None: trigger = False else: print "Decoding packet starting from sample %d" %\ (glbl_index + start) return (glbl_index, ) +\ self.decode(samples[start:], *args, **kwargs) def find_pkt(self, samples): """ Returns the index of the first sample of STS None if no packet was detected. """ lts = LONG_PREAMBLE[-64:] peaks = np.array([abs(c) for c in np.correlate( samples, lts, mode='valid')]).argsort()[-2:] if (max(peaks) - min(peaks) == 64) and (min(peaks) - 32 - 160) > 0: return min(peaks) - 32 - 160 else: return None def demodulate(self, carriers, signal=None): if signal is None: demod = Demodulator(rate=6) else: demod = Demodulator(signal.rate, signal.mcs, signal.ht) return demod.demodulate(carriers) def deinterleave(self, in_bits, rate=6, mcs=0, ht=False, verbose=False): if not ht: n_bpsc, n_cbps, n_dbps = RATE_PARAMETERS[rate] n_col = 16 n_row = 3 * n_bpsc else: n_bpsc, n_cbps, n_dbps = HT_MCS_PARAMETERS[mcs] n_col = 13 n_row = 4 * n_bpsc s = max(n_bpsc/2, 1) first_perm = dict() for j in range(0, n_cbps): first_perm[j] = (s * (j/s)) + ((j + n_col*j/n_cbps) % s) second_perm = dict() for i in range(0, n_cbps): second_perm[i] = n_col*i - (n_cbps-1)*(i/n_row) if verbose: print "Bit rate: %f Mb/s" % (rate) print "Bits per sub carrier: %d" % (n_bpsc) print "Coded bits per symbol: %d" % (n_cbps) print "Data bits per symbol %d" % (n_dbps) print "S = %d" % (s) print "====== Overall permutation =========" for j in range(0, n_cbps): print '%d -> %d -> %d' % (j, first_perm[j], second_perm[first_perm[j]]) if in_bits is None: idx_map = [] for k in range(n_cbps): idx_map.append((second_perm[first_perm[k]], k)) return sorted(idx_map) if verbose: print '%d bits, %f samples' % (len(in_bits), float(len(in_bits))/n_cbps) out_bits = [0]*len(in_bits) for n in range(len(in_bits)/n_cbps): for j in range(n_cbps): base = n*n_cbps out_bits[base+second_perm[first_perm[j]]] = in_bits[base+j] return out_bits def viterbi_decode(self, bits, signal=None): if signal is None: ht = False rate = 6 else: ht, rate, mcs = signal.ht, signal.rate, signal.mcs if (not ht and rate in [9, 18, 36, 54]) or (ht and mcs in [2, 4, 6]): print '[PUNCTURE] 3/4' # 3/4 new_bits = [] for i in range(0, len(bits), 12): new_bits.extend(bits[i:i+3]) new_bits.extend([2, 2]) new_bits.extend(bits[i+3:i+7]) new_bits.extend([2, 2]) new_bits.extend(bits[i+7:i+11]) new_bits.extend([2, 2]) new_bits.extend(bits[i+11:i+12]) bits = new_bits elif (not ht and rate in [48]) or (ht and mcs in [5]): print '[PUNCTURE] 2/3' # 2/3 new_bits = [] for i in range(0, len(bits), 9): new_bits.extend(bits[i:i+3]) new_bits.append(2) new_bits.extend(bits[i+3:i+6]) new_bits.append(2) new_bits.extend(bits[i+6:i+9]) new_bits.append(2) bits = new_bits elif ht and mcs in [7]: print '[PUNCTURE] 5/6' # 5/6 new_bits = [] for i in range(0, len(bits), 6): new_bits.extend(bits[i:i+3]) new_bits.extend([2, 2]) new_bits.extend(bits[i+3:i+5]) new_bits.extend([2, 2]) new_bits.append(bits[i+5]) bits = new_bits else: print '[NO PUNCTURE]' extended_bits = np.array([0]*2 + bits + [0]*12) trellis = cc.Trellis(np.array([7]), np.array([[0133, 0171]])) return list(cc.viterbi_decode(extended_bits, trellis, tb_depth=35))[:-7] def descramble(self, bits): X = [0]*7 X[0] = bits[2] ^ bits[6] X[1] = bits[1] ^ bits[5] X[2] = bits[0] ^ bits[4] X[3] = X[0] ^ bits[3] X[4] = X[1] ^ bits[2] X[5] = X[2] ^ bits[1] X[6] = X[3] ^ bits[0] out_bits = [] for i, b in enumerate(bits): feedback = X[6] ^ X[3] out_bits.append(feedback ^ b) X = [feedback] + X[:-1] return out_bits def check_signal(self, signal): if signal.rate_bits not in RATE_BITS: print '[SIGNAL] invalid rate: %s' % (signal.rate_bits) return False if signal.rsvd != '0': print '[SIGNAL] wrong rsvd' return False if not signal.parity_ok: print '[SIGNAL] wrong parity' return False if not all([b == '0' for b in signal.tail_bits]): print '[SIGNAL] wrong tail: %s' % (signal.tail_bits) return False return True def check_ht_signal(self, signal): if signal.mcs > 7: print '[HT SIGNAL] mcs not supported: %d' % (signal.mcs) return False if signal.cbw != '0': print '[HT SIGNAL] CBW not supported' return False if signal.rsvd != '1': print '[HT SIGNAL] wrong rsvd' return False if signal.stbc != '00': print '[HT SIGNAL] stbc not supported: %s' % (signal.stbc) return False if signal.fec != '0': print '[HT SIGNAL] FEC not supported' return False if signal.num_ext_stream != '00': print '[HT SIGNAL] EXT spatial stream not supported: %s' % (signal.num_ext_stream) return False if not all([b == '0' for b in signal.tail_bits]): print '[HT SIGNAL] wrong tail: %s' % (signal.tail_bits) return False return True def decode(self, samples, *args, **kwargs): eq = ChannelEstimator(samples) carriers = eq.next_symbol() assert len(carriers) == 48 carrier_bits = self.demodulate(carriers) raw_bits = self.deinterleave(carrier_bits) bits = self.viterbi_decode(raw_bits) signal = Signal(bits) print "[SIGNAL] %s" % (signal.__dict__) if not self.check_signal(signal): return cons = [] if signal.rate == 6: # decode next two OFDM SYMs to detect potential 802.11n packets demod_out = [] for _ in range(2): carriers = eq.next_symbol() assert len(carriers) == 48 cons.extend(carriers) # HT-SIG was rotated by 90 degrees, rotate it back carriers = [c*complex(0, -1) for c in carriers] carrier_bits = self.demodulate(carriers) demod_out.extend(carrier_bits) raw_bits = self.deinterleave(demod_out) signal_bits = self.viterbi_decode(raw_bits) ht_signal = HTSignal(signal_bits) if ht_signal.crc_ok: print "[HT SIGNAL] %s" % (ht_signal.__dict__) if not self.check_ht_signal(ht_signal): return signal = ht_signal eq.switch_ht() cons = [] eq.short_gi = ht_signal.short_gi == '1' if signal.ht: num_symbol = int(math.ceil(float((16+signal.length*8+6))/ HT_MCS_PARAMETERS[signal.mcs][2])) else: num_symbol = int(math.ceil(float((16+signal.length*8.0+6))/ RATE_PARAMETERS[signal.rate][2])) print "%d DATA OFDM symbols to decode" % (num_symbol) if signal.rate == 6 and not signal.ht: num_symbol -= 2 for _ in range(num_symbol): carriers = eq.next_symbol() if signal.ht: assert len(carriers) == 52, len(carriers) else: assert len(carriers) == 48, len(carriers) cons.extend(carriers) demod_out = self.demodulate(cons, signal) deinter_out = self.deinterleave(demod_out, signal.rate, signal.mcs, signal.ht) conv_out = self.viterbi_decode(deinter_out, signal) descramble_out = self.descramble(conv_out) if not all([b == 0 for b in descramble_out[:16]]): print '[SERVICE] not all bits are 0' # skip the SERVICE bits data_bits = descramble_out[16:] num_bytes = min(len(data_bits)/8, signal.length) data_bytes = [self.array_to_int(data_bits[i*8:(i+1)*8]) for i in range(num_bytes)] assert len(data_bytes) == num_bytes for i in range(0, num_bytes, 16): print '[%3d] %s' %\ (i, ' '.join([format(data_bytes[j], '02x') for j in range(i, min(i+16, num_bytes))])) fh = StringIO(''.join([chr(b) for b in data_bytes])) pkt = dot11.Dot11Packet(fh) return signal, cons, demod_out, deinter_out, conv_out, descramble_out, data_bytes, pkt def array_to_int(self, arr): assert len(arr) == 8 return int(''.join(['%d' % (b) for b in arr[::-1]]), 2)