A script to log what my GPS tells me
By Bob Mesibov, published 17/01/2017 in Tutorials
I carry a Garmin Etrex 10 when I go bug-collecting. It's a handheld GPS receiver without many frills, and I only use it to record the latitude and longitude of each of my collecting sites as a waypoint.
At the start of a field trip I delete all previous waypoints. When I do this, the trip's waypoints are then serially numbered from 001 and stored in the Etrex 10 in a file with a name like Waypoints_14-NOV-16.gpx.
When I get home, I connect the Etrex 10 to my computer with a micro-USB/USB cable, mount GARMIN (/media/bob/GARMIN) and navigate to the Garmin/GPX/ folder to find the trip's GPX file. I copy that file to the clipboard (actually, just the file path) by choosing Copy on the right-click menu in my file manager. I then use a keyboard shortcut to launch my gpslogger script, which is explained in this article. The trip's waypoints are automatically appended to a GPS log file in my preferred format (see below) and a notification appears on my desktop, like this:
Horizontal fibs
An example of the waypoint GPX file I've copied looks like this:
and each waypoint is marked up like this:
The bits I want from that string are latitude (lat), longitude (lon), date/time (time) and waypoint serial number (name). The last two are correct. The latitude and longitude are wild exaggerations.
I say that because the decimal degree locations are given to 6 significant figures. Anywhere on Earth, 0.000001 of a degree of latitude is about 110 mm, and 0.000001 of a degree of longitude is about 80 mm at 42 degrees south. Does my Etrex 10 really know its location to the nearest 110 mm, or ±55 mm in latitude?
No, of course not. Garmin says their Etrex receivers are typically accurate to within 10 metres. "Typical" rarely applies in my case, since I'm usually working in forest on hilly country, without a clear view of the sky. If I let the Etrex 10 listen to the GPS satellites for a couple of minutes, I can often get an accuracy declaration for a waypoint of less than 10 m. But even 5 m accuracy is a long way from 0.000001 degree accuracy.
Then there's the matter of precision. A GPS reading is just one estimate of a location. A better estimate would be the average of several readings at the same spot. How many? Garmin says about waypoint averaging: For best results, collect four to eight samples for the waypoint, waiting at least 90 minutes between samples (Garmin Etrex 10 Quick Start Manual).
OK, so lat="-41.810292" lon="147.761722" is dodgy and those figures need to be rounded off. Garmin rounds them off on the screen of the Etrex 10 to 0.00001 of a degree, but that's still too many significant figures. A more realistic rounding off is to 0.0001 of a degree, which in my latitudes here in Tasmania is roughly ±5.5 m in the N/S direction and ±4 m in the E/W direction. That's the rounding off done by the gpslogger script, as shown below.
Vertical porkies
GPS elevations are even dodgier. In the sample waypoint above, the elevation ("ele") was calculated by the Etrex 10 to be 254.520554 metres. That would get rounded off on-screen to the nearest metre, and users might think that 255 was an OK estimate of elevation above sea level for that waypoint.
Not so. A rule of thumb is that GPS vertical uncertainty is 1.5 times GPS horizontal uncertainty. If I only know the horizontal location ±10 m, I only know the vertical location ±15 m, not
±0.5 m. But that's irrelevant, because GPS elevation isn't elevation above sea level. It's elevation above a mathematical model of the Earth's surface called an "ellipsoid". The difference between GPS elevation and elevation above sea level can be tens of metres.
So I ignore the elevation in the GPX file. To get elevations above sea level for my sites in Tasmania, I plug the latitude and longitude for each waypoint into the Tasmanian government's excellent online mapper, LISTmap, and read the approximate elevation off a digital topographic map with 10 m contour lines, to the nearest 10 m.
Processing the waypoint data
The script (shown below in full) uses xclip to grab the GPX filepath from the clipboard. The file is catted and sent first to tr to put the whole file on a single line. The file actually is on a single line in the GPX, but I like to make sure.
The next command in the pipeline is an AWK one. A simplified version of this first AWK command works by breaking the one-line GPX file into separate records. The input record separator RS is set to the string "<wpt", and the output record separator ORS to a newline. The result is shown below; I've double-spaced it with sed to emphasise the separate lines:
In the full version of the first AWK command, this result is then filtered to select just the lines with the string "lat", in this case the last 3 lines. I then define field separators on each line with the FS variable, using a number of strings separated by the OR operator "|". This breaks up each "lat" line into fields as shown:
FS='"|<time>|</time>|<name>|</name>'
I print the fields I want in the order I want, then separate them with a tab as defined by the output field separator variable, OFS:
Adjusting the waypoint data
Now for the rounding of latitudes and longitudes, and as described in an earlier Linux Rain article, that's not so easy! For clarity I pass the lat/lons through 2 separate, fairly ugly AWK commands to do the rounding I like. Note that the first rounding command accounts for the fact that the latitude is negative. The rounded result is saved as a temp file:
The last adjustment is to convert the UTC date/time in the GPX file to local date/time. I do this on the last column of the 4-column table with the date command and its -f option, which allows date to do the converting line by line. The converted date/time column is rejoined to the "name" and rounded latitude and longitude columns with paste:
Finishing up
The set of waypoints from the field trip is appended to my GPS log file, and a desktop notification created to tell me how many waypoints were appended. To get that number I compare the number of lines in the GPS log before and after the appending.
And here's the full script:
#!/bin/bash
before=$(wc -l < ~/Millipedes/Sites/gpslog)
cat $(xclip -selection clipboard -o) | tr -d '\n' \
| awk -v RS='<wpt' -v ORS="\n" -v FS='"|<time>|</time>|<name>|</name>' -v OFS="\t" \
'/lat/ {print $8,$2,$4,$6}' \
| awk -F"\t" '{if (substr($2,0,8)%2) printf("%s\t%.4f\t%s\t%s\n",$1,$2-0.00001,$3,$4); \
else printf("%s\t%.4f\t%s\t%s\n",$1,$2,$3,$4)}' \
| awk -F"\t" '{if (substr($3,0,8)%2) printf("%s\t%s\t%.4f\t%s\n",$1,$2,$3+0.00001,$4); \
else printf("%s\t%s\t%.4f\t%s\n",$1,$2,$3,$4)}' > /tmp/gpx
paste <(cut -f1-3 /tmp/gpx) <(date +"%A, %d %b %Y, %H:%M" -f <(cut -f4 /tmp/gpx)) \
>> ~/Millipedes/Sites/gpslog
after=$(wc -l < ~/Millipedes/Sites/gpslog)
adds=$(($after-$before))
notify-send "$adds waypoints added to GPS log file"
rm /tmp/gpx
exit