
I'm currently working on a clock using a Qualia S3 board with a 4" 720x720 round display. The idea is to display an "analog" clock face on it. The clock will show your timezone on Earth and coordinated Mars time (MTC). In looking into MTC, I found the Mars24 Sunclock, which is a piece of desktop software from NASA that shows your local time, UTC time and then MTC.
The algorithm for calculating MTC was published by NASA researcher Michael Allison in 1997 and then updated in 2000 by Michael Allison and Megan McEwen. It is nicely summarized on the Timekeeping on Mars Wikipedia page:
The Mars Sol Date (MSD) can be computed from the Julian date referred to Terrestrial Time (TT), as[46]
MSD = (JDTT − 2405522.0028779) / 1.0274912517...This leads to the following formula giving MSD from the UTC-referred Julian date:
MSD = (JDUTC + (TAI−UTC)/86400 − 2405522.0025054) / 1.0274912517where the difference TAI−UTC is in seconds. JDUTC can in turn be computed from any epoch-based time stamp, by adding the Julian date of the epoch to the time stamp in days. For example, if t is a Unix timestamp in seconds, then
JDUTC = t / 86400 + 2440587.5It follows, by a simple substitution:
MSD = (t + (TAI−UTC)) / 88775.244147 + 34127.2954262MTC is the fractional part of MSD, in hours, minutes and seconds:[3]
MTC = (MSD mod 1) × 24 h
I wish I could tell you that I can read through that and understand it and as a result write a block of Python code that performs that calculation without a lot of trial and error. Unfortunately, I really struggle with math so I turned to ChatGPT-4. I was incredibly reticent about using ChatGPT or any other LLM but I have to say that I've found ChatGPT-4 to be a very helpful tool when writing code that saves a lot of time for tasks like this. I entered that algorithm into ChatGPT-4 and asked it to convert it to CPython and it popped out a script that worked perfectly when compared to the Mars24 clock. You can see my full chat log here.
import time import math def get_mtc(unix_timestamp, tai_utc_diff): """ Calculate Coordinated Mars Time (MTC) given a Unix timestamp and TAI-UTC difference. @param unix_timestamp: Unix timestamp in seconds @param tai_utc_diff: TAI-UTC difference in seconds @return: MTC in HH:MM:SS format """ # Calculate MSD msd = (unix_timestamp + tai_utc_diff) / 88775.244147 + 34127.2954262 print(msd) # Calculate MTC mtc = (msd % 1) * 24 print(msd % 1) # Convert MTC to HH:MM:SS hours = int(mtc) minutes = int((mtc - hours) * 60) seconds = int((mtc - hours - minutes / 60) * 3600) return f"{hours:02d}:{minutes:02d}:{seconds:02d}" current_time = int(time.time()) print(current_time) tai_utc_diff = 37 # As of 2020, this value is 37 seconds. You'll need to update it if it changes. mtc = get_mtc(current_time, tai_utc_diff) print(f"MTC: {mtc}")
The time module differs slightly from CPython to CircuitPython. So in bringing this formula over to CircuitPython I made a few changes:
- The current time is fetched via Adafruit IO
- The struct time object from IO is converted to seconds
- The fetched time is in my timezone, so I compute the UTC offset in seconds and then add it to the fetched time to generate the unix time.
def mars_time(): dt = io.receive_time() print(dt) utc_offset = 3600 * abs(timezone) tai_offset = 37 millis = time.mktime(dt) unix_timestamp = millis + utc_offset # Convert to MSD msd = (unix_timestamp + tai_offset) / 88775.244147 + 34127.2954262 print(msd) # Convert MSD to MTC mtc = (msd % 1) * 24 mtc_hours = int(mtc) mtc_minutes = int((mtc * 60) % 60) mtc_seconds = int((mtc * 3600) % 60) print(f"Mars Time: {mtc_hours:02d}:{mtc_minutes:02d}:{mtc_seconds:02d}") return mtc_minutes, mtc_hours
However, the result was incorrect and not just slightly but up to 15-20 minutes. On top of that, the time was only updating about 4-5 times an hour. I realized that the Mars Sol Date calculation (msd
value in the code) was being rounded to one decimal place. For example, the CPython script would return 53264.93340539572
while the CircuitPython formula would return 53264.9
. I was hitting the float limit of CircuitPython, which I had never encountered before. CicuitPython floats have only about 5-1/2 digits of precision. More detail can be found in the CircuitPython Essentials guide.
Concerned that this project was doomed, I turned to the #help-with-circuitpython channel on the Adafruit Discord. Discord user elpekenin helpfully suggested the Jepler_CircuitPython_udecimal library from CircuitPython core developer Jeff. This library is a subset of the CPython Decimal library, which is designed for arithmetic and greater accuracy over float
numbers. I modified my function in CircuitPython:
def mars_time(): dt = io.receive_time() print(dt) utc_offset = 3600 * abs(timezone) tai_offset = 37 millis = time.mktime(dt) unix_timestamp = millis + utc_offset # Convert to MSD msd = (unix_timestamp + tai_offset) / Decimal("88775.244147") + Decimal("34127.2954262") print(msd) # Convert MSD to MTC mtc = (msd % 1) * 24 mtc_hours = int(mtc) mtc_minutes = int((mtc * 60) % 60) mtc_seconds = int((mtc * 3600) % 60) print(f"Mars Time: {mtc_hours:02d}:{mtc_minutes:02d}:{mtc_seconds:02d}") return mtc_minutes, mtc_hours
And great success! msd
was now returning an identical result to the CPython calculation and as a result accurate MTC. Big thanks to elpekenin and Jeff for writing that library. I will be documenting the clock in a Learn Guide soon, but I wanted to highlight this particular challenge in case others run up against the float limitations in CircuitPython when working on projects with formulas like this.