sensor history bug in vivoactive HR firmware 3.80

there is a bug in firmware 3.80 vivoactive HR where getMax() and getMin return wrong values. It started with firmware 3.80.
I'm not sure how to reproduce it.

The following code return different results:

hrmax=sensorIter.getMax();
hrmin=sensorIter.getMin();

and

for (x=147;x>=0;x--) {
sample = sensorIter.next();
if( null != sample ) {
if( sample.data != null) {
if(sample.data<hrmin) {hrmin=sample.data;}
if(sample.data>hrmax) {hrmax=sample.data;}
}
}
}

In some case, the hrmin and hrmax from getMax and getMin return outdated results.
Everything is ok in the simulator.
  • Your code snippet shows that you are iterating over 147 samples of sensor history data. Are there more than 147 samples of history in that iterator? Is there a reason you iterate backward instead of just asking for the data in the order you want it?

    I'm currently trying to reproduce the problem, and I'll post back this afternoon to report whether or not I'm able to reproduce. For now I've pasted a properly completed bug report below. Your bug reports should look something like this.




    o A Descriptive Title (i.e. &#8220;Simulator Freezes Launching App in Eclipse&#8221;)
    getMin() and getMax() on the iterator returned by SensorHistory.getHeartRateHistory() are reporting incorrect values

    o The Environment:
    vivoactive_hr 3.80
    ConnectIQ 2.2.5

    o A detailed description of the issue
    The values reported by the getMin() and getMax() methods of SensorHistoryIterator are not reporting the correct values on vivoactive_hr. To reproduce, run the following code on a device.

    o Any applicable additional information
    N/A

    o A code sample that can reproduce the issue (in email only if preferred)
    using Toybox.Application as App;
    using Toybox.WatchUi as Ui;
    using Toybox.Graphics as Gfx;
    using Toybox.Timer as Timer;
    using Toybox.SensorHistory;

    class MyApp extends App.AppBase
    {
    hidden var timer;

    function initialize() {
    AppBase.initialize();
    }

    function onTimer() {
    Ui.requestUpdate();
    }

    function onStart(params) {
    timer = new Timer.Timer();
    timer.start(self.method(:onTimer), 60 * 1000 * 10, true);
    }

    function onStop(params) {
    timer.stop();
    timer = null;
    }

    function getInitialView() {
    return [ new MyView() ];
    }
    }

    class MyView extends Ui.View
    {
    hidden var min_failed;
    hidden var max_failed;

    function initialize() {
    View.initialize();
    min_failed = 0;
    max_failed = 0;
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.clear();

    var font = Gfx.FONT_NUMBER_MEDIUM;
    var dy = dc.getFontHeight(font);

    var x = dc.getWidth() / 2;
    var y = dc.getHeight() / 2 - (dy * 3) / 2;

    var historyIterator = SensorHistory.getHeartRateHistory({
    :period => 147,
    :order => SensorHistory.ORDER_NEWEST_FIRST
    });

    var min = historyIterator.getMin();
    var max = historyIterator.getMax();

    var xmin = null;
    var xmax = null;

    var sample = historyIterator.next();
    while (sample != null) {

    var data = sample.data;
    if (data != null) {
    if (xmin == null || data < xmin) {
    xmin = data;
    }
    if (xmax == null || xmax < data) {
    xmax = data;
    }
    }

    sample = historyIterator.next();
    }

    if (xmin != min) {
    min_failed += 1;
    }

    if (xmax != max) {
    max_failed += 1;
    }

    // cleanup for presentation in case we have no history...

    if (min == null) {
    min = "--";
    }

    if (max == null) {
    max = "--";
    }

    if (xmin == null) {
    xmin = "--";
    }

    if (xmax == null) {
    xmax = "--";
    }

    dc.drawText(x, y, font, Lang.format("$1$/$2$", [ min, max ]),
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);

    y += dy;

    dc.drawText(x, y, font, Lang.format("$1$/$2$", [ xmin, xmax ]),
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);

    y += dy;

    if (min_failed || max_failed) {
    dc.setColor(COLOR_RED, Gfx.COLOR_BLACK);
    }
    else {
    dc.setColor(Gfx.COLOR_GREEN, Gfx.COLOR_BLACK);
    }

    dc.drawText(x, y, font, Lang.format("$1$/$2$", [ min_failed, max_failed ]),
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    }
    }

  • Ok, maybe I have found the problem:

    https://forums.garmin.com/showthread.php?375783-The-NEW-HR-measuring-frequency-in-the-Vivoactive-HR

    In my code, I assume that the sensor history is 148 samples for 4 hours (the width of the vivoactive HR).
    Since firmware 3.80, the sample rate seems higher: 240 samples / 4 hours.
    So, I cannot use getMax() and getMin() for the last 148 samples.

    Can you confirm that the sample rate is different in firmware 3.80 ?

    Also, the SDK needs an update ?

    Thanks !
  • In my code, I assume ...

    Don't assume. Things like this could potentially change, causing your app to misbehave (as you've noted).

    If you want to get the last 148 samples ordered oldest to newest, request that....
    var historyIterator = SensorHistory.getHeartRateHistory({
    :period => 148,
    :order => SensorHistory.ORDER_OLDEST_FIRST
    });


    If you want the last 4 hours of samples, request that...
    var historyIterator = SensorHistory.getHeartRateHistory({
    :period => new Time.Duration(4 * Gregorian.SECONDS_PER_HOUR),
    :order => SensorHistory.ORDER_OLDEST_FIRST
    });


    If you want to do something more tricky, you may have to do some counting or sample skipping. For instance if you want up to 4 hours of data, but no more than 148 samples, you might have to request 4 hours of data and then cull the returned data down to 148 samples.

    Also, the SDK needs an update ?

    I'm not sure what you're talking about.

    Travis
  • Thank you very much for your help !
    Yes, I'm now doing some counting and samples skipping and it works.