-
Notifications
You must be signed in to change notification settings - Fork 20
/
wpcr.py
executable file
·87 lines (79 loc) · 2.86 KB
/
wpcr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/python2
import numpy
import scipy.signal
tau = numpy.pi * 2
max_samples = 1000000
debug = False
# determine the clock frequency
# input: magnitude spectrum of clock signal (numpy array)
# output: FFT bin number of clock frequency
def find_clock_frequency(spectrum):
maxima = scipy.signal.argrelextrema(spectrum, numpy.greater_equal)[0]
while maxima[0] < 2:
maxima = maxima[1:]
if maxima.any():
threshold = max(spectrum[2:-1])*0.8
indices_above_threshold = numpy.argwhere(spectrum[maxima] > threshold)
return maxima[indices_above_threshold[0]]
else:
return 0
def midpoint(a):
mean_a = numpy.mean(a)
mean_a_greater = numpy.ma.masked_greater(a, mean_a)
high = numpy.ma.median(mean_a_greater)
mean_a_less_or_equal = numpy.ma.masked_array(a, ~mean_a_greater.mask)
low = numpy.ma.median(mean_a_less_or_equal)
return (high + low) / 2
# whole packet clock recovery
# input: real valued NRZ-like waveform (array, tuple, or list)
# must have at least 2 samples per symbol
# must have at least 2 symbol transitions
# output: list of symbols
def wpcr(a):
if len(a) < 4:
return []
b = (a > midpoint(a)) * 1.0
d = numpy.diff(b)**2
if len(numpy.argwhere(d > 0)) < 2:
return []
f = scipy.fft(d, len(a))
p = find_clock_frequency(abs(f))
if p == 0:
return []
cycles_per_sample = (p*1.0)/len(f)
clock_phase = 0.5 + numpy.angle(f[p])/(tau)
if clock_phase <= 0.5:
clock_phase += 1
symbols = []
for i in range(len(a)):
if clock_phase >= 1:
clock_phase -= 1
symbols.append(a[i])
clock_phase += cycles_per_sample
if debug:
print("peak frequency index: %d / %d" % (p, len(f)))
print("samples per symbol: %f" % (1.0/cycles_per_sample))
print("clock cycles per sample: %f" % (cycles_per_sample))
print("clock phase in cycles between 1st and 2nd samples: %f" % (clock_phase))
print("clock phase in cycles at 1st sample: %f" % (clock_phase - cycles_per_sample/2))
print("symbol count: %d" % (len(symbols)))
return symbols
# convert soft symbols into bits (assuming binary symbols)
def slice_bits(symbols):
symbols_average = numpy.average(symbols)
bits = (symbols >= symbols_average)
return numpy.array(bits, dtype=numpy.uint8)
def read_from_stdin():
return numpy.frombuffer(sys.stdin.buffer.read(), dtype=numpy.float32)
# If called directly from command line, take input file (or stdin) as a stream
# of floats and print binary symbols found therein.
if __name__ == '__main__':
import sys
debug = True
if len(sys.argv) > 1 and sys.argv[1] != '-':
samples = numpy.fromfile(sys.argv[1], dtype=numpy.float32)
else:
samples = read_from_stdin()
symbols=wpcr(samples)
bits=slice_bits(symbols)
print(list(bits))