Discussion about height tendency during a run

Hi,

I just want to start a discussion what's the best way to give the runner a tendency whether the height is rising or falling.

I could grab info.altitude to build a short history, let's say 5 values. Then I could calculate the derivation of this history.

What do you think?
  • You are trying to determine if the device/user is ascending or descending? If so, yes, you could track a few data points and use the sum of differences to tell if there is a climb or descent or not.

    Travis
  • Something to consider is if the device has a baro altimeter or if it uses GPS. With GPS, you should be able to get a trend, but the actual values may swing a bit more than with a baro. If I wear a baro and non- baro at the same time, the total ascent/decent on the baro device looks really good, but I've seen the non-baro device being off by a factor of 5 or 10 (a total Ascent of 40' on the baro might be 300' on the non baro)
  • Jim, this is a good idea to use it only on watches with baro.

    What I am trying to do is to show the current ascending/descending as a rotating arrow. If the arrow shows 90 degrees up it's 100 % rise (45 degrees) and if the arrow shows 90 degrees down it's 100 % fall ((45 °).

    A little bit math says: (x2-x1) / (y2-y1), where y can be represented by info.elapsedDistance and x can be calculated by the height.

    What do you think?
  • hi,
    this is what i did for a suunto app some time ago.

    you can calculate slope using x and y coordinates: dy/dx, but you can also use speed: [dy/dt] / [dx/dt]

    you need to calculate vx and vy.
    in order to calculate speed, you can use a 1 second timer.

    the most direct way to estimate them would be:
    vx = (x2-x1) / (1s) = x2-x1
    and
    vy = ... = y2-y1

    you have to be careful, not to perform the division if vx = 0, to avoid a division by null.


    actually i didnt calculate the speeds using that method. i used instead Savitzky Golay filters. the problem of the method above, is that the output is very noisy. with the savitzy golay filters, you can get a balance between noise and a certain delay introduced in the derivative.

    you can find the formula and data here (use the table "coefficients for 1st derivative", you have to choose one column. you can begin with "linear or quadratic", windows size=7, for instance.

    https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter#Tables_of_selected_convolution_coefficients
  • Sounds very interesting. Where did you get vx and vy? If I see that right you sample e. g . in the direct method every second and apply y2-y1/x2-x1. That's the same formula which I posted. Did I get that right?
  • Sounds very interesting. Where did you get vx and vy? If I see that right you sample e. g . in the direct method every second and apply y2-y1/x2-x1. That's the same formula which I posted. Did I get that right?


    vx and vy are speed for axis x and y. you have to calculate them. you do vx = (x2-x1)/(t2-t1)
    t2-t1 is always "1", if you calculate it every second. so in the end, vx = x2-x1. and it is the same for vy.

    but instead of calculating speed using this formula, vx = (x2-x1)/(t2-t1),
    you can use the mentioned filter. you need more older values, depending on the column. not just x2 and x1, but for instance x3,x2,x1,x0,x-1,x-2 and x-3. and when you substitute the formula, "h" is the step width => 1 second.
  • Hi Daniel,

    thanks for your reply. My electrotechnic study is long in the past but I understand your post in principal. Saying this, I don't know where to get vx and vy nor x and y.

    IMO, y is easy, since it is the altitude. I can get it from info.altitude. I set the last y values in your formula for 1st derivative and would get vor y-direction:
    Y = 1/12 (info.altitude(t-4) - 8* info.altitude(t-3) + 8 * info.altitude(t-1) - info.altitude(t))

    x could be calculated out of info.elapsedDistance:
    X = 1/12 (info.elapsedDistance(t-4) - 8* info.elapsedDistance(t-3) + 8 * info.elapsedDistance(t-1) - info.elapsedDistance(t))

    Y/X is the current grade.

    Am I right?
  • IMO, y is easy, since it is the altitude. I can get it from info.altitude. I set the last y values in your formula for 1st derivative and would get vor y-direction:
    Y = 1/12 (info.altitude(t-4) - 8* info.altitude(t-3) + 8 * info.altitude(t-1) - info.altitude(t))

    x could be calculated out of info.elapsedDistance:
    X = 1/12 (info.elapsedDistance(t-4) - 8* info.elapsedDistance(t-3) + 8 * info.elapsedDistance(t-1) - info.elapsedDistance(t))

    yes, it looks fine :) you have to keep an array of the last 5 samples, for altitude and elapsed distance. depending on the device, altitude can be gps based, which is not accurate. i am not sure about distance: in the suunto, if a footpod was paired, distance was coming from footpod, which is more accurate. i do not know if this is the case for the garmin. gps based distance might be also inaccurate.

    Y/X is the current grade.

    exactly. be careful not to divide by zero.

    Am I right?

    you are :)
  • I assume that on devices with baro the altitude comes from the barometer and not from GPS. Nevertheless, the elapsedDistance is from GPS. I assume that only a few user carry a footpod.

    I created a quick and dirty code.

    function calcSlope(dist, alt) {

    elapsedDistanceHistory = shiftArr(elapsedDistanceHistory);
    altitudeHistory = shiftArr(altitudeHistory);

    elapsedDistanceHistory[0] = dist;
    altitudeHistory[0] = alt;

    if (arrHasNull(elapsedDistanceHistory)) {return 0;}
    if (arrHasNull(altitudeHistory)) {return 0;}

    Sys.println(altitudeHistory);
    var dy = (altitudeHistory[4] - 8*altitudeHistory[3] + 8*altitudeHistory[1] - altitudeHistory[0]) / 12;
    var dx = (elapsedDistanceHistory[4] - 8*elapsedDistanceHistory[3] + 8*elapsedDistanceHistory[1] - elapsedDistanceHistory[0]) / 12;

    if (dx == 0) { return 0; }
    var slope = dy/dx;

    if (slope > 1) { return 1; }
    if (slope < -1) { return -1; }

    return slope;
    }


    The values are mostly between -1 and 1 which seems to be indeed the first derivative. In rare conditions the values are way beyond 1, e. g. 8345. I have filtered that out in my code.

    Here is a part of a trace file I created by simulating one of my runs:
    0: 0
    1: 0
    2: 0
    3: 0
    4: 0
    5: -1
    6: 1
    7: 0
    8: 0
    9: 0
    10: 0
    11: 0
    13: 0
    14: 0
    15: 1
    16: 1
    17: -0.513558
    18: -0.174785
    19: -0.098443
    20: -0.053616
    21: -0.050028
    22: -0.051430
    23: -0.026632
    24: -0.026556
    25: -0.049711
    26: -0.016834
    28: -0.018508
    29: -0.049917
    30: -0.019868
    31: 0.003435
    32: 0.003710
    33: -0.026306
    34: -0.023227
    35: -0.018325
    36: -0.052093
    37: -0.017985
    38: -0.015558
    39: -0.086764
    40: -0.030461
    41: -0.010006


    The left value is the time in seconds. The right one the calculated slope. As you can see, the filter needs some time to get stable but at around 20 seconds it's pretty good. What do you think?
  • I assume that on devices with baro the altitude comes from the barometer and not from GPS. Nevertheless, the elapsedDistance is from GPS. I assume that only a few user carry a footpod.

    I created a quick and dirty code.

    function calcSlope(dist, alt) {

    elapsedDistanceHistory = shiftArr(elapsedDistanceHistory);
    altitudeHistory = shiftArr(altitudeHistory);

    elapsedDistanceHistory[0] = dist;
    altitudeHistory[0] = alt;

    if (arrHasNull(elapsedDistanceHistory)) {return 0;}
    if (arrHasNull(altitudeHistory)) {return 0;}

    Sys.println(altitudeHistory);
    var dy = (altitudeHistory[4] - 8*altitudeHistory[3] + 8*altitudeHistory[1] - altitudeHistory[0]) / 12;
    var dx = (elapsedDistanceHistory[4] - 8*elapsedDistanceHistory[3] + 8*elapsedDistanceHistory[1] - elapsedDistanceHistory[0]) / 12;

    if (dx == 0) { return 0; }
    var slope = dy/dx;

    if (slope > 1) { return 1; }
    if (slope < -1) { return -1; }

    return slope;
    }


    The values are mostly between -1 and 1 which seems to be indeed the first derivative. In rare conditions the values are way beyond 1, e. g. 8345. I have filtered that out in my code.

    Here is a part of a trace file I created by simulating one of my runs:
    0: 0
    1: 0
    2: 0
    3: 0
    4: 0
    5: -1
    6: 1
    7: 0
    8: 0
    9: 0
    10: 0
    11: 0
    13: 0
    14: 0
    15: 1
    16: 1
    17: -0.513558
    18: -0.174785
    19: -0.098443
    20: -0.053616
    21: -0.050028
    22: -0.051430
    23: -0.026632
    24: -0.026556
    25: -0.049711
    26: -0.016834
    28: -0.018508
    29: -0.049917
    30: -0.019868
    31: 0.003435
    32: 0.003710
    33: -0.026306
    34: -0.023227
    35: -0.018325
    36: -0.052093
    37: -0.017985
    38: -0.015558
    39: -0.086764
    40: -0.030461
    41: -0.010006


    The left value is the time in seconds. The right one the calculated slope. As you can see, the filter needs some time to get stable but at around 20 seconds it's pretty good. What do you think?