A GPS disciplined clock

Dependencies:   net lpc1768 crypto clock web log

gps/pps.c

Committer:
andrewboyson
Date:
2019-10-28
Revision:
80:6a4e8d12d20f
Parent:
79:992d470e0619
Child:
81:a1de28caf11a

File content as of revision 80:6a4e8d12d20f:

#include <stdbool.h>
#include <time.h>

#include "log.h"
#include "clkgov.h"
#include "mstimer.h"
#include "hrtimer.h"
#include "gpio.h"
#include "bitband.h"
#include "gps.h"

#define IO0INTENR_ADDR 0x40028090
#define IO0INTCLR_ADDR 0x4002808C
#define     ISER0_ADDR 0xE000E100

#define     FIX_PIN FIO0PIN(23)
#define  ENABLE_DIR FIO0DIR(24)
#define  ENABLE_SET FIO0SET(24)
#define  ENABLE_CLR FIO0CLR(24)
#define     PPS_PIN FIO0PIN(17)
#define PPS_INT_ENR BIT_BAND4(IO0INTENR_ADDR, 17) = 1
#define PPS_INT_CLR BIT_BAND4(IO0INTCLR_ADDR, 17) = 1
#define ISER0 *((volatile unsigned *) ISER0_ADDR)

static volatile uint32_t pulseHr = 0;        //Set after a PPS interrupt. Good for accurate interval between two pulses
static volatile uint32_t pulseMs = 0;        //Set after a PPS interrupt. Good for comparing between PPS and NMEA
static volatile bool     pulseValid = false; //Set after the first PPS interrupt
static volatile bool     hadPulse   = false; //Set after each PPS interrupt

int      PpsMsSinceLastPulse = 0;     //Calculated after a pulse from pulseHr
uint32_t PpsLastPulseMs      = 0;     //Copied     after a pulse from pulseMs
bool     PpsLastPulseIsSet   = false; //Copied     after a pulse from pulseValid

void PpsHandler() //Called by a PPS interrupt
{
    PPS_INT_CLR;
    ClkGovSyncPpsI();
    pulseHr    = HrTimerCount();
    pulseMs    = MsTimerCount;
    pulseValid = true;
    hadPulse   = true;
}

#define CONFIDENCE_OK  10
#define TIMEOUT_MS   1000

static int confidence = 0;
static void confidenceReset()
{
    if (confidence == CONFIDENCE_OK) GpsLog("PPS is not stable\r\n");
    confidence = 0;
}
static void confidenceIncrease()
{
    if (confidence == CONFIDENCE_OK) return;
    confidence++;
    if (confidence == CONFIDENCE_OK) GpsLog("PPS is stable\r\n");
}
bool PpsIsStable() { return confidence == CONFIDENCE_OK; }

static bool pulseWasStopped = true;
static void checkForPulsesStoppingAndStarting()
{
    if (PpsMsSinceLastPulse > TIMEOUT_MS)
    {
        if (!pulseWasStopped)
        {
            GpsLog("PPS pulses have stopped\r\n");
            pulseWasStopped = true;
        }
        confidenceReset();
    }
    if (hadPulse)
    {
        static bool prevPulseValid = false;
        if (pulseWasStopped)
        {
            if (prevPulseValid) GpsLog("PPS pulses have restarted after %d ms\r\n", PpsMsSinceLastPulse);
            else                GpsLog("PPS pulses have started\r\n");
            pulseWasStopped = false;
        }
        prevPulseValid = pulseValid;
    }
}

static void buildConfidence()
{
    static uint32_t prevPulseHr = 0;
    static bool     prevPulseValid = false;
    if (prevPulseValid)
    {
        uint32_t hrSinceLastPulse        = pulseHr - prevPulseHr;
        uint32_t msSinceLastPulse        = hrSinceLastPulse / (HR_TIMER_COUNT_PER_SECOND / 1000);
        uint32_t msSinceLastPulseMod1000 = msSinceLastPulse % 1000;

        if (msSinceLastPulseMod1000 == 999 || msSinceLastPulseMod1000 == 000)
        {
            confidenceIncrease();
        }
        else
        {
            GpsLog("PPS interval %03d is not 999 or 1000 ms\r\n", msSinceLastPulse);
            confidenceReset();
        }
    }
    prevPulseHr    = pulseHr;
    prevPulseValid = pulseValid;
}

void PpsMain()
{
    //Wait for 100ms after the start up in order to ignore initial glitches on the PPS line
    static bool afterStartup = false; //Need to store the timeout as hr timer rolls over every 44 seconds
    if (HrTimerCount() > HR_TIMER_COUNT_PER_SECOND / 10) afterStartup = true;
    if (!afterStartup)
    {
        hadPulse = false; //Cancel the pulse
        return;
    }
    
    checkForPulsesStoppingAndStarting(); //Do this before storing PPsMsSinceLastPulse to allow log to show when they started not zero
    
    PpsMsSinceLastPulse = HrTimerSinceMs(pulseHr);
    PpsLastPulseMs      = pulseMs;
    PpsLastPulseIsSet   = pulseValid;
    
    if (hadPulse)
    {
        buildConfidence();
        GpsHadPps();
    }

    hadPulse = false;
}

void PpsInit()
{
    ENABLE_SET;       //Set the enable to high
    ENABLE_DIR = 1;   //Set the enable pin direction to 1 == output
    PPS_INT_ENR;      //Set the PPS pin to be interrupt on rise
    ISER0 |= 1 << 21; //6.5.1 bit1 == Interrupt set enable for EINT3. It MUST be enabled even for GPIO interrupts - I checked.
}