grimbough / FITfileR

R package for reading data from FIT files using only native R code, rather than relying on external libraries.
https://msmith.de/FITfileR
50 stars 13 forks source link

Missing variables #39

Closed sBrook3 closed 5 months ago

sBrook3 commented 5 months ago

Hello,

Thankyou for building and sharing this, its been very useful. I think probably the issue I'm having is lacking experience with R, but I've tried reading a number of different fit files (from different types of bike computer), and in all of them I'm seeing a much reduced range of variables than I can see when converting them to csv first or viewing through eg. training peaks. I can do without most of it but unfortunately torque is missing and I need that for what I'm trying to do.

Here's what I'm doing:

new_ride<-readFitFile('/Users/sarah/Downloads/new_project.fit') 
new_ride_records<-records(new_ride)%>% 
                  bind_rows()%>% 
                  arrange(timestamp)

which gives me

str(new_ride_records)

## tibble [7,464 × 20] (S3: tbl_df/tbl/data.frame)
##  $ timestamp                   : POSIXct[1:7464], format: "2024-03-21 15:31:20" "2024-03-21 15:31:21" ...
##  $ position_lat                : num [1:7464] NA 51.7 51.7 51.7 51.7 ...
##  $ position_long               : num [1:7464] NA -0.0629 -0.0629 -0.0629 -0.0629 ...
##  $ gps_accuracy                : int [1:7464] NA 7 7 7 5 5 5 5 5 5 ...
##  $ altitude                    : num [1:7464] NA NA NA NA NA NA NA NA NA NA ...
##  $ distance                    : num [1:7464] NA 0 5.57 11.67 17.65 ...
##  $ heart_rate                  : int [1:7464] 99 100 100 100 99 99 98 98 98 98 ...
##  $ calories                    : int [1:7464] 0 0 0 0 0 0 0 0 0 0 ...
##  $ cadence                     : int [1:7464] 0 2 82 83 84 86 86 86 87 51 ...
##  $ speed                       : num [1:7464] NA 5.85 5.92 6.1 5.89 ...
##  $ power                       : int [1:7464] NA NA 158 133 110 104 90 82 69 34 ...
##  $ left_right_balance          : int [1:7464] NA NA 57 58 55 54 49 54 50 56 ...
##  $ combined_pedal_smoothness   : num [1:7464] NA NA 51.5 50 49.5 51.5 55 47 46 48 ...
##  $ temperature                 : int [1:7464] NA NA NA NA 19 19 19 19 19 19 ...
##  $ grade                       : num [1:7464] NA NA NA NA NA NA NA NA NA NA ...
##  $ ascent                      : int [1:7464] NA NA NA NA NA NA NA NA NA NA ...
##  $ descent                     : int [1:7464] NA NA NA NA NA NA NA NA NA NA ...
##  $ battery_soc                 : num [1:7464] NA 82 NA NA NA NA NA NA NA NA ...
##  $ total_hemoglobin_conc       : num [1:7464] NA NA NA NA NA NA NA NA NA NA ...
##  $ saturated_hemoglobin_percent: num [1:7464] NA NA NA NA NA NA NA NA NA NA ...

and the session Info:

sessionInfo()

R version 4.3.0 (2023-04-21)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS 14.4.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/London
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_1.1.4    FITfileR_0.1.8

loaded via a namespace (and not attached):
 [1] bit_4.0.5         compiler_4.3.0    promises_1.2.0.1  tidyselect_1.2.1  Rcpp_1.0.10       leaflet_2.2.2     later_1.3.1       yaml_2.3.7        fastmap_1.1.1     mime_0.12         R6_2.5.1          generics_0.1.3   
[13] knitr_1.43        htmlwidgets_1.6.2 tibble_3.2.1      shiny_1.8.0       pillar_1.9.0      rlang_1.1.3       utf8_1.2.4        httpuv_1.6.11     xfun_0.39         bit64_4.0.5       cli_3.6.2         withr_3.0.0      
[25] magrittr_2.0.3    crosstalk_1.2.0   digest_0.6.31     rstudioapi_0.14   xtable_1.8-4      lifecycle_1.0.4   vctrs_0.6.5       evaluate_0.21     glue_1.7.0        fansi_1.0.6       rmarkdown_2.22    tools_4.3.0      
[37] pkgconfig_2.0.3   ellipsis_0.3.2    htmltools_0.5.5  

Thanks again, and any suggestions you can offer would be much appreciated! Sarah

grimbough commented 5 months ago

Can you share a file? In my experience FIT files contain quite a bit of undocumented data, and I choose to throw away anything that I don't know what it is. I think the most likely thing is that torque is somehow being discarded internally, which I should be able to fix with an example file to debug.

sBrook3 commented 5 months ago

new_project.fit.zip It wouldn't let me upload a .fit directly so hopefully that will work.

thanks

grimbough commented 5 months ago

Thanks for that file. Can I ask what you use to convert to CSV? I've tried a few tools like Garmin's FitCSVTool and the perl fitdump tool, but there doesn't seem to be any mention of torque in the files those tools create.

sBrook3 commented 5 months ago

I'm using this: https://bluecattechnical.shinyapps.io/EnDuRA/ you have to create a login but its free.

grimbough commented 5 months ago

Thanks for the link. My guess is that they are calculating the torque value using the power and rpm data that are already in the file using the following equation:

Torque (nM) = Power (W) ∗ 60 / (2 ∗ pi ∗ RPM)

Here's some code that get's a plot similar to that produced by EnDuRA:

library(FITfileR)
library(dplyr)
library(ggplot2)
library(lubridate)
library(hms)

## download and extract the example file
destfile = tempfile(fileext = ".zip")
url = "https://github.com/grimbough/FITfileR/files/15011268/new_project.fit.zip"
dl = download.file(url = url, destfile = destfile)
example_file = unzip(destfile, exdir = tempdir())

## read and extract the records
fitFile <- readFitFile(example_file[1])

laps <- laps(fitFile) %>%
  bind_rows() %>%
  arrange(timestamp)

lap_intervals <- interval(laps$start_time, laps$timestamp)

records <- records(fitFile) %>%
  bind_rows()%>% 
  arrange(timestamp)

records <- mutate(records, 
                  ## find the time elapsed since the 
                  elapsed_time = as_hms(timestamp - min(timestamp)),
                  ## calculate the torque at each second
                  torque = (power * 60) / (2 * pi * cadence),
                  ## assign each record to a lap - this is a bit slow
                  lap = as.factor(
                      sapply(timestamp, FUN = \(x) which(x %within% lap_intervals)[1])
                    )
                  )

## create a plot
ggplot(records, aes(x = elapsed_time, y = torque)) +
  geom_line(aes(color = lap)) +
  scale_x_time() +
  theme_minimal() +
  ylim(0,100)

image

sBrook3 commented 5 months ago

Thanks so much for digging into that. I'd completely assumed the torque would be coming directly from the power meter. I've tried it on a few rides and it seems to work.

Thanks!