What is the difference in accuracy / speed of update? getRunningDynamics() vs RunningDynamicsListener

Hi,

So there are two different ways that I can see to get running dynamics info.

This:

// In compute
if(Toybox.AntPlus has :RunningDynamics) {
	var o = Toybox.AntPlus.RunningDynamics.getRunningDynamics();
	// Etc.
}

And this:

// Separate class...
class RunDynamicsListen extends Toybox.AntPlus.RunningDynamicsListener {
	var dynamics;
	function initialize() {
		RunningDynamicsListener.initialize();
	}
	function onRunningDynamicsUpdate(data) {
		dynamics = data;	
	}
}
// In view
hidden var listen;
hidden var dynamics;
// In DataField.initialize
if(Toybox.AntPlus has :RunningDynamics) {
	listen = new RunDynamicsListen();
    dynamics = new Toybox.AntPlus.RunningDynamics(listen);
}
// In compute
if(listen != null) {
    var o = listen.dynamics;
    // Etc.
}

One of them is clearly a ton more code and memory usage. But for a DF display, I only need (can have) a per-second update in any case. 

So...

Unless there is a major cost in terms of lagging values, I would much prefer to stick with the first. 

Am I correct that there is no real downside, or is the update massively slower if I just call getRunnningDynamics() ?

Thanks,

G

  • I would expect them to be pretty much the same.  If in doubt, build two DF and run them at the same time in an activity and see what you see,

  • The first code snippet is wrong. You'd have to write..

    // In DataField
    hidden var dynamics;
    
    // In DataField.initialize
    if(Toybox.AntPlus has :RunningDynamics) {
        dynamics = new Toybox.AntPlus.RunningDynamics(null);
    }
    
    // In DataField.compute
    if(dynamics != null) {
        // Etc.
    }

    And the alternative is this:

    // Separate class...
    class RunDynamicsListen extends Toybox.AntPlus.RunningDynamicsListener {
    	var dynamics;
    	
    	function initialize() {
    		RunningDynamicsListener.initialize();
    	}
    	
    	function onRunningDynamicsUpdate(data) {
    		dynamics = data;	
    	}
    }
    
    // In DataField
    hidden var listen;
    hidden var dynamics;
    
    // In DataField.initialize
    if(Toybox.AntPlus has :RunningDynamics) {
    	listen = new RunDynamicsListen();
        dynamics = new Toybox.AntPlus.RunningDynamics(listen);
    }
    
    // In compute
    if(listen != null) {
        var o = listen.dynamics;
        // Etc.
    }

    The cost is an extra class to receive the data, the gain is that you don't have to query the system for the values every second (the system will update them as changes happen, at most once a second)

    For a simple application the difference doesn't matter that much.

  • The first code snippet is wrong. You'd have to write..

    What you say makes sense, and I am willing to implement it as a precaution (see also the thread I'm about to start on undocumented differences between watches...) but it doesn't seem to match with either the simulator or my real world experience. The below is literally the sum and extent of running dynamics code I have on a DataField that I have just got back from a run with that faithfully recorded my running dynamics values to exactly match the running dynamics values provided by default for the watch (a 735xt with HR-Run strap to provide running dynamics):

    (:runningdynamics) function readValues(info) {
    	if(Toybox.AntPlus has :RunningDynamics) {
    		var o = Toybox.AntPlus.RunningDynamics.getRunningDynamics();
    		if(o) {	current = setVal(o,pCurrent);			}
        }
    }

    So my question is this:

    Is this just a lucky accident because of non-standard behaviour of the 735xt or is it actually a coding issue that I need to fix?

  • Your code just happens to work because the RunningDynamics class doesn't have or access any member data stored internally. It is not expected to work, and you shouldn't rely on it.

    The method getRunningDynamics() is a non-static method of class AntPlus.RunningDynamics. In order to legally call a non-static method, you have to have an object instance to call the function on. You'd normally get an object instance via a new expression (e.g., new AntPlus.RunningDynamics(listener)).

    This may seem confusing because you may have seen things like Toybox.Activity.getActivityInfo() in code, and that is correct and legal.. why would this be any different?

    In the case of Toybox.Activity.getActivityInfo() the name Toybox.Activity is actually a module. Much like a static member function, module functions don't require an object instance. We don't have any examples of static methods in the ConnectIQ API so this is as close an example as I can provide.

    Why is an object instance required to invoke a non-static member function? All non-static member functions have access to data stored in the instance that the function is called on (via the hidden self reference). Static methods do not have a self reference, so they can't try to access instance data.

    Consider the following class and sample code:

    class Thing
    {
        hidden var value;
        
        function initialize(value) {
            self.value = value;
        }
    
        // does not access member data
        function getValue1() {
            return 1;
        }
        
        // accesses member data
        function getValue2() {
            return self.value;
        }
    }
    
    // correct usage
    var thing = new Thing(999);
    var good_value1 = thing.getValue1(); // safe
    var good_value2 = thing.getValue2(); // safe
    
    // incorrect usage
    var bad_value1 = Thing.getValue1(); // Seems to work
    var bad_value2 = Thing.getValue2(); // BOOM!

    If you call the non-static method getValue1() from outside the class as Thing.getValue1(), it seems to work because the function getValue1() doesn't reference any data in Thing. But what about Thing.getValue2()? It accesses data in the Thing, but we aren't calling the function on a Thing... what would be returned? Nothing. You get a Symbol Not Found error at runtime.

    This unusual behavior isn't unique to MonkeyC. A C++ compiler will prevent you from directly calling a non-static member function like this, but you can still call such a function without an object instance, and no error will occur. I believe this is technically Undefined Behavior, so you shouldn't do it, but it is possible...

    class Thing
    {
        int _value;
        
    public:
    
        Thing(int value) : _value(value) {
        }
    
        // does not access member data
        int getValue1() const {
            return 1;
        }
        
        // accesses member data
        int getValue2() const {
            return this->_value;
        }
    };
    
    int main()
    {
        Thing* thing;
       
        // correct usage
        thing = new Thing(999);
        int good_value1 = thing->getValue1(); // safe
        int good_value2 = thing->getValue2(); // safe
        delete thing;
    
        // incorrect usage
        thing = 0; 
        int bad_value1 = thing->getValue1(); // Seems to work
        int bad_value2 = thing->getValue2(); // BOOM!
    }
    

  • @

    With this code that you mentioned in your opening post, I get "method" when I do a simple System.println(o).

    // In compute
    if(Toybox.AntPlus has :RunningDynamics) {
    	var o = Toybox.AntPlus.RunningDynamics.getRunningDynamics();
    	// Etc.
    }

    How do I get for example steplength extracted from it?

  • getRunningDynamics returns either null or one of these:

    https://developer.garmin.com/connect-iq/api-docs/Toybox/AntPlus/RunningDynamicsData.html

    So you can refer to whatever you need. 

  • -

    As I explained above, you don't want to write Toybox.AntPlus.RunningDynamics.getRunningDynamics() in your code. The getRunningDynamics() function is an instance method, which means you need to have an object instance to call it on.

    How do I get for example steplength extracted from it?

    You've already asked this in a thread you created.

    https://forums.garmin.com/developer/connect-iq/f/discussion/237087/using-runningdynamicsdata-groundcontactbalance-and-other-metrics-in-a-datafield