Reading data from GPS devices
Training Support
With a small GPS receiver on his wrist, Mike has been jogging through San Francisco neighborhoods. While catching his breath, safe at home, he visualizes the data he acquired while running with Perl.
A few years ago, portable GPS devices looked more like the clunky cellphones of the early 1990s. Today, athletes no longer need to drag along that much extra weight, as devices like the Garmin Forerunner 10 [1] have shrunk to the size of digital LED watches from the 1970s (Figure 1). These ultimate sports accessories log geographic coordinates during runs.
Thus, runners can see how fast they are currently traveling and whether they need to speed up or slow down to achieve their own time goals. After completing all of this muscular activity, runners can then enjoy the experience of logging new speed records, viewing the running route on a map, reviewing the miles traveled, or marveling at an altitude profile of the route.
After plugging the Garmin 10 device into a USB port on my Ubuntu machine (Figure 1), the Linux kernel immediately detects the device as a storage unit and mounts the files stored on the device under /media/GARMIN
. However, to do this, I did need a special adapter cable that clings to the GPS watch like a creature from the movie Alien and that came with the Garmin package. In the GARMIN/ACTIVITY
directory, Linux users will find the FIT files in which the manufacturer stores motion activities in a proprietary binary format.
Athletes can upload these files to a newly created account on the Garmin website [2] and then view a presentation of the run data (Figure 2). The example shows an approximately five-mile route around Lake Merced – a lake near the Pacific Ocean just south of San Francisco – which I ran for this Perl column in about 40 minutes.
The graph shows total time, average time per mile (8:45 minutes), and elevation change (300 feet). If you prefer metric units, you can change to kilometers and meters on the website.
The Six-Second Cycle
The GPS device determines the runner's position approximately every six seconds with the help of earth-orbiting navigation satellites; it then stores the data points as geographical latitudes and longitudes and measures the current speed and distance traveled.
Unlike other GPS units, the Garmin 10 does not determine the current altitude above sea level during the run; apparently, this would require more expensive hardware to get right. But the Garmin website effortlessly fills this topographic gap in the movement profile based on the coordinates, using a server-side static elevation profile that probably knows the altitude of any inhabited part of the earth. Unless you are running up the stairs in a high rise, you will receive accurate altitude information for your run in this way.
Do It Yourself
Even the neatest website can be improved; some people want to remodel their data for seemingly esoteric purposes. Instead of painstakingly decrypting the proprietary format, you can pick up the Garmin SDK online [3]. Although the SDK does not define a Perl API, it does document all the data structures used in the log data.
On the basis of this information, developer Kiyokazu Suto built a Perl module [4] under a public domain-like license, but so far it has not been uploaded to CPAN. From the developer's website, you can download Garmin::FIT, which reads FIT files; Figure 3 shows an example of how the fitdump
utility included with the module outputs the data.
Despite the format having been disclosed, extracting the data from the binary blob proves to be a Sisyphean task. The Garmin::FIT module offers the print_all_fields()
method, which I leveraged to hash up the dumper in Listing 1 [5]; it produces the output in Figure 3. To process the data, developers need to dig deeper and write their own functions.
Listing 1
fittest
Extracting from FIT
Listing 2 thus takes on the task of reshaping the FIT data and producing an easily readable YAML file. To do this, line 14 loads a FIT file passed in at the command line into the Garmin::FIT module. The goal of the subsequent procedure is to create a Perl array with the data from the record
entries in the FIT file and then call the DumpFile()
function from the YAML module to dump them as YAML data into a .yaml
file of the same name.
Listing 2
fit2yaml
To search the FIT data, line 19 calls the data_message_callback_by_name()
method and sets up a callback that the FIT parser invokes for each entry found. The callback function message
defined beginning in line 38 extracts the important values from the given parameters and puts them together to create a new data structure.
As Figure 4 shows, the current total run distance does not reside directly in the distance
entry of the variable $v
presented to the callback. Instead, the data structure $desc
contains a number of values that the user must combine in mysterious ways to arrive at the desired result. To get the total distance, for example, the i_distance
key in $desc
contains an array index number (the number 4 in this case), which can be used to extract the desired total distance from the $v
array as $v[4]
. Then, fit2yaml
combines this with the unit m
for meters and determines the result with the Garmin::FIT value_cooked()
method.
The method still needs the value for a_distance
(scaling and units) and the value for I_distance
(validity scope) so that it can finally arrive at the desired value for the distance. Because the format can store all kinds of data in a tiny space, this method was most likely chosen to save space.
For simplicity's sake, Listing 2 does not attempt to handle all the supported data formats but focuses on the record
entries with the run data sampled every six seconds. Other logged events, such as where and when the runner pressed the Start button, are ignored to limit the scope of the script.
Figure 5 shows the finished YAML data: a list of records that each contain a distance
field (distance traveled from the start in kilometers), position_lat
(latitude), position_long
(longitude), speed
(in meters per second), and timestamp
(current time).
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
SUSE Renames Several Products for Better Name Recognition
SUSE has been a very powerful player in the European market, but it knows it must branch out to gain serious traction. Will a name change do the trick?
-
ESET Discovers New Linux Malware
WolfsBane is an all-in-one malware that has hit the Linux operating system and includes a dropper, a launcher, and a backdoor.
-
New Linux Kernel Patch Allows Forcing a CPU Mitigation
Even when CPU mitigations can consume precious CPU cycles, it might not be a bad idea to allow users to enable them, even if your machine isn't vulnerable.
-
Red Hat Enterprise Linux 9.5 Released
Notify your friends, loved ones, and colleagues that the latest version of RHEL is available with plenty of enhancements.
-
Linux Sees Massive Performance Increase from a Single Line of Code
With one line of code, Intel was able to increase the performance of the Linux kernel by 4,000 percent.
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.