Andrew Boyson / clock

Dependents:   oldheating gps motorhome heating

clk/clkutc.c

Committer:
andrewboyson
Date:
2019-01-09
Revision:
50:b804e93ccc1e
Parent:
49:e4424cc18bcb
Child:
51:826c58fbfaed

File content as of revision 50:b804e93ccc1e:

#include <stdint.h>

#include "clktime.h"
#include      "tm.h"

#define GPREG1 (*((volatile unsigned *) 0x40024048))

/*
+----+----+----+----+----+----+----+----+
|Flgs|  Leap months |     Leap count    |
|UUDE|    12 bits   |      16 bits      |
+----+----+----+----+----+----+----+----+

Leap months: 12 bits will hold 4096 months or 341 years

Leap count: 16 bits will hold enough leaps seconds for 60,000 years

Flgs
U = unused
D = direction: 1 to subtract; 0 to add
E = Enable: 1 if leap to take into account at the start of the leap month; 0 if already taken or to be ignored
*/

//Leap seconds
int     ClkUtcLeapSecondCount   = 0; //12 bits holds enough leap seconds for at least 300 years.
int64_t ClkUtcLeapSecondCount64 = 0;
static void makeLeapSecondCount64() { ClkUtcLeapSecondCount64 = ((int64_t)ClkUtcLeapSecondCount) << CLK_TIME_ONE_SECOND_SHIFT; }
void ClkUtcSetLeapSecondCount(int value)
{
    ClkUtcLeapSecondCount = value;
    makeLeapSecondCount64();
    GPREG1 = GPREG1 & 0xFFFF0000 | value & 0x0000FFFF;
}
void ClkUtcAddLeapSecondCount(int value)
{
    ClkUtcSetLeapSecondCount(ClkUtcLeapSecondCount + value);
}

//Next leap second
int     ClkUtcNextLeapMonth1970 = 0;
int64_t ClkUtcNextLeapSecond64 = 0;
static void makeNextLeapSecond64()
{
    int year  = ClkUtcNextLeapMonth1970 / 12 + 1970;
    int month = ClkUtcNextLeapMonth1970 % 12 + 1;
    struct tm tm;
    TmFromInteger(year, month, 1, 0, 0, 0, &tm);
    time_t t = TmUtcToTimeT(&tm);
    ClkUtcNextLeapSecond64 = (int64_t)t << CLK_TIME_ONE_SECOND_SHIFT;
}
void ClkUtcSetNextLeapMonth1970(int value)
{
    ClkUtcNextLeapMonth1970 = value;
    makeNextLeapSecond64();
    GPREG1 = GPREG1 & 0xF000FFFF | (uint32_t)value << 16 & 0x0FFF0000; //Precedence order: shifts then ands then ors.
}

bool ClkUtcNextLeapEnable   = false;
bool ClkUtcNextLeapBackward = false;
void ClkUtcSetNextLeapEnable(bool value)
{
    ClkUtcNextLeapEnable = value;
    if (value) GPREG1 |= 0x10000000;
    else       GPREG1 &= 0xEFFFFFFF;
}
void ClkUtcSetNextLeapBackward(bool value)
{
    ClkUtcNextLeapBackward = value;
    if (value) GPREG1 |= 0x20000000;
    else       GPREG1 &= 0xDFFFFFFF;
}


void ClkUtcInit(void)
{
    ClkUtcLeapSecondCount   =  GPREG1 & 0x0000FFFF;
    makeLeapSecondCount64();
    ClkUtcNextLeapMonth1970 = (GPREG1 & 0x0FFF0000) >> 16;
    makeNextLeapSecond64();
    ClkUtcNextLeapEnable    =  GPREG1 & 0x10000000;
    ClkUtcNextLeapBackward  =  GPREG1 & 0x20000000;
}


int64_t ClkUtcFromTai(int64_t tai) { return tai - ClkUtcLeapSecondCount64; }
int64_t ClkUtcToTai  (int64_t utc) { return utc + ClkUtcLeapSecondCount64; }

void    ClkUtcCheckAdjustLeapSecondCount(int64_t tai)
{
    if (!ClkUtcNextLeapEnable) return; //Do nothing if leaps are disabled
    
    int64_t utc = ClkUtcFromTai(tai);
    int64_t leapStart = ClkUtcNextLeapSecond64 - (ClkUtcNextLeapBackward ? 1 : 0);
    
    if (utc < leapStart) return; //Do nothing until reached the leap start
    
    if (ClkUtcNextLeapBackward) ClkUtcAddLeapSecondCount(-1); //skip   59
    else                        ClkUtcAddLeapSecondCount(+1); //repeat 59
    
    ClkUtcSetNextLeapEnable(false);
    
}