#!/usr/bin/env python

import os, math
import collections

'''
Sync Words Phased

0 = 00011010 11001111 11111100 00011101                                     = 0x1ACFFC1D
1 = 10001111 01010101 01010110 10000100                                     = 0x8F555684
2 = 11100101 00110000 00000011 11100010                                     = 0xE53003E2
3 = 01110000 10101010 10101001 01111011                                     = 0x70AAA97B
'''

'''
Sync Words 1/2 Symbols
0 = 00000011 11001100 11110000 11111111 11111111 11110000 00000011 11110011 = 0x03CCF0FFFFF030F3
1 = 11000000 11111111 00110011 00110011 00110011 00111100 11000000 00110000 = 0xC0FF3333333CC030
2 = 11111100 00110011 00001111 00000000 00000000 00001111 11111100 00001100 = 0xFC33F00000F0FCC0
3 = 00111111 00000000 11001100 11001100 11001100 11000011 00111111 11001111 = 0x3F00CCCCCCC33FCF

    00000011 11001100 11110000 11111111 11111111 11110000 00000011 11110011
S = 00000011 10010010 11100011 01010100 11100111 10100111 11010100 00010101

    11111100 00110011 00001111 00000000 00000000 00001111 11111100 00001100
F = 11111100 01101101 00011100 10101011 00011000 01011000 00101011 11101010
    00000011 10010010 11100011 01010100 11100111 10100111 11010100 00010101
'''


'''
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 = syncWordSize * 2

UW0 = 0x03CCF0FFFFF030F3
UW1 = 0xC0FF3333333CC030
UW2 = 0xFC33F00000F0FCC0
UW3 = 0x3F00CCCCCCC33FCF

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 1
        b = 1 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 = 1 if frameData[i+1] == 0 else 1
        b = 1 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)):
    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

def symbolToBit(symbol):
  bdata = []
  I = ord(symbol[0]) - 127
  Q = ord(symbol[1]) - 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 inverse of the word. So 180 phase
    for k in range(0, syncWordDoubleSize):
      uw0c += sbits[i+k] ^ UW2[k]
      uw1c += sbits[i+k] ^ UW3[k]
      uw2c += sbits[i+k] ^ UW0[k]
      uw3c += sbits[i+k] ^ UW1[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")
fsize = os.path.getsize("test.s")
o = open("test.s.out", "w")
ob = open("test.s.outbit", "w")

sbits = collections.deque(maxlen=frameBitSize)

count = 0
fcount = 0

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

  '''
    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
  '''
  #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])

  print "B: " + str(list(sbits)[p:p+64])
  print "W: " + str(UW0)

  '''
    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()