#!/usr/bin/env python

import os, math
import collections

ifile = "sample.s" #"sample.s"
ofile = "sample.s.out"#"sample.s.out"
skipi = 0 #4800

'''
Sync Words Phased

0 = 00011010 11001111 11111100 00011101                                     = 0x1ACFFC1D
1 = 10001111 01100101 01010110 10000100                                     = 0x8F655684
2 = 11100101 00110000 00000011 11100010                                     = 0xE53003E2
3 = 01110000 10011010 10101001 01111011                                     = 0x709AA97B



10001111 01100101 01010110 10000100
01110000 10011010 10101001 01111011

'''

'''
Sync Word in 1/2 k=7 FEC:

1111 1100 1010 0010 1011 0110 0011 1101 1011 0000 0000 1101 1001 0111 1001 0100 0xfca2b63db00d9794
0101 0110 1111 1011 1101 0011 1001 0100 1101 1010 1010 0100 1100 0001 1100 0010 0x56fbd394daa4c1c2
0000 0011 0101 1101 0100 1001 1100 0010 0100 1111 1111 0010 0110 1000 0110 1011 0x035d49c24ff2686b
1010 1001 0000 0100 0010 1100 0110 1011 0010 0101 0101 1011 0011 1110 0011 1101 0xa9042c6b255b3e3d
'''

'''
Frame Size = 1024 bytes (1020 + 4)
Rate = 1/2
Frame Symbol Size = 1024 * 8 * 2

Input = IQ Byte. Symbol = 2 Byte
Frame Input Size = 1024 * 8 * 2 * 2
'''

frameSymbolSize = 1024 * 8
frameInputSize = frameSymbolSize * 2
frameBitSize = frameSymbolSize * 2
syncWordSize = 32
syncWordDoubleSize = 64

UW0 = 0xfca2b63db00d9794
UW1 = 0x56fbd394daa4c1c2
UW2 = 0x035d49c24ff2686b
UW3 = 0xa9042c6b255b3e3d

def processFrame(frameData, n):
  '''
    Gets the bit array, corrects the phase ambiguity and returns a byte array
    frameData = bit array
    n = phase number(0 = 0, 1 = 90, 2 = 180, 3 = 270)
  '''
  # Fix Ambiguity
  '''
    p0 p1 p2 p3
    00 10 11 01
    01 00 10 11
    11 01 00 10
    10 11 01 00
  '''

  if n == 1 or n == 3:  # 90 / 270 phase
    # We can just shift to 90 and if its 270 we invert the bits later
    for i in range(0, len(frameData), 2):
        a = 1 if frameData[i+1] == 0 else 0
        b = 0 if frameData[i] == 0 else 1
        frameData[i] = a
        frameData[i+1] = b

  if n == 2 or n == 3: # 180 / 270 is 0 / 90 inverted
    frameData = invertBits(frameData)

  # Generate byte array
  bdata = ""
  for i in range(0, len(frameData), 8):
    v = 0
    for k in range(0, 8):
      v = v << 1
      v = v | frameData[i+k]
    bdata += chr(v)

  return bdata


def processFrameBitMap(frameData, n):
  '''
    Gets the bit array, corrects the phase ambiguity and returns a byte array
    frameData = bit array
    n = phase number(0 = 0, 1 = 90, 2 = 180, 3 = 270)
  '''
  # Fix Ambiguity
  '''
    p0 p1 p2 p3
    00 10 11 01
    01 00 10 11
    11 01 00 10
    10 11 01 00
  '''

  if n == 1 or n == 3:  # 90 / 270 phase
    # We can just shift to 90 and if its 270 we invert the bits later
    for i in range(0, len(frameData), 2):
        a = 0 if frameData[i+1] == 0 else 1
        b = 1 if frameData[i] == 0 else 0
        frameData[i] = a
        frameData[i+1] = b

  if n == 2 or n == 3: # 180 / 270 is 0 / 90 inverted
    frameData = invertBits(frameData)

  # Generate byte array
  bdata = ""
  for i in range(0, len(frameData)):
    bdata += chr(0xFF) if frameData[i] == 1 else chr(0x00)
    #bdata += chr(0xFF) if frameData[i] == 1 else chr(0x00)
    #bdata += chr(0xFF) if frameData[i] == 1 else chr(0x00)
  return bdata

def invertBits(frame):
  for i in range(0, len(frame)):
    frame[i] = 1 if frame[i] == 0 else 0

  return frame

def binary(num, length=8):
    return format(num, '#0{}b'.format(length + 2))

def binStringToIntArr(b):
  if b[1] == 'b':
    b = b[2:]
  a = []
  for i in b:
    a.append(int(i))
  return a

invertIQ = False

def symbolToBit(symbol):
  bdata = []
  if not invertIQ:
  	I = ord(symbol[0]) - 127
  	Q = ord(symbol[1]) - 127
  else:
  	I = ord(symbol[1]) - 127
  	Q = ord(symbol[0]) - 127

  if I > 0:
    bdata.append(1)
  else:
    bdata.append(0)

  if Q > 0:
    bdata.append(1)
  else:
    bdata.append(0)

  return bdata

def checkCorrelationInBuffer():
  uw0mc = 0 # Highest Correlation Factor for UW0
  uw0p  = 0 # Highest Correlation Position for UW0
  uw1mc = 0
  uw1p  = 0
  uw2mc = 0
  uw2p  = 0
  uw3mc = 0
  uw3p  = 0

  for i in range(0, frameBitSize - syncWordDoubleSize):
    uw0c = 0
    uw1c = 0
    uw2c = 0
    uw3c = 0
    # To get the highest correlation, we xor with the word. So 180 phase
    for k in range(0, syncWordDoubleSize):
      uw0c += sbits[i+k] ^ UW0[k]
      uw1c += sbits[i+k] ^ UW1[k]
      uw2c += sbits[i+k] ^ UW2[k]
      uw3c += sbits[i+k] ^ UW3[k]

    uw0p = i if uw0c > uw0mc else uw0p
    uw1p = i if uw1c > uw1mc else uw1p
    uw2p = i if uw2c > uw2mc else uw2p
    uw3p = i if uw3c > uw3mc else uw3p

    uw0mc = uw0c if uw0c > uw0mc else uw0mc
    uw1mc = uw1c if uw1c > uw1mc else uw1mc
    uw2mc = uw2c if uw2c > uw2mc else uw2mc
    uw3mc = uw3c if uw3c > uw3mc else uw3mc

  return uw0p, uw0mc, uw1p, uw1mc, uw2p, uw2mc, uw3p, uw3mc


UW0 = binStringToIntArr(binary(UW0, syncWordDoubleSize))
UW1 = binStringToIntArr(binary(UW1, syncWordDoubleSize))
UW2 = binStringToIntArr(binary(UW2, syncWordDoubleSize))
UW3 = binStringToIntArr(binary(UW3, syncWordDoubleSize))

#f = open("test.s", "r")
f = open(ifile, "r")
fsize = os.path.getsize(ifile)
o = open(ofile, "w")
ob = open(ofile + "bit", "w")

sbits = collections.deque(maxlen=frameBitSize)

count = 0
fcount = 0

f.seek(skipi)
count += skipi

print "Frame Input Size: %s" %frameInputSize

while count < fsize:
  '''
    Read the data from input to sbits
  '''
  data = f.read(frameInputSize)
  for i in range(0, frameInputSize, 2):
    b = symbolToBit(data[i:i+2])
    sbits.append(b[0])
    sbits.append(b[1])

  '''
    Search for the sync word using correlation
  '''

  uw0p, uw0mc, uw1p, uw1mc, uw2p, uw2mc, uw3p, uw3mc = checkCorrelationInBuffer()
  mp = max(uw0mc, uw1mc, uw2mc, uw3mc)
  print "Frame: #%s" %fcount
  print "Max Correlation: %s" %mp
  if mp == uw0mc:
    print "Max Correlation with 0   degrees Word at %s" % uw0p
    n = 0
    p = uw0p
  elif mp == uw1mc:
    print "Max Correlation with 90  degrees Word at %s" % uw1p
    n = 1
    p = uw1p
  elif mp == uw2mc:
    print "Max Correlation with 180 degrees Word at %s" % uw2p
    n = 2
    p = uw2p
  elif mp == uw3mc:
    print "Max Correlation with 270 degrees Word at %s" % uw3p
    n = 3
    p = uw3p
  if mp < 47:
    print "Frame Lock Error"
    count += frameInputSize
  else:
    '''
      Read p bits for syncing the Circle Buffer
      Each pair of bits come from 2 bytes of the input
      So we will read 1 byte per bit
    '''
    #p -= 12
    t = p if p % 2 == 0 else p + 1
    data = f.read(t)
    for i in range(0, t, 2):
      b = symbolToBit(data[i:i+2])
      sbits.append(b[0])
      sbits.append(b[1])

    '''
      Now we should have everything in sync
    '''
    frame = processFrame(list(sbits), n)
    o.write(frame)
    frameb = processFrameBitMap(list(sbits), n)
    ob.write(frameb)
    fcount += 1
    count += frameInputSize + t

'''
  Clean everything
'''

f.close()
o.close()
ob.close()