import sys
sys.path.append("..")

from pydsp import Signal
import pydsp.misc as misc
import pydsp.Modulation as Modulation
import pydsp.Window as Window
import pydsp.Filter as Filter
import math
import random
from pydsp.misc import *
import oss
import string
import struct
import os

deg45 = math.pi / 4
deg135 = math.pi/2 + deg45
deg225 = math.pi/2 + deg135
deg315 = math.pi/2 + deg225

def calcpdiff(phase1, phase2):
    phasediff = phase2 - phase1
    if phasediff < 0: phasediff = 2*math.pi + phasediff    
    return phasediff

def pskreceive(fs, freq, baudrate, signal, lo_phase = 0):
    # Calculate number of samples per symbol
    samplespersymbol = int(fs / baudrate)
    
    # Downconvert the signal from freq to a cplx baseband signal
    # We use a fixed frequency here, so any frequency drift on the signal
    # will break our demodulation.
    m = Modulation.Quadrature(fs)
    s = m.demodulate(signal, freq, phase=lo_phase, lpf=False)
    bb_sig = s.signal
    
    # Run through pulse-shape filter to remove out-of-band components
    # and also ISI. This filter coupled with the filter in the transmitter
    # create a Raised-Cosine filter.
    f = Filter.FIR(fs, cplx=True)
    f.make_rrc(baudrate, 0.5, gain=4)
    bb_shaped = f.filter(bb_sig)
    
    # Detect the carrier by measuring the signal power.
    # If it goes above a threshold we have a carrier
    s.signal = [r.real for r in bb_shaped]
    pw = s.power(fi = 150)
    cr = False
    for i in range(0, len(pw)):
        if pw[i] > 0.00001:
	    cr = i
	    break
    if not cr:
        print "No carrier detected"
        return pw,bb_shaped,None,None,None
    print "Carrier detected at sample "+str(cr)

    # Detect end of carrier
    for i in range(cr, len(pw)):
	if pw[i] < 0.000005:
	    ce = i
            break
    print "Carrier lost at sample "+str(ce)

    # We need at least 10 symbols of the training-sequence to pass
    # our filters before we play with the data.
    cr += samplespersymbol * 10
    
    # Rough estimate of symbol midpoint by detecting the peaks
    # of the real part of the signal
    bestval = 0
    bestpos = 0
    for i in range(cr, cr + (samplespersymbol * 3)):
	cur = abs(bb_shaped[i].real)
	if cur > bestval:
	    bestval = cur
	    bestpos = i
    cr = bestpos 

    print "Rough midpoint estimate: " + str(cr)

    # Fine-tune symbol timing by comparing phases of adjacent symbols
    # at slightly offset timings.
    # At the same time look for a 180deg phase-shift that indicates
    # the end of training and start of real data
    totaloffs = 0
    training = True
    while training:
	besterr = 100
	bestpos = 0

	# Try fiddling the baud alignment a bit and see if it gets better
	for i in range(-1,2):
	    phase1 = ang(bb_shaped[int(round(cr)) + i - samplespersymbol])
	    phase2 = ang(bb_shaped[int(round(cr)) + i])
	    phasediff = calcpdiff(phase1, phase2)

	    # Check for end of training, indicated by 180deg phase jump
	    if phasediff >= deg135 and phasediff <= deg225:
		training = False
		break

	    # Calculate the error for this particular shift, and see
	    # if there is any improvement.
	    error = abs(phasediff - math.pi/2)
	    if error < besterr:
		besterr = error
		bestpos = i

	if training:
	    cr = cr + bestpos + samplespersymbol
	    totaloffs += bestpos

    # Training ended, so phase-shift must have been detected
    cr = int(round(cr))
    print "Detected end of training at "+str(cr),
    print " total adjustment was: "+str(totaloffs)

    # Pre-set lastphase to our current phase, needed for differential decoding
    lastphase = ang(bb_shaped[cr])
        
    # We are now in the middle of the first symbol
    cr += samplespersymbol
    print "Estimate first data symbol midpoint at "+str(cr)


    # Sample the symbol midpoints to get the symbol sent
    # This is a very simple approach, where we pick samples that
    # are 'samplespersymbol' apart. For real applications this would need
    # to be more robust.
    syms = [bb_shaped[cr + int(samplespersymbol*i)]
            for i in range(0, int((ce-cr) / samplespersymbol))]

    # Differentially decode into dibits
    dibits = []
    for s in syms:
        phase = ang(s)
        phasediff = calcpdiff(lastphase, phase)
        lastphase = phase

        # Simple phase->dibit decoder. Just test that the phase difference
        # is within a specified range. Look at sendit.py to see how the
        # constellation looks like.
        if phasediff <= deg45 or phasediff > deg315: dibit = 0
        elif phasediff <= deg135 and phasediff > deg45: dibit = 1
        elif phasediff <= deg225 and phasediff > deg135: dibit = 3
        elif phasediff <= deg315 and phasediff > deg225: dibit = 2

        dibits.append(dibit)

    # Byte-decode the dibits
    bytes = ''
    for i in range(0, len(dibits) / 4):
        z = i*4
        b = dibits[z] << 6
        b+= dibits[z+1] << 4
        b+= dibits[z+2] << 2
        b+= dibits[z+3]
        bytes += chr(b)

    return pw, bb_shaped, syms, dibits, bytes



##
# This reads the signal for demodulation.
# You may select reading it from a file or from your soundcard
from_soundcard = False
filename = 'outfile'
sec = 10
fs = 44100
carrier_freq = 10000
baudrate = 2321


if from_soundcard:
    a = oss.open_audio("/dev/dsp",os.O_RDONLY )
    a.format(oss.AFMT_S16_LE)
    a.stereo(False)
    a.speed(fs)
    a.sync()
    print "Go!"
    u = a.read(fs * 20)
    print "Gone!"
    fmt = 'h' * (len(u) / 2)
    out = struct.unpack(fmt, u)
    s = [float(z) / 32767 for z in out]
else:
    f = open(filename, "r")
    s = []
    l = f.readline()
    l = f.readline()
    while len(l) > 2:
        s.append(float(l.strip().split(' ',1)[1]))
        l = f.readline()
    f.close()
z = s

# Feed the sampled data into the modem
pw,bb,s,d,b = pskreceive(fs = fs,             # Sample frequency
                         freq = carrier_freq, # Carrier frequency
                         baudrate = baudrate,
                         signal = s
            )


constelplot(s)

#f = open("clean", "r")
#s = []
#l = f.readline()
#l = f.readline()
#while len(l) > 2:
#    s.append(float(l.strip().split(' ',1)[1]))
#    l = f.readline()
#f.close()

#pwc,bbc,sc,dc,bc = pskreceive(fs=fs,     # Sample frequency
#               freq=12800,    # Carrier frequency
#               baudrate=3392,
#               signal = s
#            )

def segplot(v, sp):
    p1 = sp - 7 - 52
    p2 = sp + 7 + 130
    angplot(v[p1:p2])
