Vertical Speed

Hi,

Do any of you know how to calculate "Vertical Speed"?

Is it as something like (info.totalAscent + info.totalDescent) / info.timerTime?

Thanks,

  • Not sure of the formula, but it seems you'd need distance.

    "over x seconds, you moved y meters and elevation change z meters"

    A right triangle where the hypotenuse gives you the vertical distance (including horizontal distance) for the time, which will give you speed.

  • You are right, my formula does not make much sense. I believe it would be more of an average vertical speed.

    To calculate current speed, I guess we can take than last 5 seconds and do :

    current_speed = horizontal distance run in the last 5 seconds / 5

    So if we talk about vertical speed, shouldn't we replace "horizontal distance" by "vertical distance", so the formula would be :

    vertical_speed = vertical distance run in the last 5 seconds / 5

    *** vertical distance = altitude traveled in the last 5 seconds.

    What do you think?

  • Yes, your summary of the algorithm in your last post seem correct.

    You'll need to determine the vertical distance yourself though. We expose Activity::Info::altitude, totalAscent, and totalDescent. You can derive the results you're looking for from those.

  • I was thinking of calculating the difference of info.altitude every 15 seconds or so. Can you share how Garmin do it using standard datafield?

    User Manuals aren't much helpful on this. va3 has the following description :

    • Vertical Speed : The rate of ascent or descent over time.
    • 30s Avg. Vertical Speed: The 30-second moving average of vertical speed.

    but we don't even know if it's in meter/sec, meter/min, meter/hour, etc. for statute units, I guess it uses feet?

    Thanks!

  • I have a couple of different VAM fields I report.  Lap, 10min, 5min, 30sec.  

    VAM is usually measured in feet or meters / hour. So for my 10min, (AltCur - altStart) * 6.

    For lap, I remember what the elevation was at the beginning of lap,.

    For the time interval, I actually keep an array of the last 600 elevation points, one per second.  Implemented as a circular array so you aren't shifting values.  It's memory intensive, but execution efficient.

    This is the code I use and you are welcome to use it.  It's a bit more complicated because I'm trying to actually store altitude differences in the array as a fraction of "scale".  I use the parameter "cExceedRange" to monitor how often I exceed values that would fit in a byte, and adjust "Scale" accordingly. 

    It'd be easy to modify this to the simple form of storing the current elevation in the array, and doing the computations on the fly.  This was just much more memory efficient as you extend the range of your VAM interval. 

    using Toybox.System as Sys;
    using Toybox.Activity as Act;
    
    //-----------------------------------------------
    // vertical rate
    //	feet / hour - 30 seconds, 10 minutes ???
    //  feet / mi - grade - based on dist or time?
    // 10min = 600 sec
    //Grade
    //Ascent/descent
    //VAM – vertical / minute – Vertical Speed – ft / hour
    //Instant, 30 sec, Lap, Avg 
    //-----------------------------------------------
    class VAM
    {
    	const cPtMax = 601; //has to be larger than the longester interval
    	const Scale = 20;
    	var rg = new [cPtMax]b;
    	var iLast = 0;
    	var cTotal = 0;
    	var cExceedRange = 0;
    	
    	var dAlt30Sec = 0;
    	var dAlt5Min = 0;
    	var dAlt10Min = 0;
    	
    	var vam30Sec = 0; //meters / hour
    	var vam5Min = 0;
    	var vam10Min = 0;
    	var altLast = null;
    	
    	
    	function initialize()
    	{
    		for (var i = 0; i < cPtMax; ++i) {rg[i] = 128;} 
    	}
    	function incCRange(dAlt)
    	{
    		++cExceedRange;
    		Sys.println(Lang.format("Exceed VAM Range: $1$ $2$ $3$/$4$", [strTimeOfDay(true), dAlt, cExceedRange,cTotal]));
    	}
    	function add(alt)
    	{
    		alt *= Scale;
    		alt = alt.toNumber();
    		cTotal++;
    		
    		var dAlt = (altLast != null) ? (alt-altLast) : 0;
    		//Sys.println(dAlt);
    		if (dAlt < -128) {incCRange(dAlt);dAlt = -128; }
    		else if (dAlt > 127) {incCRange(dAlt);dAlt = 127;}
    		
    		dAlt30Sec = dAlt30Sec - (rg[(iLast - 30 + cPtMax) % cPtMax]-128) + dAlt;
    		dAlt5Min = dAlt5Min - (rg[(iLast - 300 + cPtMax) % cPtMax]-128) + dAlt;
    		dAlt10Min = dAlt10Min - (rg[(iLast - 600 + cPtMax) % cPtMax]-128) + dAlt;
    		
    		iLast = (iLast + 1) % cPtMax;
    		rg[iLast] = dAlt+128;
    		
    		vam30Sec = dAlt30Sec * 120 / Scale;
    		vam5Min = dAlt5Min * 12 / Scale;
    		vam10Min = dAlt10Min * 6 / Scale;
    		
    		//Sys.println(Lang.format("VAM: $1$ $2$ 30sec: $3$ $4$, 5Min: $5$ $6$, 10Min: $7$ $8$", 
    		//		[alt, dAlt, dAlt30Sec,vam30Sec,dAlt5Min,vam5Min, dAlt10Min, vam10Min]));
    				
    		altLast = alt;			
    	}
    }
    

  • Thanks ekutter for sharing the code. I really like the idea of a circular array. I will probably use a similar technique.

    I also noticed the following syntax in your code: var rg = new [cPtMax]b;

    I wasn't aware we can force an array to be a specific type. That seems pretty usefull to limit memory usage!

    How did you find this? I've never seen anything like this in the documentation. Do you know the syntax for other type (integer, float, long, etc.)? Can we use a similar technique to declare a single value or does it only work with array?

  • See "Declaring Variables" in the programmer's guide. "b" is for a Byte Array, "l" for a long, 'd" for a double.  I don't see "b" mentioned there, but you'll see it used in the API doc for ByteArray.

  • Thanks jim! Looks like I read too fast!

  • Looks like it only work with array.

    var var1 = 0b;
    var var2 = new [1]b;

    Compiler complains about var1 : extraneous input 'b' expecting {',', ';'}

    No problem with var2.

  • Correct.  Byte arrays are just a memory saving feature for arrays.  Individual numeric variables are still going to be 32 bits, unless they are doubles, in which case they are treated more like objects.  The code just converts the values as it retrieves/stores them.