3

I'm getting ready to do some work on an embedded firmware prototype that has a GPS chip attached to it (straight C on an arm0, no OS).

I'm torn on how to model UTC instants and time deltas. Traditionally in C, I'd just pick a resolution and an epoch offset and model things with "big enough" integers, but I wasn't worrying about UTC, just an internal time reference.

Lately, I've been working a bunch in Swift/Kotlin/Python3, and I found myself wondering if I wouldn't be better served by building a lightweight higher order representation of instant and deltas in C. Something where I had a distinct struct for each entity, and wrote a few functions to do the math I need between them.

This would require a little more time (to develop), but possibly give me a little more (type) safety.

I guess I'm curious how others who have had to model UTC times in embedded C have chosen to do so. Should I go with the classic offset/epoch integer model? Or model with actual structs?

(I'm not really worried about performance for this)

CLARIFICATION?

Lots of embedded processes have to measure time or do things at certain times. I've done a bunch of those. Some sort of rolling systick counter is your time base, and you do math against it. The only real drawback I've found over the years, is that it is possible to have bugs related to the semantic differences between a time marker (an instant that occurs on the the time counter) and a duration or delta, between two such times. Since both are (u)ints, it's easy to mix up the two with no warnings.

The tricky part here is the introduction of the GPS. GPS NMEA times are in UTC field values (e.g. yymmdd, hhmmss). While the UTC gets rid of the TimeZone nightmare, it doesn't get rid of things like leap years. So at bare minimum, I have to implement the math to covert said field values into a seconds offset, and then correlate/map that with the local time counter. This lead to the question: If I have to do that anyway, why not just go all the way. My goal is to schedule things to happen at certain UTC times.

  • 5
    There is no "best" without knowing your application. Tell us more about how you'd use this model. Dates and times are notoriously unholy subjects, very difficult to get right without incomprehensibly strange bugs appearing in seemingly random scenarios. If you can operate exclusively in the 'seconds since epoch' model, you should. – whatsisname Sep 29 '18 at 1:14
  • In embedded software, you not only have to consider performance, but also resource (RAM/ROM) usage. – Bart van Ingen Schenau Sep 29 '18 at 8:51
  • 5
    Why doesn't time_t and the functions in time.h suffice? – Robert Harvey Sep 29 '18 at 18:44
  • @BartvanIngenSchenau : I think I'm good on RAM/ROM. I wasn't envisioning anything huge. But yes, something like the Olson Databases would be less than desirable. – Travis Griggs Oct 1 '18 at 14:42
  • @RobertHarvey time_t and time.h could suffice. But in an embedded build with no OS and just straight libc, you have to do a bit of work to actually make the behavior(s) in time.h work. You have to find a timer on your chip (I'm doing this of course already), create a correlation between it and _gettimeofday, deal with chip sleeping events, etc. – Travis Griggs Oct 1 '18 at 15:17
2

Best way to model UTC time and deltas

Look to standards to guide you.

ISO 8601 discusses many of the issues of concern both to time stamps and to time deltas.

Even though it is primarily related to textual time presentation, the format of time as text relates directly to data structures in C.

Wall time vs. clock time.

The time on the wall is a world-wide agreed upon idea of what time is it when its UTC.

Your embedded device simply needs a clock counting at some rate and, unless you log data in non-volatile memory, can start at 0 each time the units powers on.

The relationship between wall time and your clock is usually just an offset and scale. The scale may be assumed fixed, factory calibrated or sporadically re-calibrated. The offset, that is the tricky part, as code needs to sync up with the outside world obliges info exchange at least once.

Simplicity

My goal is to schedule things to happen at certain UTC times.

I'd use the idea of local UTC for scheduling. That is your processor's best understanding of UTC, which may drift over time with the real one. On occasion, it may require syncing - be preprepared for that. Code can use convenient units (instead of seconds) for your processor like your clock frequency and int64_t. This linear approach will simplify scheduling. The YMDhms translations to/from your_time_t need only occur when communicating outside the processor (to people or other machines).

0

This would require a little more time (to develop), but possibly give me a little more (type) safety.

This is the heart of the tradeoff, and no one can answer that for you because it depends on your use case:

  • If you are doing lots of manipulation of time, it's probably better to write the library. You would have to have many routines interacting with time in different ways.
  • If you only interact with time a little (mostly just 'measure elapsed time') then you should just use an int and move on. If you have time later, you can always circle back and re-factor the code into a library with a struct. But it's more likely that other parts of your program will soak up your attention, so time spent on writing a time library will just cause delays for other parts.

Also relevant: This article has some interesting observations about the different uses of time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.