-
-
Save anamariaelek/171b9a237ac97f42344ee78539b5c0a6 to your computer and use it in GitHub Desktop.
Modify HR zone boundaries for existing Garmin activity (export .fit file from Garmin Connect and convert it to .csv file using fitcsvtool)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env Rscript | |
library(data.table) | |
library(stringr) | |
# run example: | |
# Rscript --vanilla hr.R 99,119,139,158,178,198 ACTIVITY.csv | |
# params: | |
# new upper boundaries for HR zones | |
# it should be a vector of 6 integers separated by commas, e.g. | |
# "99,119,139,158,178,198" | |
# input csv file as exported by fitcsvtool, | |
# e.g. ACTIVITY.csv generated by: | |
# java -jar FitCSVTool.jar ACTIVITY.fit ACTIVITY.csv | |
# (optional) output filename, if not specified, it will use original file name | |
# with ".mod" suffix, e.g. ACTIVITY.mod.csv | |
# Returns the new csv file whch you can then convert to fit binary file | |
# using fitcsvtool, e.g. | |
# java -jar FitCSVTool.jar -c ACTIVITY.mod.csv ACTIVITY.mod.fit | |
# test if there is at least two arguments | |
args <- commandArgs(trailingOnly = TRUE) | |
if (length(args) < 2) { | |
stop( | |
"At least two arguments must be supplied: | |
upper HR zone boundaries and input csv file\n", | |
call. = FALSE | |
) | |
} | |
if (length(args) == 2) { | |
# default output file file | |
args[3] <- stringr::str_replace(args[1], ".csv", ".mod.csv") | |
} | |
hv <- args[1] | |
fn <- args[2] | |
fo <- args[3] | |
# import csv file | |
dt <- fread(fn, fill = TRUE) | |
# set new boundaries | |
hr_bounds_v <- as.integer(strsplit(hv, split = ",")[[1]]) | |
hr_bounds <- paste(hv, collapse = "|") | |
dt[Type == "Data" & Message == "time_in_zone", `Value 5` := hr_bounds] | |
# get time stamps | |
ts <- dt[Type == "Data"][Message == "time_in_zone"]$`Value 1` | |
# update time in zones per timestamps | |
for (i in seq_along(ts)[-length(ts)]) { | |
message(i) | |
tst <- ts[i] | |
ten <- ts[i + 1] | |
x <- dt[Type == "Data"][Message == "record"][`Field 1` == "timestamp"][ | |
`Value 1` >= tst & `Value 1` <= ten | |
]$`Value 1` | |
t <- as.integer(x[-1]) - as.integer(x[-length(x)]) | |
h <- dt[Type == "Data"][Message == "record"][`Field 1` == "timestamp"][ | |
`Value 1` >= tst & `Value 1` < ten | |
]$`Value 8` | |
z <- cut(h, c(0, hv, Inf)) | |
s <- tapply(c(t, rep(1, pmax(0, length(z) - length(t)))), z, sum) | |
s[is.na(s)] <- 0 | |
dt[Type == "Data" & Message == "time_in_zone" & `Value 1` == tst, | |
`Value 2` := paste(s, collapse = "|") | |
] | |
} | |
# uppdate total time in zones | |
zs <- dt[Type == "Data" & Message == "time_in_zone"]$`Value 2` | |
zm <- sapply(zs, function(x) { | |
sapply(strsplit(x, split = "\\|")[[1]], as.integer) | |
}, USE.NAMES = FALSE) | |
rownames(zm) <- NULL | |
zt <- paste(apply(zm, 1, sum), collapse = "|") | |
dt[Type == "Data" & Message == "time_in_zone" & `Value 1` == ts[length(ts)], | |
`Value 2` := paste(s, collapse = "|") | |
] | |
# save new csv file | |
fwrite(dt, fo, sep = ",", col.names = TRUE) | |
message("Done, saved file ", fo) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment