neil on the web!

kelly watch the stars

kelly watch the stars

I found that the phone apps that I used to track my walking did not always reliably record my position, track, or time. I decided to build a gps tracker running on a Raspberry Pi to, hopfully, do a better job. My tracker prototypes write nmea data to a file for processing later. There is no live position display, yet.

The first prototype was a Pi 2 with a USB GPS dongle and gpsd. It worked.

The next, and current, prototype is a Pi Zero W, an Adafruit Ultimate GPS HAT for Raspberry Pi, a reboot or shutdown switch, a tri-colour LED, is that and my own scripts (no gpsd).

GPS units typically output strings of nmea formatted text. I use some venerable command line text processing tools, and a little python, to process this text, write it to a file, and light an LED to display the fix status.

The scripts do what I intend, but these may not be good examples of how to program, or how to choose filenames! I wanted to script using existing command line text processing tools. I write this some months after writing the scripts. These scripts and how these interact may reveal some of the development steps I took: these might need refining.

The scripts communicate via files and file contents.

I use GPXSee to visualize the nmea data files.

Files:

gpsstatus.log

Each line: date and time, a comma, a status integer that can be 0,1, or 2.

gpsstatus.fix

A single integer that can be 0,1,2.

gpsdate.log

The gps date.

gpslogfile.log

Filename of the current file of gps data.

gps-[datetime].nmea

A file of gps data in nmea format.

crontab entries

@reboot ~/gpslogger/initializeserial.sh
@reboot ~/gpslogger/loggermonitor.sh
@reboot ~/gpslogger/gpsstartlogging.sh
@reboot ~/fixtest.py

At each boot crontab runs each script. Some loop forever, or continue forever.

Scripts

initializeserial.sh

#!/bin/bash
# initializeserial for ADAFRUIT ULTIMATE GPS HAT
stty -F /dev/serial0 raw 9600 cs8 clocal -cstopb

Runs once to initilize the ADAFRUIT ULTIMATE GPS HAT as Adafruit recommend.

loggermonitor.sh

#!/bin/bash
# monitors the gps logging file
# write new status log on running at reboot
# checks time in tail as well as status - necessary.
# 2018 Jul 16 added gpsstatus.fix - single char 0 1 or 2
echo "$(date -u +%Y%m%d%H%M%S),0" > gpsstatus.log
echo "0" > gpsstatus.fix
LASTTAIL="$(tail -1 $(cat gpslogfile.log) | awk -F , '{print $2","$7}')"
sleep 10
while true
do
    THISTAIL="$(tail -1 $(cat gpslogfile.log) | awk -F , '{print $2","$7}')"
    if [ "$THISTAIL"  != "$LASTTAIL" ]
    then
        LASTTAIL="$THISTAIL"
        echo $THISTAIL | awk -F , '{ print $1","$2}' >> gpsstatus.log
        echo $THISTAIL | awk -F , '{ print $2}' > gpsstatus.fix
    else
        echo "$(date -u +%Y%m%d%H%M%S),0" >> gpsstatus.log
        echo "0" > gpsstatus.fix
    fi
    sleep 10
done
  1. Nothing happend so far so:
    1. Write a date and time, and a status of 0 to gpsstatus.log.
    2. Write 0 to gpsstatus.fix.
  2. Read the last line of yet another file, gpslogfile.log and use awk to put 2 fields from the line in the variable LASTTAIL.
  3. We wait for 10 seconds.
  4. Start the loop until the end of time!
    1. Read the last line of gpslogfile.log again and use awk to put 2 fields from the line in the variable THISTAIL.
    2. Compare THISTAIL with LASTTAIL. If these are different it means that we are logging gps data, good!
      1. Make LASTTAIL the same as THISTAIL.
      2. Append contents of THISTAIL to gpsstatus.log
      3. Overwrite gpsstatus.fix with the current fix status.
    3. If THISTAIL and LASTTAIL are identical then we:
      1. Write a date and time, and a status of 0 to gpsstatus.log.
      2. Write 0 to gpsstatus.fix.
  5. Keep looping forever.

gpsstartlogging.sh

#!/bin/bash
# logs nmea data from when gpshhmmss available
# V0.0
# grep -v ,,, so we do not log useless nmea output
# sleep to give time for a lock
sleep  20
# timestamp for nmea filename
# We only take date no time because logger gets own time from gps
RAWGPSDATE="$(cat /dev/serial0 | stdbuf -oL grep RMC | head -1 | awk -F , '{ print $10 }')"
GPSDATE="20${RAWGPSDATE:4:2}${RAWGPSDATE:2:2}${RAWGPSDATE:0:2}"
echo $GPSDATE > ~/gpsdate.log
# gets gpshhmmss from gps serial0 stream
GPSHHMMSS="$(cat /dev/serial0 | stdbuf -oL grep GGA | head -1 | awk  -F "," '{ print $2}' | awk -F "." '{print $1}')"
GPSLOGFILE="gps-$GPSDATE$GPSHHMMSS.nmea"
echo $GPSLOGFILE > ~/gpslogfile.log
# logs timestamped nmea strings to file
# does not log data with missing fields ,,,
cat /dev/serial0 | stdbuf -oL grep GGA | stdbuf -oL grep -avE ',,,|,[0-9]{6},'  >> "$GPSLOGFILE" &
  1. Waits for an RMC nmea string and extracts the date.
  2. Write the date to gpsdate.log, but I cannot remember why!
  3. Waits for a GGA nmea stirng and extracts the time.
  4. Builds the gpslog file filename using the RMC date and GGA time.
  5. Write the filename to gpslogfile.log.
  6. Appends (writes) received GGA nmea strings to the .nmea file. This last process continues until shutdown, reboot, or the end of time, whichever is sooner. The process continues because /dev/serial0 is a stream of data and cat will keep processing the stream until it receives an EOF (end of file).

fixtest.py

#!/usr/bin/env python3

from gpiozero import LED
from time import sleep
from os import system

# Set the rgbled to the correct pins
redled = LED(16)
greenled = LED(13)
blueled = LED(12)
# set some varibles
searching = 0
fix = 1
dgps = 2
fixstatus = 0

def getfixstatus():
# reads the fix status
    try:
        fixstatusfile = open("gpsstatus.fix","r")
        fixstatusread = fixstatusfile.read()
#    print("-"+fixstatusread)
        fixstatusfile.close()
    except:
        fixstatusread = "0"

    try:
        fixstatus = int(fixstatusread)
    except:
        fixstatus = 0

    return fixstatus

while True:
#    fixstatusfile = open("gpsstatus.fix","r")
#    fixstatus = int(fixstatusfile.read())
#    fixstatusfile.close()
    fixstatus=getfixstatus()
#    print(fixstatus)
# We have a fix, dgps, or not so blink LED once
    if (fixstatus == fix):
        greenled.blink(n=1)
#        print("fix")
    elif (fixstatus == dgps):
        blueled.blink(n=1)
#        print("dgps")
    else:
        redled.blink(n=1)
#        print("searching")
# sleep to save mAh
    sleep(5)

The clue is in the name of this script; just a test script.

  1. The function getfixstatus(): reads and returns the status of the fix from gpsstatus.fix.
    1. If that fails then we return a fix status of 0: no fix.
  2. Loops until forever:
    1. Blinks an LED of colour Green (fix), Blue (dgps fix), or Red (no fix)
  3. Sleeps for 5 seconds to save power.

I used some of the commented out instructions to debug the script. I use a tri-colour LED to display fix status, but this script would also work for 3 individual LEDs.

gpsstoplogging.sh

#!/bin/bash
# stops gps logging by  killall cat - so stops all cat!
#
killall cat

Not the most elegant of solutions, but it stops my running scripts.