Notes About Working with Various Arduino & Netduino Microcontroller Boards

Tuesday, June 10, 2014

Python Code to Read & Parse TSIP Data from a Copernicus II GPS Module on an Arduino Yun

The following example shows to read and parse some basic GPS data (location and time) in TSIP format from a Copernicus II GPS module. TSIP data is in binary format, unlike NMEA sentences that are in ASCII format. This code is in Python and is designed to run on an Arduino Yun.  In this case, the Copernicus II is connected to the Arduino Yun using the Yun's USB port.  I use a Sparkfun 3.3V FTDI adapter.

Connections


Copernicus II FTDI Adapter
TX-B          RXI
RX-B          TXO
GND           GND

The Copernicus II also has its VCC and GND connected to 3.3V and GND on the Yun.

This example assumes that you are using SSH to access your Yun's command line.  The program outputs the current GPS time and location to the terminal.

Prerequisits


This example requires a couple modules that are installed using opkg at the command line on the Yun.  The following commands will install them:


opkg update
opkg install kmod-usb-serial-ftdi
opkg install pyserial

Code


import serial
import struct
import math
import datetime

ser = serial.Serial("/dev/ttyUSB0", baudrate=38400)
tsip = []
last = ''
start = 0
dle_cnt = 0
id = 0

DLE = '\x10'
ETX = '\x03'

time = "No time data yet"
today = "No date data yet"

# GPS date counts weeks starting Jan. 6, 1980
startDate = datetime.date(1980, 1, 6)

while True:
  data = ser.read()
  # Test for data frame start marker DLE (0x10)
  if start == 0 and data == DLE:
    start = 1
  # To avoid confusion, when the frame marker DLE (0x10) occurs in the sequence of data
        # bytes in the data frame, it is doubled.  We need to drop the extra 0x10 by skipping the 
  # rest of the loop for the current iteration. Count the number of DLEs to track whether 
  # it is even or odd.  The count DLE that marks the end of the frame - before ETX (0x03) - 
  # is always odd. See p. 122 of the Copernicus II manual.
  elif start == 1 and data == DLE:
    dle_cnt = dle_cnt + 1
    if last == DLE:
      continue
  # If the last byte was DLE (0x10) and the current byte is not ETX (0x03),
  # then the current byte is the packet ID
  elif start == 1 and data != ETX and last == DLE:
    id = data
  # If the current byte is 0x03 (ETX), has come right after DLE (0x10), & the 
  # DLE count is odd, we have reached the end of the data frame.
  elif start == 1 and data == ETX and last == DLE and dle_cnt % 2 == 1:
    dle_cnt = 0
    last = ''
    tsip.append( data )

    # Packet 0x41 has GPS time data
    if id == '\x41':
      # t is number of seconds since start of week
      t = int(struct.unpack('f', "".join(tsip[2:6]))[0])
      # w is number of weeks since Jan. 6, 1980
      w = int(struct.unpack('h', "".join(tsip[6:8]))[0])
      # off is offset in seconds between GPS time and GMT
      off = struct.unpack('f', "".join(tsip[8:12]))[0]
      # Subtract offset seconds from time in seconds from start 
      # of week
      t = t - off
      # Day of current week; Sunday = 0
      day = math.floor(t / 86400)
      # Hour of the day
      hour = math.floor((t - (day * 86400))/3600)
      # Minutes after the hour
      min = math.floor((t - (day * 86400) - (hour * 3600))/60)
      # Seconds into current minute
      sec = t - (day * 86400) - (hour * 3600) - (min * 60)      
      time = "%02d:%02d:%02d GMT" % (hour, min, sec)
      today = startDate + datetime.timedelta(weeks=w) + \
        datetime.timedelta(days=day)
      
    # Packet 0x84 has the position data we need.
    # See p. 163 of the Copernicus II manual for structure of this packet
    if id == '\x84':
      # Join bytes into string without added spaces and unpack as big-endian 
      # double.  struct.unpack returns a tuple. Our value is in the first element.
      lat_rad = struct.unpack('>d', "".join(tsip[2:10]))[0]
      lat_deg = lat_rad * 180.0 / math.pi
      lat_dir = 'N' if lat_deg > 0 else 'S'
      long_rad = struct.unpack('>d', "".join(tsip[10:18]))[0]
      long_deg = long_rad * 180.0 / math.pi
      long_dir = 'E' if long_deg > 0 else 'W'
      print "%s %s:  %02.6f %s  %03.6f %s\n" % (today, time, abs(lat_deg), 
        lat_dir, abs(long_deg), long_dir)
      id = 0
    tsip = []
    start = 0
    continue
  last = data    
  tsip.append(data)






No comments:

Post a Comment