Racket/TSS: Skillnad mellan sidversioner
Från Täpp-Anders
Hoppa till navigeringHoppa till sök
Anders (diskussion | bidrag) Skapade sidan med '<pre> #lang racket ;;;;;;;;;;; ;; Anders Sikvall Sommarhack 2026 ;; 2026-07-09 <anders@sikvall.se> ;; ;; Använder "TrainingPeaks" metod för beräkningar av de olika parametrarna ;; baserade på Training Stress Score (TSS) som i sin tur ger de olika ;; måtten enligt: ;; ;; TSS -- Training stress score, belastningsmått för motionsaktiviteter ;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid ;; ATL -- Acuste Training Level, TSS uts...' |
Anders (diskussion | bidrag) Ingen redigeringssammanfattning |
||
| Rad 1: | Rad 1: | ||
<pre> | <pre>#lang racket | ||
#lang racket | |||
;;;;;;;;;;; | ;;;;;;;;;;; | ||
| Rad 7: | Rad 6: | ||
;; | ;; | ||
;; Använder "TrainingPeaks" metod för beräkningar av de olika parametrarna | ;; Använder "TrainingPeaks" metod för beräkningar av de olika parametrarna | ||
;; baserade på Training Stress Score (TSS) som i sin | ;; baserade på Training Stress Score (TSS) som i sin ur ger de olika | ||
;; måtten enligt: | ;; måtten enligt: | ||
;; | ;; | ||
;; TSS -- Training stress score, belastningsmått för motionsaktiviteter | ;; TSS -- Training stress score, belastningsmått för motionsaktiviteter | ||
;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid | ;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid | ||
;; ATL -- | ;; ATL -- Acute Training Level, TSS utslaget över senaste 7 dagarna | ||
;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring | ;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring | ||
(require 2htdp/batch-io) | (require 2htdp/batch-io) | ||
(require racket/date) | (require racket/date) | ||
(require plot) | |||
;; --- Konstanter och Alfa-värden --- | ;; --- Konstanter och Alfa-värden --- | ||
| Rad 28: | Rad 28: | ||
;; Datumhantering | ;; Datumhantering | ||
(define (string->date-seconds raw-str) | (define (string->date-seconds raw-str) | ||
(define clean-str (string-trim raw-str (regexp "[\"'\r\n ]"))) | (define clean-str (string-trim raw-str (regexp "[\"'\r\n ]"))) | ||
(match (regexp-match #px"([0-9]{4})-([0-9]{2})-([0-9]{2})" clean-str) | (match (regexp-match #px"([0-9]{4})-([0-9]{2})-([0-9]{2})" clean-str) | ||
[(list _ year-str month-str day-str) | [(list _ year-str month-str day-str) | ||
| Rad 39: | Rad 37: | ||
[_ (error "Felaktigt datumformat, använd YYYY-MM-DD:" raw-str)])) | [_ (error "Felaktigt datumformat, använd YYYY-MM-DD:" raw-str)])) | ||
(define (date-seconds->string secs) | (define (date-seconds->string secs) | ||
(define d (seconds->date secs)) | (define d (seconds->date secs)) | ||
| Rad 63: | Rad 60: | ||
current-ctl | current-ctl | ||
current-atl | current-atl | ||
(cons (list date-str tss current-ctl current-atl current-tsb) acc)))))) | (cons (list current date-str tss current-ctl current-atl current-tsb) acc)))))) | ||
;; --- Plottning (Standardiserad och Stabil) --- | |||
(define (plot-training-data calculated-timeline) | |||
(define-values (tss-pts ctl-pts atl-pts tsb-pts) | |||
(for/fold ([tss-acc '()] [ctl-acc '()] [atl-acc '()] [tsb-acc '()]) | |||
([day calculated-timeline]) | |||
(match-let ([(list secs date tss ctl atl tsb) day]) | |||
(values (cons (list secs tss) tss-acc) | |||
(cons (list secs ctl) ctl-acc) | |||
(cons (list secs atl) atl-acc) | |||
(cons (list secs tsb) tsb-acc))))) | |||
;; Använd enbart standardskalor och inbyggda date-ticks som bevisat fungerar | |||
(parameterize ([plot-x-ticks (date-ticks)] | |||
[plot-x-label "Datum"] | |||
[plot-y-label "Värde (TSS / CTL / ATL / TSB)"]) | |||
(plot (list (lines (reverse tss-pts) #:color "lightgray" #:width 1 #:label "TSS") | |||
(lines (reverse ctl-pts) #:color "blue" #:width 2 #:label "CTL (Kondition)") | |||
(lines (reverse atl-pts) #:color "red" #:width 2 #:label "ATL (Trötthet)") | |||
;; TSB ritas med långa streck för att sticka ut tydligt på samma skala | |||
(lines (reverse tsb-pts) #:color "forestgreen" #:width 2 #:style 'long-dash #:label "TSB (Form)")) | |||
#:title "Träningsbelastning" | |||
#:legend-anchor 'top-left))) | |||
;; --- Huvudfunktion --- | ;; --- Huvudfunktion --- | ||
| Rad 69: | Rad 90: | ||
(define raw-lines (read-csv-file input-file)) | (define raw-lines (read-csv-file input-file)) | ||
(define data-lines | (define data-lines | ||
(filter (lambda (row) | (filter (lambda (row) | ||
| Rad 77: | Rad 97: | ||
raw-lines)) | raw-lines)) | ||
(define data-map | (define data-map | ||
(for/hash ([row data-lines]) | (for/hash ([row data-lines]) | ||
(define clean-date (date-seconds->string (string->date-seconds (car row)))) | (define clean-date (date-seconds->string (string->date-seconds (car row)))) | ||
(define | (define raw-tss (string->number (string-trim (cadr row) (regexp "[\"'\r\n ]")))) | ||
(define clean-tss (if raw-tss raw-tss 0.0)) | |||
(values clean-date clean-tss))) | (values clean-date clean-tss))) | ||
(define sorted-secs | (define sorted-secs | ||
(sort (map string->date-seconds (hash-keys data-map)) <)) | (sort (map string->date-seconds (hash-keys data-map)) <)) | ||
| Rad 94: | Rad 113: | ||
(define end-secs (last sorted-secs)) | (define end-secs (last sorted-secs)) | ||
(define calculated-timeline | (define calculated-timeline | ||
(fill-and-compute-metrics data-map start-secs end-secs)) | (fill-and-compute-metrics data-map start-secs end-secs)) | ||
(define header '("Datum" "TSS" "CTL" "ATL" "TSB")) | (define header '("Datum" "TSS" "CTL" "ATL" "TSB")) | ||
(define output-rows | (define output-rows | ||
(cons header | (cons header | ||
(map (lambda (day-data) | (map (lambda (day-data) | ||
(match-let ([(list date tss ctl atl tsb) day-data]) | (match-let ([(list secs date tss ctl atl tsb) day-data]) | ||
(list date | (list date | ||
(number->string (exact->inexact tss)) | (number->string (exact->inexact tss)) | ||
| Rad 111: | Rad 128: | ||
calculated-timeline))) | calculated-timeline))) | ||
(write-file output-file | (write-file output-file | ||
(string-join | (string-join | ||
(map (lambda (row) (string-join row ",")) output-rows) | (map (lambda (row) (string-join row ",")) output-rows) | ||
"\n")) | "\n")) | ||
(printf "Klart! Data har fyllts i dag för dag och skrivits till ~a\n" output-file)) | (printf "Klart! Data har fyllts i dag för dag och skrivits till ~a\n" output-file) | ||
(plot-training-data calculated-timeline)) | |||
;; --- KÖR PROGRAMMET --- | ;; --- KÖR PROGRAMMET --- | ||
(process-training-data "training.csv" "training_calculated.csv") | (process-training-data "training.csv" "training_calculated.csv")</pre> | ||
</pre> | |||
Versionen från 9 juli 2026 kl. 19.32
#lang racket
;;;;;;;;;;;
;; Anders Sikvall Sommarhack 2026
;; 2026-07-09 <anders@sikvall.se>
;;
;; Använder "TrainingPeaks" metod för beräkningar av de olika parametrarna
;; baserade på Training Stress Score (TSS) som i sin ur ger de olika
;; måtten enligt:
;;
;; TSS -- Training stress score, belastningsmått för motionsaktiviteter
;; CTL -- Chronic Training Level, TSS utslaget över 6 veckors tid, viktat mot närtid
;; ATL -- Acute Training Level, TSS utslaget över senaste 7 dagarna
;; TSB -- Skillnaden mellan ATL-CTL, negativt betyder förbättring
(require 2htdp/batch-io)
(require racket/date)
(require plot)
;; --- Konstanter och Alfa-värden ---
(define ctl-alpha (/ 2 (+ 42 1)))
(define atl-alpha (/ 2 (+ 7 1)))
(define SECONDS-PER-DAY 86400)
(define (calc-next-ewma current-tss previous-val alpha)
(+ (* current-tss alpha) (* previous-val (- 1 alpha))))
;; Datumhantering
(define (string->date-seconds raw-str)
(define clean-str (string-trim raw-str (regexp "[\"'\r\n ]")))
(match (regexp-match #px"([0-9]{4})-([0-9]{2})-([0-9]{2})" clean-str)
[(list _ year-str month-str day-str)
(find-seconds 0 0 12
(string->number day-str)
(string->number month-str)
(string->number year-str))]
[_ (error "Felaktigt datumformat, använd YYYY-MM-DD:" raw-str)]))
(define (date-seconds->string secs)
(define d (seconds->date secs))
(format "~a-~a-~a"
(date-year d)
(if (< (date-month d) 10) (format "0~a" (date-month d)) (date-month d))
(if (< (date-day d) 10) (format "0~a" (date-day d)) (date-day d))))
;; --- Ackumulering och Utfyllnad ---
(define (fill-and-compute-metrics data-map start-secs end-secs)
(let loop ([current start-secs]
[prev-ctl 0.0]
[prev-atl 0.0]
[acc '()])
(if (> current end-secs)
(reverse acc)
(let* ([date-str (date-seconds->string current)]
[tss (hash-ref data-map date-str 0.0)]
[current-ctl (calc-next-ewma tss prev-ctl ctl-alpha)]
[current-atl (calc-next-ewma tss prev-atl atl-alpha)]
[current-tsb (- current-ctl current-atl)])
(loop (+ current SECONDS-PER-DAY)
current-ctl
current-atl
(cons (list current date-str tss current-ctl current-atl current-tsb) acc))))))
;; --- Plottning (Standardiserad och Stabil) ---
(define (plot-training-data calculated-timeline)
(define-values (tss-pts ctl-pts atl-pts tsb-pts)
(for/fold ([tss-acc '()] [ctl-acc '()] [atl-acc '()] [tsb-acc '()])
([day calculated-timeline])
(match-let ([(list secs date tss ctl atl tsb) day])
(values (cons (list secs tss) tss-acc)
(cons (list secs ctl) ctl-acc)
(cons (list secs atl) atl-acc)
(cons (list secs tsb) tsb-acc)))))
;; Använd enbart standardskalor och inbyggda date-ticks som bevisat fungerar
(parameterize ([plot-x-ticks (date-ticks)]
[plot-x-label "Datum"]
[plot-y-label "Värde (TSS / CTL / ATL / TSB)"])
(plot (list (lines (reverse tss-pts) #:color "lightgray" #:width 1 #:label "TSS")
(lines (reverse ctl-pts) #:color "blue" #:width 2 #:label "CTL (Kondition)")
(lines (reverse atl-pts) #:color "red" #:width 2 #:label "ATL (Trötthet)")
;; TSB ritas med långa streck för att sticka ut tydligt på samma skala
(lines (reverse tsb-pts) #:color "forestgreen" #:width 2 #:style 'long-dash #:label "TSB (Form)"))
#:title "Träningsbelastning"
#:legend-anchor 'top-left)))
;; --- Huvudfunktion ---
(define (process-training-data input-file output-file)
(define raw-lines (read-csv-file input-file))
(define data-lines
(filter (lambda (row)
(and (not (null? row))
(regexp-match? #px"[0-9]" (car row))
(not (string-prefix? (string-trim (car row)) "Datum"))))
raw-lines))
(define data-map
(for/hash ([row data-lines])
(define clean-date (date-seconds->string (string->date-seconds (car row))))
(define raw-tss (string->number (string-trim (cadr row) (regexp "[\"'\r\n ]"))))
(define clean-tss (if raw-tss raw-tss 0.0))
(values clean-date clean-tss)))
(define sorted-secs
(sort (map string->date-seconds (hash-keys data-map)) <))
(when (null? sorted-secs)
(error "Ingen giltig träningsdata hittades i filen."))
(define start-secs (car sorted-secs))
(define end-secs (last sorted-secs))
(define calculated-timeline
(fill-and-compute-metrics data-map start-secs end-secs))
(define header '("Datum" "TSS" "CTL" "ATL" "TSB"))
(define output-rows
(cons header
(map (lambda (day-data)
(match-let ([(list secs date tss ctl atl tsb) day-data])
(list date
(number->string (exact->inexact tss))
(real->decimal-string ctl 1)
(real->decimal-string atl 1)
(real->decimal-string tsb 1))))
calculated-timeline)))
(write-file output-file
(string-join
(map (lambda (row) (string-join row ",")) output-rows)
"\n"))
(printf "Klart! Data har fyllts i dag för dag och skrivits till ~a\n" output-file)
(plot-training-data calculated-timeline))
;; --- KÖR PROGRAMMET ---
(process-training-data "training.csv" "training_calculated.csv")