How Edge calculate current heart zone decimal value

Hi all, 

I don't understand how Garmin calculate current Heart Zone value in it's system default DataField.

I tried to develop the same "algorithm" based on my current Heart Zones but I can't get the same results...
See below an example:

Instant HR value is 62bpm. 
The simple algorithm I've made gives me I am in Zone 0.7 
Default device (I am using an Edge 840) calculation gives me Zone 0.5

My Heart Zones are the following:

Zone 0: 0 - 91
Zone 1: 92 - 109
Zone 2: 110 - 127
Zone 3: 128 - 145
Zone 4: 146 - 164
Zone 5: 165 - 183

Based on those values, my algorithm looks more precise... but I really do not understand how Garmin gives me 0.5 for the same bpm value (62).

Any idea? 

Top Replies

All Replies

  • Ok I get it. If the fractional hr zone (call it F) is > 6.0 (or even >= 5.0), then it means that the the fractional HR zone is (F-5.0) * 100% of Zone 5.

    e.g. 6.5 means 150% of zone 5

    7.3 means 230% of zone 5

    etc

    While that's interesting, it means that zone 5 is kind of "special" in this regard (inconsistent with how all the other zones are defined). Yeah, I guess it's not any worse than saying everything >= zone 5 max is 6.0. It's def different than what Garmin does (which isn't necessarily a bad thing).

    I also noticed that you are adding 1 to range N's HR max to obtain range N+1's HR min. While I understand why you did this, I think it produces somewhat discontinuous results compared to allowing the ranges to overlap (the way they are actually defined in user settings), for the purposes of calculating fractional hr zone.

    For example, if zone 1 is 120-140 and zone 2 is 140-160, I think it's kind of elegant that the following formula produces the same result at the border of zone 1 and zone 2:

    decimal HR zone = zone + (HR - min_zone[zone]) / (max_zone[zone] - min_zone[zone])

    For HR = 140 and zone = 1: 1 + (140 - 120) / (140 - 120) = 1 + 1 = 2.0

    For HR = 140 and zone = 2: 2 + (140 - 140) / (160- 140) = 2 + 0 = 2.0

    Note that zone 1 through zone 2 span 120-160, and all values from 1.0 to 3.0 will map nicely over this range. If you graphed HR on the x-axis vs fractional zone on the y-axis, you'd get a nice straight line, which is what would be expected (imo). Not so with your algorithm, where you'd get little jumps at the borders of zones.

    I haven't tried to prove it, but my guess is Garmin does calculate fractional hr zone using overlapping ranges.

    Overlapping ranges would also make more sense than non-overlapping ranges, if HR could be fractional as opposed to always being an integer (yeah now I'm getting super theoretical haha).

    Ofc it's notable that Garmin presents the overlapping HR zones in config (and in ciq), but for the time in zones chart, they adjust one of the values at each "border" by 1, so there's no overlap. (Yeah, obviously for an feature like time in zone, it's not desirable for the zones to overlap)

    However, note that you and Garmin take the opposite approach:

    - you adjust the min value of each zone (starting at zone 2) by adding 1

    - but Garmin adjusts the max value of each zone (except zone 5) by subtracting 1. (Even if your zones are defined by %, which means the integer values are no longer displayd in config, it's possible to verify this by looking at the integer zone values recorded to a FIT file using www.fitfileviewer.com, and comparing it to the ranges in Connect's time in zone chart)

    So it's probably not possible that your algorithm is doing the same thing as Garmin, even for decimal zone values <= 6.0, and even if Garmin uses non-overlapping HR ranges to calculate the fractional zone, since Garmin uses a different strategy to calculate non-overlapping ranges.

  • I don't understand your proposal regarding the N+1 thing. 

    the way they are actually defined in user settings

    With UserProfile.getHeartRateZones you only get more or less what you see in the comment about heartRateZones. I only decrease 1 from the 1st element to make it consistent (i.e instead of z1min it means z0max in my array). No overlapping.

    Also I think you use the wrong word, and you don't mean overlapping (which would mean that both 140 and 141 would be in both z1 and z2. What you mean is touching: z1: [120,140], z2:[140,150] and there's only 1 point that is in both, and 1 point doesn't have a "width" so it's not overlapping.

    As I look at it it's either (from, to] or [from, to) ranges. Since z1-z5 are defined by their max values it makes more sense to have (from, to]. But note, that here we're talking about HR in bpm as Number! Not fractional! This is the raw data. So basically the ranges become closed: [from, to] I don't think that the way I calculate the fractional will have any effect on the continuity of the fractional values.

    In theory, since, since the whole point of the data field is to read the HR from ANT straps, I could use the RR-interval (where it's supported) and thus the raw data (the calculated HR) itself would also be fractional. Then maybe the 1bpm gap between the ZnMax and Zn+1Min would cause some jumps in the calculated fractional HR.

    LOL: Here's your division by zero error:

    For HR = 140 and zone = 2: 2 + (140 - 140) / (140 - 140) = 2 + 0 = 2.0

     

  • LOL: Here's your division by zero error:

    For HR = 140 and zone = 2: 2 + (140 - 140) / (140 - 140) = 2 + 0 = 2.0

    Yeah it was a typo haha. It should've been:

    For HR = 140 and zone = 2: 2 + (140 - 140) / (160 - 140) = 2 + 0 = 2.0

  • I only decrease 1 from the 1st element to make it consistent (i.e instead of z1min it means z0max in my array)

    As I said, I think your choice of which point to adjust to make the ranges "not touching" is different than Garmin's choice, as evidenced the by time in zones graph.

    Again let's use the example of Z1 = 120-140 and Z2 = 140-160

    Garmin would display the following ranges in the time in zones chart:

    Zone 1: 120-139

    Zone 2: 140-160

    In other words, Garmin did this:

    •  z2_min = z1_max
    • z1_max = z1_max - 1

    (Again you can verify this by comparing the "touching" integer ranges in fitfileviewer.com to the ranges displayed in the time in zones chart)

    According to your code, you would do the opposite:

    •  z2_min = z1_max + 1

    So even if making the zones non-touching is the right thing to do, you've done it differently than Garmin would, at least as evidenced by the time in zones chart.

    . So basically the ranges become closed: [from, to] I don't think that the way I calculate the fractional will have any effect on the continuity of the fractional values.

    Yeah, you're right, maybe fractional HR is a red herring here. But you certainly wouldn't be adding or subtracting 1 to any of the border values if fractional HR were possible.

    I still think touching ranges vs non-touching ranges makes a difference for the fractional hr calculation.

    I admit I didn't do a great job of explaining the differences (as I see them).

    Maybe I'll graph the results of 2 algorithms ("touching ranges" vs "non-touching ranges") to demonstrate the difference, if I have a chance. Maybe I'll end up proving your point, idk.

    I think touching ranges will yield straight line segments for each zone, and the segments will touch each other at the borders.

    I think non-touching ranges will have a little discontinuity (or tiny "extra" line segment with a width of 1, if you feel like connecting the adjacent segments), at each of the borders.

    I guess none of that proves that either approach is "correct" or "incorrect". But I think my graph would look nicer.

    I'll also note that the touching ranges algorithm has the property where it still works the same if you "combine" 2 zones of the same width (which is what I was trying to get at before).

    e.g. if you apply my algorithm to all possible HR values in zone 1 (120-140) or zone 2 (140-160), you get the same results if you reimagine those 2 zones as one big zone of 120-160 and multiply the fractional part in the calculation by 2.

    iow, the way I want to do things, both sets of formulas will have the same result:

    1)

    fractional hr (in zone 1) = 1 + (hr - zone_1_min) / (zone_1_max - zone_1_min)

    fractional hr (in zone 2) = 2 + (hr - zone_2_min) / (zone_2_max - zone_2_min)

    2)

    fractional hr (in zone 1-2) = 1 + ((hr - zone_1_min) / (zone_2_max - zone_1_min)) * 2

    Similarly, if I divided a zone into 2 or more zones of equal width, the "touching zones" calculations could be adjusted in the opposite direction, to get the same results as they would for the original zone.

    I don't think the same applies to your algorithm, due to the way you treat min/max values at the borders

    --

    To be clear:

    - for time in zones calculations, it's clear to me that the zones have to be "not touching"

    - for the fractional hr calculation, using "touching" zones makes more sense to me (intuitively)

  • I think non-touching ranges will have a little discontinuity (or tiny "extra" line segment with a width of 1, if you feel like connecting the adjacent segments), at each of the borders.

    Again: currently the input is heartRate as Number. all the graphs are discontinual (is there such a word?), not only at the zone borders but everywhere.

  • discontinual (is there such a word?)

    discontinuous

    not only at the zone borders but everywhere.

    Clearly I was talking about the graph that you get if you connect the points.

    Again my points were:

    - your graph (obtaining by connecting the points) will have more sharp bends (non-differentiable points) than mine. I think that my graph will have 1 bend at the border between each zone, while yours will have 2 bends at each border.

    - the "touching ranges" algorithm works the same if you combine zones of equal widths, or divide zones into pieces of equal width, which I happen to think is mathematically elegant in a way

    I don't claim that either algorithm (touching vs non-touching) is "better" or "more correct", just that I like mine better for intuitive reasons that I probably haven't expressed very well.

    But again, I will say that your method of adjusting the configured ranges so they are "not touching" is different than Garmin's method (for time in zones). Garmin subtracts 1 from the border of zones N and N+1 to get zone N max (except when N = 5). You add 1 to the border of zones N and N+1 to get zone N+1 min.

    So it's unlikely that your algorithm will produce the same results as Garmin (even for values <= 6.0), unless Garmin happens to use 2 opposite "adjustment" algorithms for time in zones and fractional hr calculation [*]. I realize the goal isn't necessarily to do what Garmin does (although you did change your algorithm to use the min value of 30).

    [*] My completely wild guess is that Garmin does not adjust the ranges for the purpose of calculating fractional hr zone, but simply uses the "touching" zones as-is.

  • As a bonus, the touching ranges algorithm would work without modification if the input HR could actually be a real number as opposed to an integer, whereas your non-touching ranges algorithm would need to change. (Again, you would not be able to get away with adding or subtracting 1 from the border values in this case). And if you actually adjusted your non-touching ranges algorithm so that it did work with real numbers, I think it would probably end up being equivalent to the touching ranges algorithm after all.

    I don't claim that the touching ranges algorithm is objectively better or more correct, just more mathematically elegant (and simpler) from my pov.

    For example, with the touching ranges algorithm, the HR value for "100% of zone 1" (1 + 1.0 = 2.0) is the same as the HR value for "0% of zone 2" (2 + 0.0 = 2.0), which makes a lot of sense to me. (The 2 concepts seem to be synonymous logically and mathematically, at least when using the touching zones)

    I don't think the same property holds for the non-touching ranges algorithm.

    Again, consider the following configured zones: Z1 = 120-140 and Z2 = 140-160

    1) According to the touching zones algorithm, 100% of zone 1 is 140, and 0% of zone 2 is 140. If a decimal HR zone of 2.0 is displayed, it's unambiguous that it corresponds to an HR value of 140, and vice versa. (Disregarding rounding of the decimal HR zone).

    2) According to the non-touching zones algorithm, adjusted_Z1 = 121-140 and adjusted_Z2 = 141-160. Now if a decimal HR zone of 2.0 is displayed, does that correspond to 140 or 141 (or neither)? What decimal zone values should the user expect to see for 140 and 141? It's not so intuitive (to me). I would have to look at your code to know what to expect.

    And your code has arguably unintuitive / arbitrary implementation choices, such as the choice of exactly how to make the ranges non-touching, which is the opposite of what Garmin does for time in zones. Another thing that's somewhat unintuitive (to me) is adding 1 to the denominator of fractional zone calculation. I get why you did it (the size of a non-touching range is max-min+1, and you also want to avoid division by 0), but ofc it's inapplicable to the touching zones calculation (assuming there's no zero-width zones).

    To expand on the simplicity argument (I see the irony):

    (disregarding the issues surrounding absolute min/max HR values and/or HR values that go beyond max HR)

    - the touching ranges algorithm really only has one way to be implemented

    - the non-touching ranges algorithm could be implemented in at least two ways (1 for each way to make the ranges "non-touching").

    At the very least, we can say there's twice as many possible implementations for the non-touching algorithm as there are for the touching algorithm.

  • I love your math brain food - I really do!
    Here's a funny (off topic) brainer for a break: (not meant seriously)
    The amount of natural numbers (1,2,3,4...) is infinite.
    And there is an infinite amount of real numbers between neighboring natural numbers.
    So can we say that the infinity of real numbers must be much greater than the infinity of natural numbers?

  • The amount of natural numbers (1,2,3,4...) is infinite.
    And there is an infinite amount of real numbers between neighboring natural numbers.
    So can we say that the infinity of real numbers must be much greater than the infinity of natural numbers?

    Yeah, as you're alluding to, there's different cardinalities of infinities.

    There are more real numbers than natural numbers (or integers), because it's impossible to produce a 1 to 1 correspondence between all the real numbers and integers. No matter what method you try, you'll always have "left-over" reals.

    https://en.wikipedia.org/wiki/Cantor%27s_diagonal_argument

    [https://en.wikipedia.org/wiki/Cardinality]

    Otoh, perhaps unintuitively:

    - the set of even integers is the same size (cardinality) as the set of integers

    - the set of natural numbers has the same cardinality as the set of integers

    - the set of rational numbers has the same cardinality as the set of integers

    etc.

    All of the above is true bc you *can* produce a 1 to 1 correspondence between those sets and the set of integers.

  • I love your math brain food - I really do!

    idk half the time I have no idea what I'm talking about, but I'm glad you're entertained!