最後活躍 1 month ago

Soft Decision Bit LRPT Packet Finder (WIP)

修訂 1a84b617054be18a3e3f711972b2ee40b3b5b07a

soft-packet-finder.py 原始檔案
1#!/usr/bin/env python
2
3# WARNING: Work-in-progress
4
5import os, math
6import collections
7
8ifile = "2016_08_17_LRPT_09-06-31.s" #"sample.s"
9ofile = "sample.s.out"#"sample.s.out"
10skipi = 0 #4800
11
12'''
13Sync Words Phased
14
150 = 00011010 11001111 11111100 00011101 = 0x1ACFFC1D
161 = 10001111 01100101 01010110 10000100 = 0x8F655684
172 = 11100101 00110000 00000011 11100010 = 0xE53003E2
183 = 01110000 10011010 10101001 01111011 = 0x709AA97B
19
20
21
2210001111 01100101 01010110 10000100
2301110000 10011010 10101001 01111011
24
25'''
26
27'''
28Sync Word in 1/2 k=7 FEC:
29
301111 1100 1010 0010 1011 0110 0011 1101 1011 0000 0000 1101 1001 0111 1001 0100 0xfca2b63db00d9794
310101 0110 1111 1011 1101 0011 1001 0100 1101 1010 1010 0100 1100 0001 1100 0010 0x56fbd394daa4c1c2
320000 0011 0101 1101 0100 1001 1100 0010 0100 1111 1111 0010 0110 1000 0110 1011 0x035d49c24ff2686b
331010 1001 0000 0100 0010 1100 0110 1011 0010 0101 0101 1011 0011 1110 0011 1101 0xa9042c6b255b3e3d
34
35also for IQ reversal, we reverse each pair of bits. Since I'm lazy, this script does the bit reversal over binary string:
36
37x = "1010 1001 0000 0100 0010 1100 0110 1011 0010 0101 0101 1011 0011 1110 0011 1101".replace(" ", "")
38o = ""
39for i in range(0, len(x), 2):
40 o += x[i+1]
41 o += x[i]
42
43print o
44print hex(int(o, 2))
45
46So for bit reversal we have this sync words:
47
481111 1100 0101 0001 0111 1001 0011 1110 0111 0000 0000 1110 0110 1011 0110 1000 0xfc51793e700e6b68
491010 1001 1111 0111 1110 0011 0110 1000 1110 0101 0101 1000 1100 0010 1100 0001 0xa9f7e368e558c2c1
500000 0011 1010 1110 1000 0110 1100 0001 1000 1111 1111 0001 1001 0100 1001 0111 0x03ae86c18ff19497
510101 0110 0000 1000 0001 1100 1001 0111 0001 1010 1010 0111 0011 1101 0011 1110 0x56081c971aa73d3e
52
53'''
54
55'''
56Frame Size = 1024 bytes (1020 + 4)
57Rate = 1/2
58Frame Symbol Size = 1024 * 8 * 2
59
60Input = IQ Byte. Symbol = 2 Byte
61Frame Input Size = 1024 * 8 * 2 * 2
62'''
63
64frameSymbolSize = 1024 * 8
65frameInputSize = frameSymbolSize * 2
66frameBitSize = frameSymbolSize * 2
67syncWordSize = 32
68syncWordDoubleSize = 64
69
70UW0 = 0xfca2b63db00d9794
71UW1 = 0x56fbd394daa4c1c2
72UW2 = 0x035d49c24ff2686b
73UW3 = 0xa9042c6b255b3e3d
74
75REVUW0 = 0xfc51793e700e6b68
76REVUW1 = 0xa9f7e368e558c2c1
77REVUW2 = 0x03ae86c18ff19497
78REVUW3 = 0x56081c971aa73d3e
79
80'''
81 Tricky, we need a 2-bit group reversal for correcting IQ parameters.
82 This could be done in several ways, but I will do as a Lookup Table.
83 Basically for every index I in this element, there will be I with
84 2-bit group reversed output. If you're curious how I got this values,
85 I was lazy also.
86
87def bit2rev(n):
88 x = format(n, '#0{}b'.format(10))[2:] # Binary string of n
89 o = "" # Reversed 2bit binary string
90 for i in range(0, len(x), 2):
91 o += x[i+1]
92 o += x[i]
93 return int(o, 2) # Return the integer
94
95BIT2REV = []
96for i in range(256):
97 BIT2REV.append(bit2rev(i))
98
99
100So just for example: Lets supose we get a byte with value 123 that is IQ reversed.
101To un-reverse we go to the position 123 of the BIT2REV table that is: 183.
102
103So if we get the binary strings:
104
105123 = 01 11 10 11
106186 = 10 11 01 11
107
108So we inverted all 2 bit groups.
109'''
110
111BIT2REV = [
112 0, 2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15,
113 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47,
114 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31,
115 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63,
116 128, 130, 129, 131, 136, 138, 137, 139, 132, 134, 133, 135, 140, 142, 141, 143,
117 160, 162, 161, 163, 168, 170, 169, 171, 164, 166, 165, 167, 172, 174, 173, 175,
118 144, 146, 145, 147, 152, 154, 153, 155, 148, 150, 149, 151, 156, 158, 157, 159,
119 176, 178, 177, 179, 184, 186, 185, 187, 180, 182, 181, 183, 188, 190, 189, 191,
120 64, 66, 65, 67, 72, 74, 73, 75, 68, 70, 69, 71, 76, 78, 77, 79,
121 96, 98, 97, 99, 104, 106, 105, 107, 100, 102, 101, 103, 108, 110, 109, 111,
122 80, 82, 81, 83, 88, 90, 89, 91, 84, 86, 85, 87, 92, 94, 93, 95,
123 112, 114, 113, 115, 120, 122, 121, 123, 116, 118, 117, 119, 124, 126, 125, 127,
124 192, 194, 193, 195, 200, 202, 201, 203, 196, 198, 197, 199, 204, 206, 205, 207,
125 224, 226, 225, 227, 232, 234, 233, 235, 228, 230, 229, 231, 236, 238, 237, 239,
126 208, 210, 209, 211, 216, 218, 217, 219, 212, 214, 213, 215, 220, 222, 221, 223,
127 240, 242, 241, 243, 248, 250, 249, 251, 244, 246, 245, 247, 252, 254, 253, 255
128]
129
130'''
131 So for phase ambiguities we can also use a LUT. So we have four cases, but we
132 only need to account for one: 90 degrees. Because:
133 0 deg is what we want
134 90 deg we will do a LUT for getting 0 deg
135 180 deg is the negate of 0 deg (so we can just xor by 0xFF)
136 270 deg is the negate of 90 deg (so we can xor by 0xFF and then use 90 deg LUT)
137
138 So a 90 deg rotation in the constelation maps as:
139 0 deg => 90 deg
140 00 => 10
141 01 => 00
142 11 => 01
143 10 => 11
144
145 So as usual:
146
147def phaseshift(n):
148 x = format(n, '#0{}b'.format(10))[2:] # Binary string of n
149 o = "" # 90 deg phased binary string
150 for i in range(0, len(x), 2):
151 o += "1" if x[i+1] == "0" else "0"
152 o += "0" if x[i] == "0" else "1"
153 return int(o, 2) # Return the integer
154
155PHASED = []
156for i in range(256):
157 PHASED.append(phaseshift(i))
158
159'''
160
161PHASED = [
162 170, 168, 171, 169, 162, 160, 163, 161, 174, 172, 175, 173, 166, 164, 167, 165,
163 138, 136, 139, 137, 130, 128, 131, 129, 142, 140, 143, 141, 134, 132, 135, 133,
164 186, 184, 187, 185, 178, 176, 179, 177, 190, 188, 191, 189, 182, 180, 183, 181,
165 154, 152, 155, 153, 146, 144, 147, 145, 158, 156, 159, 157, 150, 148, 151, 149,
166 42, 40, 43, 41, 34, 32, 35, 33, 46, 44, 47, 45, 38, 36, 39, 37,
167 10, 8, 11, 9, 2, 0, 3, 1, 14, 12, 15, 13, 6, 4, 7, 5,
168 58, 56, 59, 57, 50, 48, 51, 49, 62, 60, 63, 61, 54, 52, 55, 53,
169 26, 24, 27, 25, 18, 16, 19, 17, 30, 28, 31, 29, 22, 20, 23, 21,
170 234, 232, 235, 233, 226, 224, 227, 225, 238, 236, 239, 237, 230, 228, 231, 229,
171 202, 200, 203, 201, 194, 192, 195, 193, 206, 204, 207, 205, 198, 196, 199, 197,
172 250, 248, 251, 249, 242, 240, 243, 241, 254, 252, 255, 253, 246, 244, 247, 245,
173 218, 216, 219, 217, 210, 208, 211, 209, 222, 220, 223, 221, 214, 212, 215, 213,
174 106, 104, 107, 105, 98, 96, 99, 97, 110, 108, 111, 109, 102, 100, 103, 101,
175 74, 72, 75, 73, 66, 64, 67, 65, 78, 76, 79, 77, 70, 68, 71, 69,
176 122, 120, 123, 121, 114, 112, 115, 113, 126, 124, 127, 125, 118, 116, 119, 117,
177 90, 88, 91, 89, 82, 80, 83, 81, 94, 92, 95, 93, 86, 84, 87, 85
178]
179
180
181def binary(num, length=8):
182 return format(num, '#0{}b'.format(length + 2))
183
184def binStringToIntSoftArr(b):
185 if b[1] == 'b':
186 b = b[2:]
187 a = []
188 for i in b:
189 a.append(int(i) * 0xFF)
190 return a
191
192def checkCorrelationInBuffer():
193 uw0mc = 0 # Highest Correlation Factor for UW0
194 uw0p = 0 # Highest Correlation Position for UW0
195 uw1mc = 0
196 uw1p = 0
197 uw2mc = 0
198 uw2p = 0
199 uw3mc = 0
200 uw3p = 0
201 ruw0mc = 0 # Highest Correlation Factor for REVUW0
202 ruw0p = 0 # Highest Correlation Position for REVUW0
203 ruw1mc = 0
204 ruw1p = 0
205 ruw2mc = 0
206 ruw2p = 0
207 ruw3mc = 0
208 ruw3p = 0
209
210 for i in range(0, frameBitSize - syncWordDoubleSize):
211 uw0c = 0
212 uw1c = 0
213 uw2c = 0
214 uw3c = 0
215 ruw0c = 0
216 ruw1c = 0
217 ruw2c = 0
218 ruw3c = 0
219 # To get the highest correlation, we xor with the word.
220 for k in range(0, syncWordDoubleSize):
221 uw0c += sbits[i+k] ^ UW0[k]
222 uw1c += sbits[i+k] ^ UW1[k]
223 uw2c += sbits[i+k] ^ UW2[k]
224 uw3c += sbits[i+k] ^ UW3[k]
225 ruw0c += sbits[i+k] ^ REVUW0[k]
226 ruw1c += sbits[i+k] ^ REVUW1[k]
227 ruw2c += sbits[i+k] ^ REVUW2[k]
228 ruw3c += sbits[i+k] ^ REVUW3[k]
229
230 uw0p = i if uw0c > uw0mc else uw0p
231 uw1p = i if uw1c > uw1mc else uw1p
232 uw2p = i if uw2c > uw2mc else uw2p
233 uw3p = i if uw3c > uw3mc else uw3p
234 ruw0p = i if ruw0c > ruw0mc else ruw0p
235 ruw1p = i if ruw1c > ruw1mc else ruw1p
236 ruw2p = i if ruw2c > ruw2mc else ruw2p
237 ruw3p = i if ruw3c > ruw3mc else ruw3p
238
239 uw0mc = uw0c if uw0c > uw0mc else uw0mc
240 uw1mc = uw1c if uw1c > uw1mc else uw1mc
241 uw2mc = uw2c if uw2c > uw2mc else uw2mc
242 uw3mc = uw3c if uw3c > uw3mc else uw3mc
243 ruw0mc = ruw0c if ruw0c > ruw0mc else ruw0mc
244 ruw1mc = ruw1c if ruw1c > ruw1mc else ruw1mc
245 ruw2mc = ruw2c if ruw2c > ruw2mc else ruw2mc
246 ruw3mc = ruw3c if ruw3c > ruw3mc else ruw3mc
247
248 return uw0p, uw0mc, uw1p, uw1mc, uw2p, uw2mc, uw3p, uw3mc, ruw0p, ruw0mc, ruw1p, ruw1mc, ruw2p, ruw2mc, ruw3p, ruw3mc
249
250
251
252UW0 = binStringToIntSoftArr(binary(UW0, syncWordDoubleSize))
253UW1 = binStringToIntSoftArr(binary(UW1, syncWordDoubleSize))
254UW2 = binStringToIntSoftArr(binary(UW2, syncWordDoubleSize))
255UW3 = binStringToIntSoftArr(binary(UW3, syncWordDoubleSize))
256REVUW0 = binStringToIntSoftArr(binary(REVUW0, syncWordDoubleSize))
257REVUW1 = binStringToIntSoftArr(binary(REVUW1, syncWordDoubleSize))
258REVUW2 = binStringToIntSoftArr(binary(REVUW2, syncWordDoubleSize))
259REVUW3 = binStringToIntSoftArr(binary(REVUW3, syncWordDoubleSize))
260
261f = open(ifile, "r")
262fsize = os.path.getsize(ifile)
263#o = open(ofile, "w")
264#ob = open(ofile + "bit", "w")
265
266sbits = collections.deque(maxlen=frameBitSize)
267
268count = 0
269fcount = 0
270
271f.seek(skipi)
272count += skipi
273
274print "Frame Input Size: %s" %frameInputSize
275
276while count < fsize:
277 '''
278 Read the data from input to sbits
279 '''
280 data = f.read(frameInputSize)
281 for i in range(0, frameInputSize):
282 sbits.append(ord(data[i]))
283
284 '''
285 Search for the sync word using correlation
286 '''
287
288 uw0p, uw0mc, uw1p, uw1mc, uw2p, uw2mc, uw3p, uw3mc, ruw0p, ruw0mc, ruw1p, ruw1mc, ruw2p, ruw2mc, ruw3p, ruw3mc = checkCorrelationInBuffer()
289 mp = max(uw0mc, uw1mc, uw2mc, uw3mc, ruw0mc, ruw1mc, ruw2mc, ruw3mc)
290
291 print "Frame: #%s" %fcount
292 print "Max Correlation: %s" % (mp / 256)
293 if mp == uw0mc:
294 print "Max Correlation with 0 degrees Word at %s" % uw0p
295 n = 0
296 p = uw0p
297 elif mp == uw1mc:
298 print "Max Correlation with 90 degrees Word at %s" % uw1p
299 n = 1
300 p = uw1p
301 elif mp == uw2mc:
302 print "Max Correlation with 180 degrees Word at %s" % uw2p
303 n = 2
304 p = uw2p
305 elif mp == uw3mc:
306 print "Max Correlation with 270 degrees Word at %s" % uw3p
307 n = 3
308 p = uw3p
309 elif mp == ruw0mc:
310 print "Max Correlation with 0 degrees Word at %s Reverse IQ" % ruw0p
311 n = 0
312 p = ruw0p
313 elif mp == ruw1mc:
314 print "Max Correlation with 90 degrees Word at %s Reverse IQ" % ruw1p
315 n = 1
316 p = ruw1p
317 elif mp == ruw2mc:
318 print "Max Correlation with 180 degrees Word at %s Reverse IQ" % ruw2p
319 n = 2
320 p = ruw2p
321 elif mp == ruw3mc:
322 print "Max Correlation with 270 degrees Word at %s Reverse IQ" % ruw3p
323 n = 3
324 p = ruw3p
325
326 if mp / 256 < 40:
327 print "Frame Lock Error. Correlation less than 47 bits"
328 count += frameInputSize
329 else:
330 '''
331 Read p bits for syncing the Circle Buffer
332 Each pair of bits come from 2 bytes of the input
333 So we will read 1 byte per bit
334 '''
335 #p -= 12
336 t = p if p % 2 == 0 else p + 1
337 data = f.read(t)
338 for i in range(0, t):
339 sbits.append(ord(data[i]))
340
341 '''
342 Now we should have everything in sync
343 '''
344 #frame = processFrame(list(sbits), n)
345 #o.write(frame)
346 #frameb = processFrameBitMap(list(sbits), n)
347 #ob.write(frameb)
348 fcount += 1
349 count += frameInputSize + t
350
351'''
352 Clean everything
353'''
354
355f.close()
356o.close()
357ob.close()