jtauber / mars-clock

an (in progress) interactive explanation of the time on Mars
https://jtauber.github.io/mars-clock/
MIT License
60 stars 9 forks source link

MTC seconds roll to 60 #9

Open mMerlin opened 3 months ago

mMerlin commented 3 months ago

Are you still keeping an eye on this repo?

I spotted a bug in the MTC hh:mm:ss display. I am writing my own python based clock based on the glss algorithm information, and used Mars Clock as a cross check of my implementation.

The bug: millis = 947,116,815,000 ¦ MTC 23:59:59 millis = 947,116,816,000 ¦ MTC 23:59:60 millis = 947,116,817,000 ¦ MTC 00:00:01

The seconds is rolling over from 59 to 60, instead of to zero and incrementing the minutes. That looks like the fractional seconds are being rounded for the seconds display value, but because it is not really the next second (minute) yet, the minutes do not increment. For a clock display, I think that should truncate (or use floor), so that the seconds value does not increment until the tenths of a second truly rolls over.

Those times are a few seconds off from what I expected from https://www.giss.nasa.gov/tools/mars24/help/algorithm.html and the near coincident worked example for 2000/01/06. 2000/01/06 00:00:00 should be MTC 23:59:39 (21 seconds before Mars midnight) A glance at the code shows that you are using a fixed value of 37 for the tai-utc offset. That likely accounts for the difference. I implemented a table lookup to handle the changing offset. Only works from 1972/01/01 forward.

edgar-bonet commented 3 months ago

@mMerlin: You are right, the call to Math.round() in h_to_hms() should be Math.floor() instead.

It seems the original intent of the code was to round to the nearest second. If this is really what we want, then it can be achieved by rounding the total number of seconds rather than the ss field:

diff --git a/index.html b/index.html
index 9516c51..46e8c16 100644
--- a/index.html
+++ b/index.html
@@ -452,13 +452,13 @@
                 return Math.sin(deg * Math.PI / 180);
             }
             function h_to_hms(h) {
-                var x = h * 3600;
+                var x = Math.round(h * 3600);
                 var hh = Math.floor(x / 3600);
                 if (hh < 10) hh = "0" + hh;
                 var y = x % 3600;
                 var mm = Math.floor(y / 60);
                 if (mm < 10) mm = "0" + mm;
-                var ss = Math.round(y % 60);
+                var ss = y % 60;
                 if (ss < 10) ss = "0" + ss;
                 return hh + ":" + mm + ":" + ss;
             }
mMerlin commented 3 months ago

As noted in my initial issue report, my preference would be not use any rounding for a clock display. So

var x = Math.floor(h * 3600);
…
var ss = y % 60;

Just changing the the existing Math.round to Math.floor would do it as well.

Since I am working in Python, I have other options. Starting from an integer number of seconds, divmod does the job in 2 lines, then f-string formatting can be used to add any needed leading zeros. That function body becomes 4 lines (or 3 if the int() is embeded in the first divmod).

x = int(h * 3600) % 86400
y, ss = divmod(x, 60)
hh, mm = divmod(y, 60)
return f'{hh:02}:{mm:02}:{ss:02}'

Though I prefer more descriptive variable names.

timparenti commented 3 months ago

Agreed with @mMerlin. Modern H:M:S time displays pretty much universally truncate fractional seconds in the same way a simpler H:M display truncates the fractional minutes implied by hidden seconds.

As such, floor() definitely yields expected behavior, not round().

jtauber commented 2 months ago

Agreed on round -> floor. I'll fix this when I next get a chance (although a PR is welcome)

jtauber commented 2 months ago

@mMerlin Note that for some related Python work, you might want to look at my initial attempts a decade ago at https://github.com/jtauber/ephemerides

mMerlin commented 2 months ago

I browsed that. With the scenario I am working with, the details are a bit different, but the basic logic looks similar to prototype code I used along the way.

I did not exactly 'port' the mars-clock. I took the equations and constants from https://www.giss.nasa.gov/tools/mars24/help/algorithm.html, treated the listed constants as 'exact', then used basic algebraic operations to derive an equation that did not need any floating point operations. The target is a Mars Clock on an embedded device running CircuitPython. Limited hardware floating point precision. What I ended up with uses pure integer math to calculate right down to nanoseconds resolution. Which is the maximum resolution of the hardware timers. With other factors, accuracy is no where near that, but with an 'accurate' nanosecond linux timestamp, it gets a nanosecond accurate MTC. It's not even complex, once the algebraic operations merged all of the conversions that were happening in the algorithm page. I derived an 'exact' nanosecond (actually 1/1000 ns) offset from unix epoch to a specific MTC midnight. After that, it is simple nanoseconds per Mars day offsets, handled by divmod, leaving the nanoseconds since the previous Mars midnight. More divmod to convert to Mars hours, minutes, seconds, with a remainder in integer nanoseconds again.

Since I tend toward OCD, the link from that reference to PyEphem is interesting. I've always been 'annoyed' by the vagueness in most (news type) descriptions of positions in space.