Files
amodem/recv.py
2014-07-22 14:19:51 +03:00

153 lines
4.3 KiB
Python

import numpy as np
import pylab
import logging
import functools
import itertools
logging.basicConfig(level=0, format='%(message)s')
log = logging.getLogger(__name__)
import sigproc
import show
from common import *
COHERENCE_THRESHOLD = 0.95
CARRIER_DURATION = 300
CARRIER_THRESHOLD = int(0.95 * CARRIER_DURATION)
def detect(x, freq):
counter = 0
for offset, buf in iterate(x, Nsym, advance=Nsym):
coeff = sigproc.coherence(buf, Fc)
if abs(coeff) > COHERENCE_THRESHOLD:
counter += 1
else:
counter = 0
if counter == CARRIER_THRESHOLD:
length = CARRIER_THRESHOLD * Nsym
return offset - length + Nsym, offset
def find_start(x, start):
WINDOW = Nsym * 10
length = CARRIER_DURATION * Nsym
begin, end = start - WINDOW, start + length + WINDOW
x_ = x[begin:end]
Hc = sigproc.exp_iwt(Fc, len(x_))
P = np.abs(Hc.conj() * x_) ** 2
cumsumP = P.cumsum()
start = begin + np.argmax(cumsumP[length:] - cumsumP[:-length])
log.info('Carrier starts at {:.3f} ms'.format(start * Tsym * 1e3 / Nsym))
return start
def extract_symbols(x, freq, offset=0):
Hc = sigproc.exp_iwt(-freq, Nsym) / (0.5*Nsym)
func = lambda y: np.dot(Hc, y)
for _, symbol in iterate(x, Nsym, advance=Nsym, func=func):
yield symbol
def demodulate(x, freq, filt, plot=None):
S = extract_symbols(x, freq) # samples -> symbols
S = np.array(list(filt(S))) # apply equalizer
if plot:
plot()
show.constellation(S, title='$F_c = {} kHz$'.format(freq / 1e3))
for bits in sigproc.modulator.decode(S): # list of bit tuples
yield bits
def receive(x, freqs):
x = iter(x)
prefix = [1]*300 + [0]*100
symbols = itertools.islice(extract_symbols(x, Fc), len(prefix))
bits = np.round(np.abs(list(symbols)))
bits = np.array(bits, dtype=int)
if all(bits != prefix):
return None
log.info( 'Prefix OK')
filters = {}
full_scale = len(freqs)
training_bits = np.array(([1]*10 + [0]*10)*20 + [0]*100)
expected = full_scale * training_bits
for freq in freqs:
S = list(itertools.islice(extract_symbols(x, freq), len(expected)))
filt = sigproc.train(S, expected)
filters[freq] = filt
S = list(filt(S))
y = np.array(S).real
train_result = y > 0.5 * full_scale
if not all(train_result == training_bits):
pylab.plot(y, '-', expected, '-')
return None
noise = y - expected
Pnoise = sigproc.power(noise)
log.debug('{:10.1f}Hz: Noise sigma={:.4f}, SNR={:.1f} dB'.format( freq, Pnoise**0.5, 10*np.log10(1/Pnoise) ))
sz = int(np.ceil(np.sqrt(len(freqs))))
streams = []
x = list(x)
for i, freq in enumerate(freqs):
plot = functools.partial(pylab.subplot, sz, sz, i+1)
stream = demodulate(x, freq, filters[freq], plot=plot)
streams.append(stream)
bitstream = []
for block in itertools.izip(*streams):
for bits in block:
bitstream.extend(bits)
return bitstream
def main(fname):
_, x = load(open(fname, 'rb'))
result = detect(x, Fc)
if result is None:
log.info('No carrier detected')
return
begin, end = result
x_ = x[begin:end]
Hc = sigproc.exp_iwt(-Fc, len(x_))
Zc = np.dot(Hc, x_) / (0.5*len(x_))
amp = abs(Zc)
log.info('Carrier detected at ~{:.1f} ms @ {:.1f} kHz: coherence={:.3f}%, amplitude={:.3f}'.format(
begin * Tsym * 1e3 / Nsym, Fc / 1e3, abs(sigproc.coherence(x_, Fc)) * 100, amp
))
start = find_start(x, begin)
x = x[start:]
peak = np.max(np.abs(x))
if peak > SATURATION_THRESHOLD:
raise ValueError('Saturation detected: {:.3f}'.format(peak))
data_bits = receive(x / amp, frequencies)
if data_bits is None:
log.info('Cannot demodulate symbols!')
else:
import ecc
data = iterate(data_bits, bufsize=8, advance=8, func=to_byte)
data = ''.join(c for _, c in data)
data = ecc.decode(data)
if data is None:
log.warning('No blocks decoded!')
return
with file('data.recv', 'wb') as f:
f.write(data)
if __name__ == '__main__':
main('rx_.int16')
import os
if os.environ.get('SHOW') is not None:
pylab.show()