[Appbuilder project] Training label data field

Hi everyone,

I’m currently working on a personal project based on Garmin / Firstbeat workout labels and training load logic.
I'm not what you could call a developer, my skills in coding are very limited so I rely on AppBuilder, a customizable data field from Flowstate (all credits to him !) which collects and processes data from the sensors (HR, GPS mostly).

The idea is to build an AppBuilder data field that can estimate, in real time during a run, the likely training classification of the session — for example:

  • Recovery / Base
  • Tempo
  • Threshold
  • VO2max
  • Anaerobic Capacity
  • Sprints

The current test version does not try to perfectly reproduce Garmin yet. It is an experimental field intended to compare, during and after an activity, the live estimates from the data field with the final Garmin classification and Traning load distribution. I hope it will help me understand how training assesment works - and maybe make my training more efficient !

My approach is based on information from the following Firstbeat patent, especially the parts related to:

  • cumulative time in intensity zones
  • %LTHR
  • %AnT speed
  • modified intensity
  • aerobic vs anaerobic training effect logic

patentimages.storage.googleapis.com/.../US20230414143A1.pdf

I am now looking for a few interested users who would be willing to test the data field and compare its output with Garmin’s final results after activities.

The most useful test sessions would probably be things like :

  • easy endurance runs
  • tempo or threshold runs
  • long intervals
  • short intervals / 30-30 / 200 m reps
  • sprint sessions

What would help most is feedback such as:

  • Garmin’s final workout label
  • Aerobic / Anaerobic Training Effect
  • load distribution (Low / High / Anaerobic)
  • screenshots or observations from the data field during the activity

This is still an early test version, so I fully expect the model to need refinement. The idea is simply to improve it step by step with real-world comparisons.

If anyone is interested in testing it or discussing the logic behind Garmin / Firstbeat workout classification, the code to copy/paste (after update with your personal values for LTHR, LT pace (converted to speed) is in the post below.
PLEASE NOTE : I use a Fenix 8. I tried to optimize the code not to be too heavy on memory but if you want to give it a try, better check on a short run first if your device is capable of running the DF. It made my watch crash a few times when displaying the map screen. So I recommend not using maps or music if you are interested.

Thanks a lot !

  • Here is the code. Update your personal LTHR & LT pace (converted to speed) in lines 16 & 17

    setv(10,if(getv(10),getv(10),0))
    setv(11,if(getv(11),getv(11),0))
    setv(12,if(getv(12),getv(12),0))
    setv(13,if(getv(13),getv(13),0))
    setv(14,if(getv(14),getv(14),0))
    setv(15,if(getv(15),getv(15),0))
    setv(16,if(getv(16),getv(16),0))
    setv(17,if(getv(17),getv(17),0))
    setv(18,if(getv(18),getv(18),0))
    setv(19,if(getv(19),getv(19),0))
    setv(20,if(getv(20),getv(20),0))
    setv(21,if(getv(21),getv(21),0))
    setv(22,if(getv(22),getv(22),0))
    setv(5,if(getv(5),getv(5),0))
    setv(60,if(getv(60),getv(60),'Recovery'))
    setv(91,168)
    setv(92,13.09)
    setv(1,if(HR gt 0,HR,getv(91)))
    setv(2,if(Speed gt 0,Speed,0.1))
    setv(3,getv(1)/getv(91))
    setv(4,getv(2)/getv(92))
    setv(5,if(getv(5),getv(5),getv(1)))
    setv(6,getv(1)-getv(5))
    setv(6,if(getv(6) gt 0,getv(6),0))
    setv(5,getv(5)*0.8+getv(1)*0.2)
    setv(7,getv(6)/15)
    setv(7,if(getv(7) gt 0,getv(7),0))
    setv(7,if(getv(7) lt 1,getv(7),1))
    setv(8,((getv(3)*6)+(getv(4)*4))/10)
    setv(9,getv(8)*(1+0.2*getv(7)))
    setv(10,getv(10)+1)
    setv(11,if(getv(3) lt 0.90 and getv(4) lt 0.95 and getv(9) lt 0.95,getv(11)+1,getv(11)))
    setv(12,if(((getv(3) gte 0.90 and getv(3) lt 0.96) or (getv(4) gte 0.90 and getv(4) lt 0.96)) and getv(9) lt 1.00,getv(12)+1,getv(12)))
    setv(13,if(((getv(3) gte 0.94 and getv(3) lte 1.02) or (getv(4) gte 0.94 and getv(4) lte 1.05)) and getv(9) lt 1.03,getv(13)+1,getv(13)))
    setv(14,if(((getv(3) gte 1.00) or (getv(4) gte 1.02)) and getv(9) lte 1.15,getv(14)+1,getv(14)))
    setv(22,if(getv(9) gt 1.00,getv(22)+1,getv(22)))
    setv(20,if(getv(9) gt 1.15,getv(20)+1,getv(20)))
    setv(21,if(getv(9) gt 1.40,getv(21)+1,getv(21)))
    setv(16,getv(15))
    setv(15,if(getv(7) gte 0.33,1,if(getv(7) lte 0.20,0,getv(15))))
    setv(17,if(getv(15) gt getv(16),getv(17)+1,getv(17)))
    setv(18,if(getv(15) eq 1,getv(18)+1,getv(18)))
    setv(19,if(getv(17) gt 0,getv(18)/getv(17),0))
    setv(60,
    if(getv(21) gte 20 and getv(17) gte 3 and getv(19) lte 20,'Sprints',
    if(getv(20) gte 60 and getv(17) gte 3,'Anaerobic',
    if((getv(14) gte 480 or getv(14) gte getv(10)*0.15) and getv(20) lt 60,'VO2max',
    if((getv(13) gte 600 or getv(13) gte getv(10)*0.15) and getv(20) lt 30,'Threshold',
    if((getv(12) gte 900 or getv(12) gte getv(10)*0.20) and getv(22) lt getv(10)*0.10,'Tempo',
    if(getv(10) lt 600,'No label',
    if(getv(10) lt 2400,'Recovery',
    if((getv(11) gte getv(10)*0.60) and getv(20) lt 10,'Base','Recovery')))))))))

  • Just 1 thought: understanding the Garmin algorithms is one thing, you can then be more "efficient" in making those algorithms happy, which might or might not mean much about your physical improvement

  • You are absolutely right. But I'm a bit of a nerd (if that was not obvious haha) and I always feel frustrated when I get "Tempo" after a hard 20x 30/30 intevals run Smiley 

  • I always feel frustrated when I get "Tempo" after a hard 20x 30/30 intevals run

    The most common reason for that is an inaccurate Max HR in the User Profile. Wrong LTHR is another possibility.

  • Yes, could be. Could also be not going hard enough on the reps, or warming up / cooling down at a higher HR than expected.