Tuesday, July 03, 2012

My 2nd Hunk of Python Code

Build (potentially useless) NMEA data from MOTOACTV downloaded CSV file:


Acknowledgement: thanks to Beni Hess' comment on a Python Recipe post for saving me days of trying to figure out the XORs for the checksums.

Also, this NMEA reference from Robosoft proved helpful in building the sentences.

Updated below, now that all the calculations are set up as functions. Final version is posted here.

#!/usr/bin/env python
import csv, time, math, operator

filename = "rawDataCsv.csv"
# filename = "miniDataCsv.csv"

# Recorded epoch is GMT, staticly setting this to CDT for now
TZoffset_hours = int(-5)
# Living without minute offsets and assuming zero
TZoffset_string = str(TZoffset_hours) + ",00"

# Function to generate time (to the second, with truncated hundredths -- close enough) and date from the recorded timestamp
def Epoch2DateTimeStrings(epoch):
    noms_epoch = str(epoch)[:-3]
    just_ms = str(epoch)[-3:]
    cut_epoch = float(noms_epoch)
    HHMMSS = time.strftime("%H%M%S",time.gmtime(cut_epoch))
    DDMMYYYY = time.strftime("%d,%m,%Y",time.gmtime(cut_epoch))
    hundredths = str(just_ms)[:2]
    time_string = str(HHMMSS) + "." + hundredths
    date_string = str(DDMMYYYY)
    return (date_string, time_string)

# Function to determine the cardinality of a coordinate (lat/long), based on its sign (+/-)
def Cardinality(coordinate, latlong):
    if latlong == "longitude":
        if coordinate < 0: cardinality = "W"
        else: cardinality = "E"
    elif latlong == "latitude":
        if coordinate < 0: cardinality = "S"
        else: cardinality = "N"
    return cardinality

# Function to convert a coordinate (lat/long) from decimal degrees to degrees and decimal minutes
def Deg2DegMin(coordinate):
    coord = math.modf(math.fabs(coordinate))
    coord_deg = int(coord[1])
    coord_min = float(coord[0] * 60)
    return (coord_deg, coord_min)

# Function to convert Latitude and Longitude to NMEA GLL format
def LatLong2Strings (latitude, longitude):
    latitude_direction = Cardinality(latitude, "latitude")
    longitude_direction = Cardinality(longitude, "longitude")
    # Latitude
    deg_lat, min_lat = Deg2DegMin(latitude)
    deg_lat = str(deg_lat)
    min_lat = str("%.4f" % min_lat)
    lat_string = deg_lat + min_lat + "," + latitude_direction + ","
    # Longitude
    deg_long, min_long = Deg2DegMin(longitude_decdeg)
    deg_long = str(deg_long)
    min_long = str("%.4f" % min_long)
    long_string = deg_long + min_long + "," + longitude_direction + ","
    # Return the strings
    return (lat_string, long_string)


def BuildNMEA(lat_string, long_string, time_string, date_string, TZoffset_string):
    # Concatenate the pieces into the sections of the NMEA GLL and ZDA sentences that get checksummed (omit the leading $ and trailing *ck)
    gpgll_string = str("GPGLL," + lat_string + long_string + time_string + ",A")
    gpzda_string = str("GPZDA," + time_string + "," + date_string + "," + TZoffset_string)
    # Calculate the checksums (just in case)
    GLL_checksum = reduce(operator.xor, (ord(c) for c in gpgll_string), 0)
    ZDA_checksum = reduce(operator.xor, (ord(c) for c in gpzda_string), 0)
    # Convert to Hex (no 0x) and pad a leading zero if needed
    GLL_hex = ("%X" % GLL_checksum).zfill(2)
    ZDA_hex = ("%X" % ZDA_checksum).zfill(2)
    # Now, put it all together into a full NMEA GLL and ZDA sentences and render them
    NMEA_GLL_sentence = str("$" + gpgll_string + "*" + GLL_hex)
    NMEA_ZDA_sentence = str("$" + gpzda_string + "*" + ZDA_hex)
    # Return the sentences
    return (NMEA_ZDA_sentence, NMEA_GLL_sentence)



ACTVdatafile = open(filename,"rb")
ACTVdata = csv.reader(ACTVdatafile)
rowindex = 0
for row in ACTVdata:
    # Don't try to grok the heading row from the CSV file
    if rowindex == 0: pass
    # Build NMEA sentences from every other row
    else:
        latitude_decdeg = float(row[5])
        ms_epoch = row[9]
        longitude_decdeg = float(row[15])
        date_string, time_string = Epoch2DateTimeStrings(ms_epoch)
        lat_string, long_string = LatLong2Strings(latitude_decdeg, longitude_decdeg)
        NMEA_ZDA, NMEA_GLL = BuildNMEA(lat_string, long_string, time_string, date_string, TZoffset_string)
        print NMEA_ZDA
        print NMEA_GLL  
    rowindex += 1           

ACTVdatafile.close()