#!/usr/bin/python

import os
import sys

# open trace file
# read line
# if -ve going edge, calculate time since last -ve edge in ns
# if duration >1000ns, skip to next line
# divide duration by pulse width (start at 67ns)
# subtract 1 to get number of zeros after the initial 1
# add to current buffer
# check start of buffer against patterns to decode
# remove match and add decoded value to output
# If buffer exceeds 12 bits after decode, byte-sync has been lost
# (000010000000 - part of 0000100000001000)
# Special case for address mark (see below)

# if not in sector
#   search for sync field (8 pulses of 0xff)
#   assert Lock to Data (LTD) and Bit Sync (BITSY)
#   wait for another 23 pulses
#   calculate value of pulse width
#   deassert BITSY and assert Sync Address Mark (SAM)
#   Check for match containing 0, when found:
#       Count four bit positions after the 0
#       Check for unique pattern flag
#   i.e. check for 0010000000100100
#   indicates valid preamble and sync

# Look for id then data
# gap, ID pre-amble, ID sync, ID marker, ID data, ID CRC, ID postamble
# 0x33, 0xff       , 0x62*  , 0xfe     , ...    , ...   , 0x33
# data preamble, data sync, data marker, user data, data ECC, data postamble
# 0xff         , 0x62*    , 0xfe       , ...      , ...     , 0x33

# Output:
#   33 (NN)
#   ff (NN)
#   62 fe CC CC HH SS EE EE - Sector: Cyl/Head/Sect
#   33 (NN)
#   ff (NN)
#   62 fe - Data:
#   nnn\txx xx xx xx xx xx xx xx  xx xx xx xx xx xx xx xx\t|................|
#   ECC: EE EE EE EE EE EE

# Write completed sector to sect-CCCC-HH-SS as binary
# Append ECC to ecc.txt as hex: sect-CCCC-HH-SS II II DD DD DD DD DD

RLLTONRZ = {
    '1000': '11',
    '001000': '011',
    '00001000': '0011',
    '0100': '10',
    '100100': '010',
    '00100100': '0010',
    '000100': '000',
    '0010000000100100': '01100010',
    '0000100': ''
    }

def decode_rll(pattern):
    """
    Decode a bitstring pattern as 2,7 RLL
    Return the decoded bitstring and pattern remainder
    """
    decoded = None
    mark = False
    newpattern = pattern
    for k in RLLTONRZ:
        if len(k) <= len(pattern):
            if pattern[0:len(k)] == k:
                decoded = RLLTONRZ[k]
                if len(decoded) == 8 or len(decoded) == 0:
                    mark = True
                newpattern = pattern[len(k):]
                break
    return (decoded, newpattern, mark)

def hexdump(data, start=0):
    """
    Print hex and ascii representation of data
    with each line beginning with an address starting at stat
    """
    col = 0
    for pos in range(0, len(data)):
        if col == 0:
            line = "%08x " % (start + pos)
            asc = ''
        line = line + "%02x " % ord(data[pos])
        if data[pos] > 31 and data[pos] < 127:
            asc = asc + chr(data[pos])
        else:
            asc = asc + '.'
        col = col + 1
        if (col % 8) == 0:
            line = line + " "
        if (col % 16) == 0:
            print line + '|%s|' % asc
            col = 0
    if col > 0:
        if col < 8:
            line = line + " "
        line = line + " " * (16 - col)
        print line + '|%s|' % asc

def main():
    if len(sys.argv) < 2:
        print "Usage: %s INPUTFILE" % sys.argv[0]
        exit(0)
    if not os.access(sys.argv[1], os.F_OK):
        print >> sys.stderr, "Input file: %s is not accessible" % sys.argv[1]
    try:
        infd = open(sys.argv[1], "r")
    except IOError:
        print >> sys.stderr, "Error opening input file: %s" % sys.argv[1]

    ltd = False
    bitsy = False
    sam = False
    bytesync = False
    fault = False
    pulsewidth = 66.67
    lastts = 0.0
    syncstartts = 0
    buff = ''
    decoded = ''
    output = ''
    synccount = 0
    bytecount = 0
    indata = False
    state = 1
    for line in infd:
        l = line.split(',')
        try:
            ts = float(l[0])
            s = int(l[1])
        except ValueError:
            print >> sys.stderr, "Skipping invalid line in input: %s" % line
            continue
        if s == 1:
            # Don't care about positive going edges
            continue
        if lastts == 0:
            # Can't calculate duration between pulses until we've seen the first pulse
            lastts = ts
            continue
        if fault:
            err = "Fault: "
            if ltd:
                err = err + "LTD "
            if bitsy:
                err = err + "BITSY "
            if sam:
                err = err + "SAM "
            if bytesync:
                err = err + "BYTESYNC "
            err = err + "Buffer: %s" % buff
            print >> sys.stderr, err
            ltd = False
            bitsy = False
            sam = False
            bytesync = False
            fault = False
            buff = ''
            decoded = ''
            synccount = 0
            bytecount = 0
            hexdump(output)
            output = ''
            state = 1

        # Pattern read is 1 followed by two to seven 0s
        patterntime = int((ts - lastts) * 1e9)
        patternlen = int(round(patterntime / pulsewidth))
        lastts = ts
        if patternlen > 8:
            print >> sys.stderr, "Invalid pattern, more than 7 zeros (%d), Searching for new sync field..." % (patternlen - 1)
            print "pattern too long: %d" % patterntime
            fault = True
            continue
        # Append pattern to buffer
        buff = buff + "1" + "0" * (patternlen-1)
        #print buff

        if bitsy or not ltd:
            # Not locked to data - search for sync field 0xff (10001000)
            if buff[0:4] == '1000':
                if synccount == 0:
                    print >> sys.stderr, "Got first sync nibble at: %f" % ts
                    syncstartts = ts
                    if state == 1:
                        state = 2
                synccount = synccount + 1
                buff = buff[4:]
            else:
                print >> sys.stderr, "searchsync"
                fault = True
                continue
            if synccount == 8:
                print >> sys.stderr, "Lock To Data at: %f" % ts
                ltd = True
                bitsy = True
            elif synccount == 31:
                print >> sys.stderr, "BITSY completed at: %f" % ts
                bitsy = False
                sam = True
                synclen = (ts - syncstartts) * 1e9
                #pulsewidth = synclen / 124  # 31 blocks of 4 pulses
                #print "New pulsewidth: %.2f" % pulsewidth
                if state == 2:
                    state = 3
        elif sam:
            # Search for 0010000000100100
            if buff[0:4] == '1000':
                synccount = synccount + 1
                buff = buff[4:]
                continue
            elif buff[0:16] == '0010000000100100':
                print >> sys.stderr, "Found SAM at: %f" % ts
                buff = buff[16:]
                sam = False
                bytesync = True
                if state == 3:
                    state = 4
            elif len(buff) < 16:
                continue
            else:
                print >> sys.stderr, "SAM"
                fault = True
        elif bytesync:
            data, buff, mark = decode_rll(buff)
            #print "decoded: %s, remaining buffer: %s, Mark? %s" % (data, buff, repr(mark))
            if data is not None:
                decoded = decoded + data
                if state == 4 and decoded[0:8] == '11111110':
                    state = 5
                #if state == 7:
                    #print "data: %s\tdecoded: %s" % (data, decoded)
                    #print "buffer: %s" % buff
                    #hexdump(output)
                if state == 7 and decoded[0:8] == '11111111':
                    state = 8
                while len(decoded) > 7:
                    output = output + chr(int(decoded[0:8], 2))
                    decoded = decoded[8:]
                    bytecount = bytecount + 1
                    #if not indata:  # in ID field
                    #    if bytecount == 6:
                if state == 5 and bytecount == 5:  # 0xfe + 4x data
                    state = 6
                elif state == 6 and bytecount == 7:  # 0xfe + 4x data + 2x CRC
                    state = 7
                    hexdump(output)
                    print "Sector ID: %d/%d/%d" % (((ord(output[1]) << 4) + ord(output[2])), ord(output[3]), ord(output[4]))
                    output = ''
                    bytecount = 0


if __name__ == "__main__":
    main()
