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
-
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.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.
-
Valve and Arch Linux Announce Collaboration
Valve and Arch have come together for two projects that will have a serious impact on the Linux distribution.
-
Hacker Successfully Runs Linux on a CPU from the Early ‘70s
From the office of "Look what I can do," Dmitry Grinberg was able to get Linux running on a processor that was created in 1971.