Skip to contents

Welcome to the fine-scale-tracking vignette! Thanks for taking some time to get to know our package; we hope it sparks joy.

This practical covers how to compute a dead-reckoned track from accelerometer and magnetometer data and compares this track estimate to GPS positions that were taken by the tag. This illustrates both the high resolution of dead-reckoned tracks and the very large errors that can accumulate. You will then explore how to merge the GPS and dead-reckoned tracks to produce a high-resolution track with lower error and try visualizing the track coloured by another parameter.

Estimated time for this vignette: 40 minutes.

These practicals all assume that you have R/Rstudio installed on your machine, some basic experience working with them, and can execute provided code, making some user-specific changes along the way (e.g. to help R find a file you downloaded). We will provide you with quite a few lines. To boost your own learning, you would do well to try and write them before opening what we give, using this just to check your work.

Additionally, be careful when copy-pasting special characters such as _underscores_ and ‘quotes’. If you get an error, one thing to check is that you have just single, simple underscores, and 'straight quotes', whether 'single' or "double" (rather than “smart quotes”).

Load and Explore Data

If you want to run this example, download the “testset7.nc” file from https://github.com/animaltags/tagtools_data and change the file path to match where you’ve saved the files

Load the data set testset7.nc. This dataset is built into the tagtools package, so you can access it using system.file.

Then, check the contents of the data structures to answer the following questions. (See the code below if and only if you need a hint.)

  • What species did the data come from, and where did the data come from?

A humpback whale, Megaptera novaeangliae.

  • What is the sampling rate of the accelerometer data?

1 Hz.

  • What processing steps have already been applied to the magnetometer data?

sens_struct, do_cal, tag2animal, crop_to, decdc(5)… So, it has been converted to a sensor structure, calibrated, put into the animal frame, cropped to a certain segment of data, and decimated by a factor of five (as a result, the sampling rate is now the same as the acceleration data, 1 Hz).

  • What is in the 3 columns in the POS (GPS position) data?

The three columns are the time, the latitude, and the longitude.

  • Which frame is the accelerometer data in? Which frame is the magnetometer data in?

The accelerometer data is oriented in the animal frame, and so is the magnetometer data.

str(d7$info, max.level = 1)
#> List of 35
#>  $ depid                        : chr "mn18_175d"
#>  $ dtype_source                 : chr "mn18_175d001,mn18_175d002,mn18_175d003,mn18_175d004,mn18_175d005,mn18_175d006,mn18_175d007,mn18_175d008,mn18_17"| __truncated__
#>  $ dtype_datetime_made          : chr "2018/07/05 15:25:29"
#>  $ dtype_nfiles                 : num 18
#>  $ dtype_format                 : chr "DTG"
#>  $ device_serial                : num 1.88e+09
#>  $ device_make                  : chr "DTAG"
#>  $ device_type                  : chr "Archival"
#>  $ device_model                 : chr "DTAG4"
#>  $ device_url                   : chr "https://www.soundtags.org"
#>  $ sensors_firm                 : chr "Not specified"
#>  $ sensors_soft                 : chr "Not specified"
#>  $ sensors_list                 : chr "3axis Accelerometer,3 axis Magnetometer,Pressure,GPS,Sound"
#>  $ animal_id                    : chr "Unknown"
#>  $ animal_species_common        : chr "Humpback whale"
#>  $ animal_species_science       : chr "Megaptera novaeangliae"
#>  $ animal_dbase_url             : chr "http://www.arkive.org/humpback-whale/megaptera-novaeangliae/"
#>  $ dephist_device_tzone         : chr "0"
#>  $ dephist_device_regset        : chr "yyyy/mm/dd HH:MM:SS"
#>  $ dephist_device_datetime_start: chr "2018/06/24 11:14:24"
#>  $ dephist_deploy_locality      : chr "Cape Cod, Massachusetts"
#>  $ dephist_deploy_location_lat  : chr ""
#>  $ dephist_deploy_location_lon  : chr ""
#>  $ dephist_deploy_datetime_start: chr "2018/06/24 11:14:24"
#>  $ dephist_deploy_method        : chr "suction cup"
#>  $ project_name                 : chr "ONR Tag Design"
#>  $ project_datetime_start       : chr ""
#>  $ project_datetime_end         : chr ""
#>  $ provider_name                : chr "Susan Parks"
#>  $ provider_details             : chr "Univ Syracuse"
#>  $ provider_email               : chr "sparks@syr.edu"
#>  $ provider_license             : chr "Contact data provider"
#>  $ provider_cite                : chr "Contact data provider"
#>  $ provider_doi                 : chr "Contact data provider"
#>  $ creation_date                : chr "06-Dec-2019 10:34:49"
str(d7$Aa, max.level = 1)
#> List of 23
#>  $ data              : num [1:10756, 1:3] -0.725 -1.73 -1.221 -1.632 -2.85 ...
#>  $ sampling          : chr "regular"
#>  $ sampling_rate     : num 1
#>  $ sampling_rate_unit: chr "Hz"
#>  $ depid             : chr "mn18_175d"
#>  $ creation_date     : chr "09-Jul-2018 12:06:55"
#>  $ history           : chr "sens_struct,do_cal,tag2animal,crop_to,decdc(5)"
#>  $ type              : chr "acc"
#>  $ full_name         : chr "Acceleration"
#>  $ description       : chr "triaxial acceleration"
#>  $ unit              : chr "m/s2"
#>  $ unit_name         : chr "metres per second squared"
#>  $ unit_label        : chr "m/s^2"
#>  $ start_offset      : num 0
#>  $ start_offset_units: chr "seconds"
#>  $ column_name       : chr "x,y,z"
#>  $ frame             : chr "animal"
#>  $ axes              : chr "FRU"
#>  $ cal_poly          : num [1:6] 77.4183 77.7662 77.7547 -0.0921 -0.0762 ...
#>  $ cal_map           : num [1:9] 0 -1 0 -1 0 0 0 0 1
#>  $ otab              : num [1:5] 0 0 -0.855 0.157 1.902
#>  $ crop              : num [1:2] 91265 102021
#>  $ crop_units        : chr "seconds"
str(d7$POS, max.level = 1)
#> List of 20
#>  $ data              : num [1:63, 1:3] 0.827 12.058 338.364 698.659 738.046 ...
#>  $ sampling          : chr "irregular"
#>  $ sampling_time     : chr "column 1"
#>  $ sampling_time_unit: chr "second"
#>  $ depid             : chr "mn18_175d"
#>  $ creation_date     : chr "05-Jul-2018 15:25:54"
#>  $ history           : chr "sens_struct,crop_to"
#>  $ type              : chr "pos"
#>  $ full_name         : chr "Position"
#>  $ description       : chr "GPS position"
#>  $ unit              : chr "degrees"
#>  $ unit_name         : chr "decimal degrees"
#>  $ unit_label        : chr "degrees"
#>  $ start_offset      : num 0
#>  $ start_offset_units: chr "seconds"
#>  $ column_name       : chr "time,lat,long"
#>  $ frame             : chr "WGS84"
#>  $ axes              : chr "NE"
#>  $ crop              : num [1:2] 91265 102021
#>  $ crop_units        : chr "seconds"

Plot the pressure and accelerometer data with plott() to get a sense for what the animal might be doing in this data segment. Note that the code example below assumes you have called the data set d7:

library(tagtools)
d7_file_path <- ""
d7 <- load_nc(d7_file_path)
sampling_rate <- d7$P$sampling_rate
plott(X = list(Depth = d7$P, Acceleration = d7$Aa), fsx = sampling_rate, interactive = TRUE)

Then, plot the GPS positions:

plot(d7$POS$data[,3], d7$POS$data[,2], type = 'b', pch = 20)

Dead-reckoning

What is Dead-reckoning?

Hearken back to your earliest trigonometry course. Chances are, you used angles and distances to calculate other distances (on a right triangle, especially). Then, in calculus, you’ve probably used angles and rates of change to calculate distance or other rates of change. Dead-reckoning is essentially this—using just a heading and a forward speed in order to plot a forward path. The technique is fairly commonplace in sailing and aviation, and can be made accurate with the help of known position data. If you want more background, consult this video for an example with some simple trigonometry.

However, per Wikipedia (which simply says it very well), dead-reckoning “is subject to significant errors of approximation. For precise positional information, both speed and direction must be accurately known at all times during travel. Most notably, dead reckoning does not account for directional drift during travel through a fluid medium. These errors tend to compound themselves over greater distances”. We will see how this is true over the course of this vignette—water is seldom entirely still, and there is quite a current present in the data we will soon investigate.

Why use Dead-reckoning?

The plot shows a mix of intensive and extensive movements, but the constraint of only getting positions when the animal is at the surface means we cannot infer much about the movement behaviour within dives. Dead-reckoning from accelerometer and magnetometer data is the only way to estimate movement within dives without requiring external tracking infrastructure.

Estimating Animal Speed

Dead-reckoning uses the accelerometer and magnetometer to calculate the direction of travel. In what frame do these data need to be?

They both need to be in the animal frame. Thankfully, they both already are!

An estimate of the forward speed is also required. We don’t have a speed sensor—doing this would actually be quite cumbersome, as compared to an acceleration sensor. Nevertheless, we can compute the vertical speed (i.e., the differential of the depth) during descents and ascents, which might be a good starting guess:

v <- depth_rate(d7$P)
plott(X = list(Depth = d7$P$data, `Vertical Speed` = v), 
      fsx = sampling_rate)

Set interactive = TRUE and zoom in to individual dives on this plot to get a rough idea of the descent and ascent speed of the whale.

plott(X = list(Depth = d7$P$data, `Vertical Speed` = v), 
       fsx = sampling_rate, 
      interactive = TRUE)