FitContributor::Field::SetData should accept null as a value

And gaps in both native and CIQ activity FIT records are not displayed correctly by Garmin Connect.

Hopefully I'm not missing something, but I think there's a major gap in the functionality for FitContributor::Field::SetData. Native fields can have "gaps" (null/no data) in their data (in the middle of an activity) but 3rd-party fields can't -- once they write their first value, they have to keep writing for the rest of the activity. So if there's an actual gap in the real data, a 3rd-party app has no choice but to write zeros.

Consider the case of going for a run recording HR (from wrist) to the FIT file. Let's say the runner takes off their watch in the middle run and the wrist HR turns off. The native graph in Garmin Connect will show a nice gap where that happened. It won't show zeros, it will show nulls (no values) -- if you roll over the graph it will skip that portion completely and if you look at the FIT file contents, there are no HR records for that time period.

Now consider the case of a Connect IQ field recording the exact same data.

If there's no HR data available at beginning of the run and the field hasn't called setData yet, no problem. The records for the 3rd-party field won't be present for that time period. But the first time the app calls setData, it's obligated to keep doing so every second, even if it has no data. If you don't call setData, the device just records the last value you set. And if you call setData with null, it crashes.

So in the example of runner who takes off his wrist HR in the middle of a run:
- The native field is able to write no data
- The 3rd party field has two choices:
-- Don't call setData, in which case the graph will have a nice horizontal line where the same old data was written over and over again
-- Call setData with 0, which doesn't match the actual situation. HR data was unavailable -- the runner's heart rate wasn't zero

Oh, and I tested the scenario where a CIQ app doesn't write data at the beginning of the activity (e.g. recording HR while not wearing watch)-- this is displayed properly in the Garmin Connect mobile app but not the website. The website acts as though the first available value existed all the way to the beginning (even if you waited for a few minutes to start recording.) The native field has the opposite problem: Garmin Connect mobile shows a bad graph for the native HR, but the website shows the correct graph.

It would be nice to have some kind of parity with native fields, especially if the data is to be analyzed automatically later.
  • And I think in this case it's based on the fit specification, and not a bug in CIQ.

  • And I think in this case it's based on the fit specification, and not a bug in CIQ.

    Is it possible for native fields to have gaps in the data? Based on what I saw, it is possible. If the FIT specification doesn't allow for that, then I must be wrong about that (which is certainly possible).

    I did take the time to write the following. Maybe I misunderstood what I saw? I feel like I wouldn't have made up something like "if you look at the FIT file contents, there are no HR records for that time period", but who knows.

    Consider the case of going for a run recording HR (from wrist) to the FIT file. Let's say the runner takes off their watch in the middle run and the wrist HR turns off. The native graph in Garmin Connect will show a nice gap where that happened. It won't show zeros, it will show nulls (no values) -- if you roll over the graph it will skip that portion completely and if you look at the FIT file contents, there are no HR records for that time period.
  • Read the original post, he explains pretty well what he expects (and I as well). Exactly the same as the non-CIQ (build-it) fit values.

    i.e. good as the built-in field's missing data is displayed (gap):

    i.e not good as the CIQ data is displayed:

  • Also, as stated in the OP, even a CIQ custom field can have "no data" at the *beginning* of activity (just avoid calling setData() until you have data). It's just that once you call setData() for the *first* time, you'll be writing data at every opportunity whether you want to or not.

    So in a sense, this feature is inconsistent even with itself. Why can I have no data at the beginning of an activity but not the middle or the end? (The answer is that the design of setData() doesn't allow for it.)

    EDIT: another way of looking at this feature request is that we need a "clearData()" function in addition to setData(). Either way, I'm sure it will never happen.

  • BTW just looked at the docs for Toybox.FitContributor.Field:

    "If setData() is called before the previous data is written out, the previous value will be lost and replaced by the current data. For this reason, we do not recommend using this feature for time-sensitive data requiring sub-second granularity."

    They don't say (nor do I know of, though that doesn't mean it doesn't exist somewhere in the docs that I didn't find yet) what should be used instead. But then this sentence means: if you need precision that is higher than 1Hz then don't use Field => don't use Fit. OR there's some hidden api that the built-in fields use that we can't use, and they accidentally put this sentence about "do not recommend" to the public docs instead of the in-house docs...

  • "If setData() is called before the previous data is written out, the previous value will be lost and replaced by the current data. For this reason, we do not recommend using this feature for time-sensitive data requiring sub-second granularity."

    They don't say (nor do I know of, though that doesn't mean it doesn't exist somewhere in the docs that I didn't find yet) what should be used instead. But then this sentence means: if you need precision that is higher than 1Hz then don't use Field => don't use Fit. OR there's some hidden api that the built-in fields use that we can't use, and they accidentally put this sentence about "do not recommend" to the public docs instead of the in-house docs...

    I wouldn't read too much into that, imo. They're just saying this isn't suitable for high-frequency data, not implying that they have something for us which is.

    OR there's some hidden api that the built-in fields use that we can't use

    Well, native software can do lots of stuff that CIQ apps can't, so I think it's a given that there's a whole native API which is inaccessible to CIQ.

  • "Honestly, I think Garmin should auto-lock super old threads like this. Not much good ever comes of resurrecting them, IMO. If there's further interest in a topic, a new topic could be created (maybe with a link to the old topic.)"

    Though maybe it would be a reasonable feature to ask for the owner of the thread (the opener) to be able to lock it...

    That would maybe "fix" your current problem (that I woke up your 4 ear old thread) but certainly not the real problem that we're writing about, nor any other problems, and most specifically not the problem that Garmin couldn't care less about things like this...

  • That would maybe "fix" your current problem (that I woke up your 4 ear old thread)

    I don't mind but I see a lot of super old threads being necro'd, and it's usually by spammers. In this case I welcome the discussion although I don't have any hope of the issue being resolved. If you'd like to open a bug report I'll certainly support you.

    It's just that modern discussion forums (like reddit) tend to lock old threads. This obviously has pros and cons, but I feel like the pros outweigh the cons in most cases.

  • not the problem that Garmin couldn't care less about things like this...

    To be fair I don't think addressing this kind of thing is going to help their bottom line. Same with the "please respect FIT native_num" request that comes up every now and then.

    I say this a lot, but 99.999% of ppl I run with (who use Garmins) couldn't care less about Connect IQ (except maybe for watchfaces).

    I'm also starting to see a sizeable minority of Coros and Apple Watch users....

  • The fit contrib data will be written every second, even if setData isn't called for something lie HR.  That's why setData must be called before the data is written.  setData does not cause the data to be written