A GPS disciplined clock

Dependencies:   net lpc1768 crypto clock web log

gps/gps.c

Committer:
andrewboyson
Date:
2018-01-31
Revision:
1:1c4013b67497
Parent:
0:67724a462d86
Child:
4:108157115360

File content as of revision 1:1c4013b67497:

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

#include "log.h"
#include "defs.h"
#include "nmea.h"
#include "sync.h"
#include "timer.h"
#include "led.h"

#define GPS_ENABLE_MASK_0 (1UL << 24)
#define GPS_FIX_MASK_0    (1UL << 23)
#define GPS_PPS_MASK_0    (1UL << 17)

#define CONFIDENCE_OK  60
#define MIN_MS_SINCE_PULSE 150

#define BUCKET_COUNT 20
uint32_t buckets[BUCKET_COUNT] = {0};

static int bucketEnumerateIndex = 0;
void GpsBucketEnumerateStart()
{
    bucketEnumerateIndex = 0;
}
bool GpsBucketEnumerate(int size, char *text) //returns false if there is no more information
{
    if (bucketEnumerateIndex >= BUCKET_COUNT) return false;
    snprintf(text, size, "%d\r\n", buckets[bucketEnumerateIndex]); //snprintf always teminates with a null character after n-1 characters
    bucketEnumerateIndex++;
    return true;
}

static int confidence = 0;
bool GpsStable = false;
static time_t gpsTime = 0;
static uint32_t msTimer;
static volatile bool hadPulse;

__irq void GpsPpsHandler()
{
    LPC_GPIOINT->IO0IntClr = GPS_PPS_MASK_0;
    SyncPpsI();
    hadPulse = true;
}

void pulseN(int ms)
{    
    int missed = (ms + 500) / 1000;
    int ppsOffset = ms % 1000;
    
    gpsTime += missed;
    
    if (ppsOffset != 999 && ppsOffset != 0)
    {
        LogTimeF("GPS %4d ms PPS interval is not 999 or 000 ms - sync disabled\r\n", ms);
        confidence = 0;
    }
    
    if (confidence == CONFIDENCE_OK) SyncPpsN(gpsTime);
    
}
void GpsNmeaTimeReceived(time_t nmeaTime, int nmeaMs)
{
    //Ignore time messages if the module is not ready or has no fix
    if (!NmeaHaveFix) return;
    if (NmeaModuleReadiness != NMEA_READY) return;
    
    //Get the time of the message relative to the last pulse
    int ms = TimerSinceMs(msTimer);
    
    //Add time to bins to allow display of distribution
    int bucket = ms * BUCKET_COUNT / 1000;
    if (bucket < BUCKET_COUNT) buckets[bucket]++;

    //Check for non zero ms in NMEA
    if (nmeaMs) LogTimeF("GPS %4d ms NMEA time has non zero ms = %d\r\n", ms, nmeaMs);

    //Ignore time messages which have probably overflowed from a previous pulse
    if (ms < MIN_MS_SINCE_PULSE)
    {
        LogTimeF("GPS %4d ms NMEA time message ignored as received before %d ms\r\n", ms, MIN_MS_SINCE_PULSE);
        return;
    }
    
    //Handle the time message
    if (gpsTime == nmeaTime)
    {
        confidence++;
        if (confidence > CONFIDENCE_OK) confidence = CONFIDENCE_OK; 
    }
    else
    {
        confidence = 0;
        if (gpsTime) LogTimeF("GPS %4d ms NMEA time differs by %+d seconds so corrected pps count - sync disabled\r\n", ms, nmeaTime - gpsTime);
        else         LogTimeF("GPS %4d ms not set so setting pps count to NMEA time - sync disabled\r\n", ms, nmeaTime - gpsTime);
        gpsTime = nmeaTime;
    }
}
void GpsMain()
{    
    NmeaMain();
    
    //Disable syncing until Nmea module is ready
    if (NmeaModuleReadiness != NMEA_READY) confidence = 0;
    
    //Handle the arrival of a pulse
    int ms = TimerSinceMs(msTimer);
    bool pulseIsStopped = ms > 1000;
    if (hadPulse)
    {
        msTimer = TimerNowCount();
        pulseN(ms);
        hadPulse = false;
        pulseIsStopped = false; //This gets the signal 1 scan before the ms is reset
    }

    //Check pulses are arriving
    static bool pulseWasStopped = false;
    if ( pulseIsStopped) confidence = 0;
    if ( pulseIsStopped && !pulseWasStopped) LogTimeF("GPS %4d ms PPS pulses have stopped - sync disabled\r\n", ms);
    if (!pulseIsStopped &&  pulseWasStopped) LogTimeF("GPS %4d ms PPS pulses have started\r\n", ms);
    pulseWasStopped = pulseIsStopped;
    
    
    //Record any fix quality changes
    static bool lastHaveFix = false;
    if (!NmeaHaveFix) confidence = 0;
    if ( NmeaHaveFix && !lastHaveFix) LogTimeF("GPS %4d ms NMEA fix acquired\r\n", ms);
    if (!NmeaHaveFix &&  lastHaveFix) LogTimeF("GPS %4d ms NMEA fix lost - sync disabled\r\n", ms);
    lastHaveFix = NmeaHaveFix;
    
    //Settle time after disruption
    GpsStable = confidence == CONFIDENCE_OK;
    static bool lastStable = false;
    if ( GpsStable && !lastStable) LogTimeF("GPS %4d ms stable - sync enabled\r\n", ms);
    lastStable = GpsStable;
}
void GpsInit()
{
    msTimer = TimerNowCount();
    NmeaInit();
    
    LPC_GPIO0->FIODIR      |= GPS_ENABLE_MASK_0; //Set the enable pin direction to 1 == output
    LPC_GPIO0->FIOCLR       = GPS_ENABLE_MASK_0; //Set the enable to low to reset the GPS
    while (TimerSinceMs(msTimer) < 10) __nop();
    LPC_GPIO0->FIOSET       = GPS_ENABLE_MASK_0; //Set the enable to high
    
    LPC_GPIOINT->IO0IntEnR |= GPS_PPS_MASK_0;    //Set the PPS pin to be interrupt on rise
    NVIC->ISER[0]          |= 1 << 21;           //6.5.1 bit1 == Interrupt set enable for EINT3. It MUST be enabled even for GPIO interrupts - I checked.
}