Programming Data Fields in Garmin IQ

Former Member
Former Member
Hi Everyone!

My name is Jose, and I am a very enthusiastic triathlete. Although I am good at Matlab and Python programming, I am very new to the Monkey Platform, very. However, I am very interested in programming my own data fields and I had some questions:

1. Is it possible to get activity values from past times? For example for the last ten seconds? This will help me to calculate the grade according to the elevation, or heart-rate-curve slope. How can I do this?

2. Is it possible to dynamicaly compute a new variable, when its inputs are increasing size constantly? I would like to compute the NP, which is obtained by:

NP = [avg(P[SUB]1[/SUB][SUP]4[/SUP]+P[SUB]2[/SUB][SUP]4[/SUP]+...+P[SUB]i[/SUB][SUP]4[/SUP])][SUP]1/4[/SUP]

3. Is it possible to have access to the activity information of the current lap? Such as Pace averaged in Lap?

Thank you for your answers!!
Jose
  • Former Member
    Former Member over 10 years ago
    1. use a 10 element array which you insert into with a circular (mod 10) counter? Call Activity.getActivityInfo() in onSensor()?
    2. Math.sqrt, Math.pow, divide by numvber of instances. With the array from 1. that should be easy.
    3. Agree, it would be good to have a Toybox::Activity::Info.html equivalent for the current lap, but I haven't found it.
  • Former Member
    Former Member over 10 years ago
    Hi!

    Thank you very much for your quick response. Could you please be more specific on the questions 1 and 2? I am sorry but I have absolute no experience in Java. Would you be so kind to send me a minimal section of code for the extraction of the data in previous times? In other languages I would do something like:

    pastHR = info.currentHearRate(timeVector)

    Using timeVector as the indices I want to extract, but I'm not familiar at all with the syntax of Monkey, so I don't know about circular counters.

    Similarly, for the second question, for each second I would catch the vector of previous power data, and use it like this:

    pastPower = info.currentHearRate(timeVector)
    NP = Math.pow(Math.sum(Math.pow(pastPower,4))/length(pastPower),1/4)

    And I would repeat the same calculation for each second. I appreciate any help you can gave me with my code.

    Thanks in advance!
    Jose
  • Former Member
    Former Member over 10 years ago
    Not necessarily correct syntax, but should give you an idea:

    class View extends Ui.View {

    var i = 0
    var lastSeconds = [0,0,0,0,0,0,0,0,0,0,0]

    function onSensor(sensor_info) {
    var hr = activity_info.currentHeartRate;
    lastSeconds= hr;
    i = i & lastSeconds.size();
    }

    function calculateFloatingAverage() {
    var sum = 0;
    for (var i = 0; i < lastSeconds.size(); i++)
    {
    sum += (lastSeconds* lastSeconds* lastSeconds* lastSeconds)
    }
    sum = sum / lastSeconds.size();

    return Math.sqrt(Math.sqrt(sum));
    }

    }
  • i = i & lastSeconds.size();



    This most definitely won't do what you want. I believe you want...

    // advance `i' to the next index in lastSeconds, and wrap
    // around to the beginning when we would go past-the-end
    i = (i + 1) % lastSeconds.size();


    Additionally, a data field doesn't have access to sensors. The right thing to do would be to implement compute(info) in your data field and put the above logic in there.
  • Former Member
    Former Member over 10 years ago
    Hi!

    Thank you for your help. I am trying to calculate the average of the last 10 HR Data, but my code seems just to take into account the HR Value on the position i=0. Could you help me please? Thank you so much!


    using Toybox.WatchUi as Ui;

    class DriftRunView extends Ui.SimpleDataField {
    var i = 0;
    //var A = 0.0;
    //var B = 0.0;
    //var C = 0.0;
    //var D = 0.0;
    //var N = 0.0;
    //var slope = 0.0;
    //var result = 0.0;
    var lastSeconds = [0,0,0,0,0,0,0,0,0,0];

    //! Set the label of the data field here.
    function initialize() {
    label = "Drift [Zones/Min]";
    }

    //! The given info object contains all the current workout
    //! information. Calculate a value and return it in this method.
    function compute(info) {
    // See Activity.Info in the documentation for available information.
    lastSeconds= info.currentHeartRate;
    i = i & lastSeconds.size();
    var A = 0.0;
    for (var j = 0; j < lastSeconds.size(); j++)
    {
    A += lastSeconds[j];
    //B += lastSeconds[j];
    //C += j;
    //D += (lastSeconds[j] *lastSeconds[j]);
    }
    //N = lastSeconds.size();
    //slope = (N*A-B*C)/(N*D-B*B);
    //result = A/N;
    return A/lastSeconds.size();
    }


    }[/CODE]
  • Assuming you are writing code for your original post, this should work.

    class MyDataField extends Ui.SimpleDataField
    {
    var values;
    var index;

    function initialize(n) {
    values = new [n];
    for (var i = 0; i < n; ++i) {
    values = 0.0;
    }

    index = 0;
    }

    function compute(info) {

    // handle case where no heart rate data is available by
    // displaying nothing
    if (info.currentHeartRate == null) {
    return null;
    }

    values[index] = Math.pow(info.currentHeartRate, 4);
    index = (index + 1) % values.size();

    // the previous line is equivalent to this...
    // index += 1;
    // if (index == values.size()) {
    // index = 0;
    // }

    var sum = 0;
    for (var i = 0; i < values.size(); ++i) {
    sum += values;
    }

    sum /= values.size();

    return Math.pow(sum, .25);
    }

    return sum;
    }
    [/code]

    If you want to average over the last 10 seconds, you'd pass 10 to the constructor for the data field.
  • Former Member
    Former Member over 10 years ago
    Since everyone had a chance to write some code here, I'd like to add some :)

    I'd use Travis' code, but if you don't want false readings for the first n seconds you could make some small changes:
    - don't initialize the elements of values,
    - change the part where sum is calculated to

    var elements = values.size();
    if (values[values.size() - 1] == null) {
    elements = index;
    }

    for (var i = 0; i < elements; i += 1) {
    sum += values;
    }

    sum /= elements;
    [/CODE]
  • You should be able to just say...

    var not_ready_yet = (values[index] == null);

    values[index] = Math.pow(info.currentHeartRate, 4);
    index = (index + 1) % values.size();

    if (not_ready_yet) {
    return null;
    }


    That would only give you a result after you had N samples. The other option would be to do as you've done and average across the elements in the array that have been initialized. I think it could be done more simply as...

    var sum = 0;

    var i;
    for (i = 0; i < values.size(); ++i) {
    if (values== null) {
    break;
    }
    sum += values;
    }

    sum /= i;
    [/code]
  • Former Member
    Former Member over 10 years ago
    Thank you all for your answers!

    I was able to compute the expected increment on heart rate per minute by performing a linear regression on the las 60s hr data! I also was able to compute the NP for my bike trainer with a given power curve! Thanks for the help on the codes! I will finish my data fields today and them compile them (let's hope I have no big problems). Then I will try it on the 920xt! Yeah!!!!

    Jose