News:

;) This forum is the property of Proton software developers

Main Menu

A happy ending

Started by charliecoutas, Nov 08, 2026, 02:39 PM

Previous topic - Next topic

charliecoutas

We have a 7 segment clock display on the ceiling above our bed (it saves having to lean over to see a bedside clock) and this morning, at 07.30 I leapt out of bed to make our breakfast. On returning, Lynda, sleepily eyed, asked why I was up at 06.30? (She had looked at her watch). The clock was one hour fast.

The clock is driven by a little GPS module. I got out of bed and looked at the heating controller which is also driven by the same GPS module. "Wednesday the 10th of March 2007" it said. Having seen this before on another GPS module, I knew what it was. I am writing this up because it is surely bound to happen to some forum members at some time in the future. The problem is this:

The bright sparks who designed the GPS receivers decided that a 10 bit week counter would be enough. 1024 weeks is long enough not to bother anyone. Well no, 1024 weeks is 19.2 years so every 19.2 years the day, month and years will be wrong from then on. I think it's also known as an Epoch Error.

The solution last time was to root around in my Aladins cave and find another GPS module. But this time I reasoned that simply adding 19.2 years on to the (wrong) reported date would fix it. I found a few algorithms on the web and converted one from C to Positron. To my surprise it worked second time and I offer the code below. The brackets look a bit dangerous but Positron obviously handles the lack of brackets in the same way that C does. It will go wrong again in another 19.2 years but I don't care.

So get the day of month, month and year from the dicky GPS module and convert it to Julian days (the number of days since Julian Clary was born). Add 1024 weeks-worth days (7168) then de-convert it back again. There weren't many comments in the code I found but it works. I have probably used DWord where Word would do but I played safe):


; Get GPS data into:- Day_in_month, Month_no, Year_no. Jnd is the number of Julian days

    Calc_Julian()                             ;convert the wrongly reported date to "Julian days"
    Jdn = Jdn+7168                            ;Add 1024 weeks in days
    Reverse_Julian()                          ;convert back to corrected date   

Dim Ja      As Dword                          ;workspace
Dim Jy      As Dword                          ;year number
Dim Jm      As Dword                          ;month number
Dim Jdn     As Dword                          ;day in month number
   
Proc Calc_Julian()
    Ja = (14-Month_no)/12
    Jy = Year_no + 4800 - Ja
    Jm = Month_no + 12 * Ja - 3
    Jdn = Day_in_month + (153 * Jm + 2) / 5 + 365 * Jy + Jy / 4 - Jy / 100 + Jy / 400 - 32045
EndProc

Dim Jf As Dword
Dim Je As Dword
Dim Jg As Dword
Dim Jh As Dword

Proc Reverse_Julian()
    Jf = Jdn + 1401 + (((4 * Jdn + 274274) / 146097) * 3) / 4 - 38
    Je = 4 * Jf + 3
    Jg = (Je//1461) / 4
    Jh = 5 * Jg + 2
    Day_in_month = (Jh//153) / 5 + 1
    Month_no = (Jh / 153 + 2)//12 + 1
    Year_no = Je / 1461 - 4716 + (14 - Month_no) / 12
EndProc

top204

Nice one Charles, and thanks for sharing the excellently written words, and the code listing.

Regards
Les

charliecoutas

#2
Thanks Les. Out of interest I got ChatGPT to write the code for Julian Day Number generation. It had to be reminded who wrote Positron and that floating point was available if needed:

'-------------------------------------------------------------------------------
' Astronomical Julian Date (JD) Calculation using Floating-Point Math
' Target: Positron8 / Positron16 Compilers (Les Johnson)
'-------------------------------------------------------------------------------
Device = 18F25K22               ' Example device
Declare Xtal = 16

' Input Date & Time variables
Dim In_Day As Byte
Dim In_Month As Byte
Dim In_Year As Word
Dim In_Hour As Byte
Dim In_Minute As Byte
Dim In_Second As Byte

' Working and Output variables (Using Positron Float types)
Dim Calc_Year As Float
Dim Calc_Month As Float
Dim A_Corr As Float
Dim B_Corr As Corr              !!! Charlie here, I think that should be As Float !!!
Dim Day_Fraction As Float
Dim Final_JD As Float           ' Holds the complete fractional Julian Date

' Working integer variables for dropping remainders
Dim Tmp_Int1 As Dword
Dim Tmp_Int2 As Dword

'-------------------------------------------------------------------------------
' Main Program Execution
'-------------------------------------------------------------------------------
Main:
    ' Test Date/Time: November 10, 2026 at 12:30:00 UTC
    In_Day = 10
    In_Month = 11
    In_Year = 2026
    In_Hour = 12
    In_Minute = 30
    In_Second = 0

    GoSub Calculate_Float_JD
   
    ' Final_JD now contains the high-precision floating point value: 2461355.0208
    End

'-------------------------------------------------------------------------------
' Subroutine: Calculate_Float_JD
' Converts standard date/time parameters into a continuous Float Julian Date
'-------------------------------------------------------------------------------
Calculate_Float_JD:
    Calc_Year = In_Year
    Calc_Month = In_Month

    ' 1. Adjust for January and February shifting back to previous year
    If In_Month <= 2 Then
        Calc_Year = Calc_Year - 1
        Calc_Month = Calc_Month + 12
    EndIf

    ' 2. Calculate Gregorian Leap Year Correction terms
    Tmp_Int1 = Calc_Year / 100                 ' Forces integer truncation (Floor)
    A_Corr = Tmp_Int1
    Tmp_Int1 = A_Corr / 4                      ' Forces integer truncation (Floor)
    B_Corr = 2 - A_Corr + Tmp_Int1

    ' 3. Calculate core date components using truncated chunks
    Tmp_Int1 = 365.25 * (Calc_Year + 4716)     ' Equates to Floor(365.25 * (Y+4716))
    Tmp_Int2 = 30.6001 * (Calc_Month + 1)      ' Equates to Floor(30.6001 * (M+1))

    ' Combine chunks into the base Julian Day at Noon UT
    Final_JD = Tmp_Int1 + Tmp_Int2 + In_Day + B_Corr - 1524.5

    ' 4. Add the day fraction based on time (adjusted for the Noon UT epoch start)
    Day_Fraction = (In_Hour * 3600.0) + (In_Minute * 60.0) + In_Second
    Day_Fraction = Day_Fraction / 86400.0
   
    ' Add time component to find final continuous position
    Final_JD = Final_JD + Day_Fraction
Return

Charlie

tumbleweed

You might be better off with the original dword code... I don't know that single precision floating-point has enough accuracy (6-7 decimal digits)

▲