#!/usr/bin/python3
"""
Re-write "gps_win.pl" to "GPS.py"
by Qige <qigezhao@gmail.com>
2017.10.10-2017.10.13 compatible with u-blox6 GMOUSE GPS VK-162
2017.10.16 compatible with u-blox7, Stoton GPS+BDS GNSS100B
2017.10.17 final re-format
2017.10.17 read hex & ascii GPS Sensor data. not verified yet

Done:
    * verify with real GPS Sensor (GMOUSE VK-162, USB Mouse style);
    * Compatible with multi protocol.
"""

import re
import sys
import time

import serial
import serial.tools.list_ports

FLAG_RUN = 1
FLAG_DBG = 0

GPS_SENSOR      = 'gps.txt'


# application
def appVersion():
    print('ARNPerf v7.1.4 (https://github.com/zhaoqige/arnperf.git')
    print('---- by Qige <qigezhao@gmail.com> v7.1.4.181017-py ----')
    print('-------------------------------------------------------')

def appHelp():
    print('Usage: GSP.py com4 [gps.txt] # user defined GPS Sensor & output file')
    print('Usage: GSP.py                # find GPS Sensor, then write to "gps.txt"')

def cliParams():
    if sys and sys.argv and len(sys.argv) >= 3:
        return sys.argv[1:3] # 3rd not included
    
    if sys and sys.argv and len(sys.argv) >= 2:
        return sys.argv[1], None
    
    return None, None


def HexToAscii(data):
    if data and len(data) > 0:
        result = []
        for c in data:
            try:
                cs = chr(c)
            except:
                #cs = chr(str(c))
                cs = chr(ord(c))

            if re.match('[0-9a-zA-Z,\$,\*\.\\\\]', cs): # 0-9a-z
                result.extend(cs)

        return ''.join(result)

    return None

# serial port handler
def spOpen(serialName):
    # 115200/8/N/1
    try:
        if serialName:
            serialFd = serial.Serial(serialName, timeout = 3)
            serialFd.baudrate       = 115200
            #serialFd.baudrate       = 9600
            serialFd.bytesize       = 8
            serialFd.parity         = serial.PARITY_NONE;
            serialFd.stopbits       = 1
            
            serialFd.timeout        = 3
            serialFd.writeTimeout   = 1
            
            if serialFd and serialFd.readable():
                return serialFd
        else:
            return None
        
    except serial.SerialException:
        return None

def spRead(serialFd):
    if serialFd and (serialFd.readable()):
        buffer = serialFd.read(512)
        #buffer = '\xc4\xe3\xba\xc3\xb0\xa1121A\xba\xc5'
        #buffer = '00*1F$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F'
        if buffer:
            #return buffer # v7.1.2
            # v7.1.3 fix hex data from GPS Sensor
            data = HexToAscii(buffer)
            return data
    
    return None

def spWrite(serialFd, data):
    if serialFd and data:
        serialFd.write(data)

def spClose(serialFd):
    if serialFd:
        serialFd.close()

# u-blox 6 chip: GPS, start with $GP*
def GPSUblox6(msg):
    if msg and len(msg) >= 6:
        flagUblox6 = re.search("GPRMC|GPGGA|GPGSA|GPGSV|GPVTG|GPGLL|GPTXT", \
                    str(msg))
        
        if (flagUblox6):
            return 1
    
    return 0

# u-blox 7 chip: GPS+BSD, start with $GN*
def GPSUblox7(msg):
    if msg and len(msg) >= 6:
        flagUblox7 = re.search("GNRMC|GNGGA|GNGSA|GBGSV|GNGSV|GNVTG|GNGLL|GNTXT", \
                    str(msg))
        
        if (flagUblox7):
            return 1
    
    return 0

# find first GPS Sensor, return fd
def GPSSensorFindFd(spDev):
    if spDev:
        spDesc = spDev[0]
        #spDesc = 'com17' # TODO: DEBUG USE ONLY!
        serialFd = spOpen(spDesc)
        if serialFd:
            spData = spRead(serialFd) # read GPS Sensor
            # DEBUG USE ONLY!
            #spData = ",,,,0000*1F$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F"
            spDataLength = 0
            if spData:
                spDataLength = len(spData)
            
            if spDataLength >= 6:
                if GPSUblox6(spData) or GPSUblox7(spData):
                    serialName = serialFd.name
                    print("-> GPS sensor found:", serialName, '|', spDataLength, 'bytes')
                    return serialFd
                else:
                    if spData.isalnum(): print(spData)
                    spClose(serialFd)

        else:
            print('->', spDesc, '- Device Busy or Not GPS Sensor')
            
    else:
        print('error> Unknown Device')
        
    return None

# protocol: NEMA-0138
# $GPRMC sample data
# "$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50"
def ProtoNEMA0183FindGPRMC(data):
    gprmcRaw = None
    if data:
        gpList = re.split(r'[\$\n\r\\\n\\\r]', str(data))
        if gpList and len(gpList) >= 1:
            for line in gpList:
                if line and re.search('GPRMC|GNRMC', line):
                    if (FLAG_DBG > 0): print('dbg> line:', line)
                    gprmcRaw = line
                    break
    
    return gprmcRaw

def ProtoNEMA0183DegreeConvert(degreeRaw, isSW):
    vi = int(float(degreeRaw) / 100)