Ongoing developments. Time diff is calculated quite reliable. Debugging!

gpstest
roman 2023-11-25 03:28:08 +03:00
rodzic 65aaacca12
commit f0e79b50bc
7 zmienionych plików z 179 dodań i 55 usunięć

Wyświetl plik

@ -33,7 +33,7 @@ pico_generate_pio_header(pico-hf-oscillator-test ${CMAKE_CURRENT_LIST_DIR}/piodc
target_sources(pico-hf-oscillator-test PUBLIC
${CMAKE_CURRENT_LIST_DIR}/lib/assert.c
# ${CMAKE_CURRENT_LIST_DIR}/lib/utility.c
${CMAKE_CURRENT_LIST_DIR}/lib/thirdparty/strnstr.c
${CMAKE_CURRENT_LIST_DIR}/piodco/piodco.c
${CMAKE_CURRENT_LIST_DIR}/gpstime/GPStime.c
${CMAKE_CURRENT_LIST_DIR}/test.c

Wyświetl plik

@ -67,12 +67,15 @@
#define RAM_A __not_in_flash("A") /* Place time-critical var in RAM */
/* A macro for arithmetic right shifts, with casting of the argument. */
#define iSAR(arg, rcount) (((int32_t)(arg)) >> (rcount))
#define iSAR32(arg, rcount) (((int32_t)(arg)) >> (rcount))
#define iSAR64(arg, rcount) (((int64_t)(arg)) >> (rcount))
/* A macro of multiplication guarantees of doing so using 1 ASM command. */
#define iMUL32ASM(a,b) __mul_instruction((int32_t)(a), (int32_t)(b))
#define iMUL32ASM(a, b) __mul_instruction((int32_t)(a), (int32_t)(b))
/* Performing the square by ASM. */
#define iSquare32ASM(x) (iMUL32ASM((x), (x)))
#define iSquare32ASM(x) (iMUL32ASM((x), (x)))
#define ABS(x) ((x) > 0 ? (x) : -(x))
#endif

Wyświetl plik

@ -11,19 +11,15 @@
//
// DESCRIPTION
//
// GPS time utilities for PioDco oscillator provides a precise reference
// frequency in order to obtain an absolute accuracy of PioDco. The value of
// this accuracy depends on quality of navigation solution of GPS receiver.
// This quality can be estimated by GDOP (geometric dilution of precision) and
// TDOP (time dilution of precision) received in NMEA-0183 message packet from
// GPS receiver.
// The Pico's frequency error due to drift of its CLK oscillator is
// calculated by that utilities and is used to shift PioDco frequency to
// compensate the drift. So, the absolute frequency value which we possess
// on the GPIO pin is supposed to be much more accurate when using GPS time
// utilities.
// GPS time utilities for PioDco oscillator calculates a precise frequency
// between the local Pico oscillator and reference oscillator of GPS system.
// The value of the shift is used to correct PioDco generated frequency. The
// practical precision of this solution within tenths millihertz range.
// The value of this accuracy depends on quality of navigation solution of GPS
// receiver. This quality can be estimated by GDOP and TDOP parameters received
// in NMEA-0183 message packet from GPS receiver.
// Owing to the meager PioDco frequency step in millihertz range, we obtain
// a quasi-analog precision frequency source.
// a quasi-analog precision frequency source (if the GPS navigation works ok).
// This is an experimental project of amateur radio class and it is devised
// by me on the free will base in order to experiment with QRP narrowband
// digital modes including extremely ones such as QRSS.
@ -64,6 +60,7 @@
///////////////////////////////////////////////////////////////////////////////
#include "GPStime.h"
static GPStimeContext *spGPStimeContext = NULL;
static GPStimeData *spGPStimeData = NULL;
GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio)
@ -84,8 +81,9 @@ GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio)
ASSERT_(pgt);
pgt->_uart_id = uart_id;
pgt->_uart_baudrate = uart_baud;
pgt->_pps_pio = pps_gpio;
pgt->_pps_gpio = pps_gpio;
spGPStimeContext = pgt;
spGPStimeData = &pgt->_time_data;
gpio_init(pps_gpio);
@ -105,33 +103,120 @@ void GPStimeDestroy(GPStimeContext **pp)
void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events)
{
const uint64_t tm64 = GetUptime64();
if(spGPStimeData)
{
const uint64_t tm64 = GetUptime64();
spGPStimeData->_u64_sysclk_pps_last = tm64;
const int32_t ksliding_len = sizeof(spGPStimeData->_pu64_sliding_pps_tm)
/ sizeof(spGPStimeData->_pu64_sliding_pps_tm[0]);
spGPStimeData->_u64_sysclk_pps_last = tm64;
++spGPStimeData->_ix_last;
spGPStimeData->_ix_last %= ksliding_len;
const uint8_t ix_oldest = spGPStimeData->_ix_last;
spGPStimeData->_ix_last %= eSlidingLen;
int64_t dt_1M = 1000000LL * (tm64 - spGPStimeData->_pu64_sliding_pps_tm[ix_oldest]);
const int64_t dt_per_window = tm64 - spGPStimeData->_pu64_sliding_pps_tm[spGPStimeData->_ix_last];
spGPStimeData->_pu64_sliding_pps_tm[spGPStimeData->_ix_last] = tm64;
if(spGPStimeData->_u64_pps_period_1M)
if(ABS(dt_per_window - eCLKperTimeMark * eSlidingLen) < eMaxCLKdevPPM * eSlidingLen)
{
spGPStimeData->_u64_pps_period_1M += iSAR(dt_1M - spGPStimeData->_u64_pps_period_1M + 4, 3);
if(spGPStimeData->_u64_pps_period_1M)
{
spGPStimeData->_u64_pps_period_1M += iSAR64((int64_t)eDtUpscale * dt_per_window
- spGPStimeData->_u64_pps_period_1M + 2, 2);
spGPStimeData->_i32_freq_shift_ppb = (spGPStimeData->_u64_pps_period_1M
- (int64_t)eDtUpscale * eCLKperTimeMark * eSlidingLen
+ (eSlidingLen >> 1)) / eSlidingLen;
}
else
{
spGPStimeData->_u64_pps_period_1M = (int64_t)eDtUpscale * dt_per_window;
}
}
else
{
spGPStimeData->_u64_pps_period_1M = dt_1M;
}
#if 1
dt_1M = (dt_1M + ksliding_len/2) / ksliding_len;
const uint64_t tmp = (spGPStimeData->_u64_pps_period_1M + ksliding_len/2) / ksliding_len;
printf("%llu %lld %llu\n", spGPStimeData->_u64_sysclk_pps_last, dt_1M, tmp);
#ifdef DEBUGLOG
const int64_t dt_1M = (dt_per_window + (eSlidingLen >> 1)) / eSlidingLen;
const uint64_t tmp = (spGPStimeData->_u64_pps_period_1M + (eSlidingLen >> 1)) / eSlidingLen;
printf("%llu %lld %llu %lld\n", spGPStimeData->_u64_sysclk_pps_last, dt_1M, tmp,
spGPStimeData->_i32_freq_shift_ppb);
#endif
}
}
/// @brief Calculates current unixtime using information available.
/// @param pg Ptr to the context.
/// @param u32_tmdst Ptr to destination unixtime val.
/// @return 0 if OK.
/// @return -1 There was NO historical GPS fixes.
/// @return -2 The fix was expired (24hrs or more time ago).
int GPStimeGetTime(const GPStimeContext *pg, uint32_t *u32_tmdst)
{
assert_(pg);
assert(u32_tmdst);
/* If there has been no fix, it's no way to get any time data... */
if(!pg->_time_data._u32_utime_nmea_last)
{
return -1;
}
const uint64_t tm64 = GetUptime64();
const uint64_t dt = tm64 - pg->_time_data._u64_sysclk_nmea_last;
const uint32_t dt_sec = PicoU64timeToSeconds(dt);
/* If expired. */
if(dt_sec > 86400)
{
return -2;
}
*u32_tmdst = pg->_time_data._u32_utime_nmea_last + dt_sec;
return 0;
}
void __not_in_flash_func (GPStimeUartRxIsr)()
{
if(spGPStimeContext)
{
uart_inst_t *puart_id = spGPStimeContext->_uart_id ? uart1 : uart0;
for(;;uart_is_readable(puart_id))
{
uint8_t chr = uart_getc(puart_id);
spGPStimeContext->_pbytebuff[spGPStimeContext->_u8_ixw++] = chr;
spGPStimeContext->_is_sentence_ready = ('\n' == chr);
break;
}
if(spGPStimeContext->_is_sentence_ready)
{
spGPStimeContext->_i32_error_count -= GPStimeProcNMEAsentence(spGPStimeContext);
}
}
}
int GPStimeProcNMEAsentence(GPStimeContext *pg)
{
assert_(pg);
uint8_t *prmc = strnstr(pg->_pbytebuff, "$GPRMC", sizeof(pg->_pbytebuff));
if(prmc)
{
uint8_t u8ixcollector[16], chksum = '$'^'G'^'P'^'R'^'M'^'C'^',';
for(uint8_t u8ix = prmc - pg->_pbytebuff + 7, i = 0;
u8ix != prmc - pg->_pbytebuff; ++u8ix)
{
chksum ^= pg->_pbytebuff[u8ix];
if(',' == pg->_pbytebuff[u8ix])
{
pg->_pbytebuff[u8ix] = 0;
u8ixcollector[i++] = u8ix;
if(12 == i)
{
break;
}
}
}
}
/*
"$GPRMC,105954.000,A,3150.6731,N,11711.9399,E,0.00,96.10,250313,,,A*53";
*/
return 0;
}

Wyświetl plik

@ -27,10 +27,11 @@
// This is an experimental project of amateur radio class and it is devised
// by me on the free will base in order to experiment with QRP narrowband
// digital modes including extremely ones such as QRSS.
// I gracefully appreciate any thoughts or comments on that matter.
// I gracefully appreciate any thoughts or comments on this matter.
//
// PLATFORM
// Raspberry Pi pico.
// A GPS receiver module which supports PPS (pulse per second) output.
//
// REVISION HISTORY
//
@ -68,28 +69,38 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "../defines.h"
#include "../lib/assert.h"
#include "../lib/utility.h"
#include "../lib/thirdparty/strnstr.h"
#define ASSERT_(x) assert_(x)
enum
{
eDtUpscale = 1000000,
eSlidingLen = 32,
eCLKperTimeMark = 1000000,
eMaxCLKdevPPM = 250
};
typedef struct
{
uint8_t _u8_is_sol_active; // A navigation solution is valid.
uint32_t _u32_utime_nmea_last; // The last unix time received from GPS.
uint64_t _u64_sysclk_nmea_last; // The sysclk of the last unix time received.
int64_t _i64_lat_100k, _i64_lon_100k; // The lat, lon, degrees, multiplied by 1e5.
uint8_t _u8_is_solution_active; /* A navigation solution is valid. */
uint32_t _u32_utime_nmea_last; /* The last unix time received from GPS. */
uint64_t _u64_sysclk_nmea_last; /* The sysclk of the last unix time received. */
int64_t _i64_lat_100k, _i64_lon_100k; /* The lat, lon, degrees, multiplied by 1e5. */
uint64_t _u64_sysclk_pps_last; // The sysclk of the last rising edge of PPS.
uint64_t _u64_pps_period_1M; // The PPS avg. period *1e6, filtered.
uint64_t _u64_sysclk_pps_last; /* The sysclk of the last rising edge of PPS. */
uint64_t _u64_pps_period_1M; /* The PPS avg. period *1e6, filtered. */
uint64_t _pu64_sliding_pps_tm[32]; // A sliding window to store PPS periods.
uint8_t _ix_last; // An index of last write to sliding window.
uint64_t _pu64_sliding_pps_tm[eSlidingLen]; /* A sliding window to store PPS periods. */
uint8_t _ix_last; /* An index of last write to sliding window. */
int32_t _i32_LO_freq_corr_milhz; // SYSCLK frequency correction, milliHertz.
int64_t _i32_freq_shift_ppb; /* Calcd frequency shift, parts per billion. */
} GPStimeData;
@ -97,22 +108,26 @@ typedef struct
{
int _uart_id;
int _uart_baudrate;
int _pps_pio;
int _pps_gpio;
GPStimeData _time_data;
uint8_t _pbytebuff[256];
uint8_t _u8_ixw;
uint8_t _is_sentence_ready;
int32_t _i32_error_count;
} GPStimeContext;
GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio);
void GPStimeDestroy(GPStimeContext **pp);
void GPStimeISRTick(GPStimeContext *pg);
//void GPStimeProcessingTick(GPStimeContext *pg);
int GPStimeProcNMEAsentence(GPStimeContext *pg);
void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events);
void __not_in_flash_func (GPStimeUartRxIsr)();
int GPStimeGetTime(const GPStimeContext *pg, uint32_t *u32_tmdst);
#endif

Wyświetl plik

@ -17,4 +17,9 @@ inline uint32_t GetTime32(void)
return timer_hw->timelr;
}
inline uint32_t PicoU64timeToSeconds(uint64_t u64tm)
{
return u64tm / 1000000U; // No rounding deliberately!
}
#endif

Wyświetl plik

@ -95,7 +95,6 @@ int PioDCOInit(PioDco *pdco, int gpio, int cpuclkhz)
sm_config_set_set_pins(&pdco->_pio_sm, pdco->_gpio, 1);
pio_gpio_init(pdco->_pio, pdco->_gpio);
pio_sm_init(pdco->_pio, pdco->_ism, pdco->_offset, &pdco->_pio_sm);
//pio_sm_set_enabled(pdco->_pio, pdco->_ism, true);
return 0;
}
@ -117,9 +116,6 @@ int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, uint32_t ui32_frq_millihz)
pdco->_frq_cycles_per_pi = (int32_t)(((int64_t)pdco->_clkfreq_hz * (int64_t)(1<<24) * 1000LL
+(i64denominator>>1)) / i64denominator);
//pdco->_frq_cycles_per_pi = (int32_t)(((int64_t)pdco->_clkfreq_hz * (int64_t)(1<<24)
//+ (ui32_frq_hz>>1)) / (int64_t)ui32_frq_hz);
si32precise_cycles = pdco->_frq_cycles_per_pi;
return 0;
@ -151,12 +147,10 @@ void RAM (PioDCOWorker)(PioDco *pDCO)
register PIO pio = pDCO->_pio;
register uint sm = pDCO->_ism;
register int32_t i32acc_error = 0;
register uint32_t *preg32 = pDCO->_ui32_pioreg;
register uint8_t *pu8reg = (uint8_t *)preg32;
//si32precise_cycles = pDCO->_frq_cycles_per_pi;
for(;;)
{
const register int32_t i32reg = si32precise_cycles;
@ -167,7 +161,7 @@ void RAM (PioDCOWorker)(PioDco *pDCO)
{
/* RPix: Calculate the integer number of CPU CLK cycles per next
DCO cycle, corrected by accumulated error (feedback of the PLL). */
const int32_t i32wc = iSAR(i32reg - i32acc_error + (1<<23), 24);
const int32_t i32wc = iSAR32(i32reg - i32acc_error + (1<<23), 24);
/* RPix: Calculate the difference btw calculated value scaled to
`fine` state and precise value of DCO cycles per CPU CLK cycle.
@ -183,3 +177,13 @@ void RAM (PioDCOWorker)(PioDco *pDCO)
dco_program_puts(pio, sm, preg32);
}
}
/// @brief Sets DCO running mode.
/// @param pdco Ptr to DCO context.
/// @param emode Desired mode.
void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode)
{
assert_(pdco);
pdco->_mode = emode;
eDCOMODE_IDLE == emode ? PioDCOStop(pdco) : PioDCOStart(pdco);
}

Wyświetl plik

@ -72,8 +72,18 @@
#include "defines.h"
enum PioDcoMode
{
eDCOMODE_IDLE = 0,
eDCOMODE_FREERUN = 1,
eDCOMODE_GPS_REFERENCE = 2,
eDCOMODE_THERMO_COMPENSATION = 3
};
typedef struct
{
enum PioDcoMode _mode; /* Running mode. */
PIO _pio; /* Worker PIO on this DCO. */
int _gpio; /* Pico' GPIO for DCO output. */
@ -95,6 +105,8 @@ int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, uint32_t ui32_frq_millihz)
void PioDCOStart(PioDco *pdco);
void PioDCOStop(PioDco *pdco);
void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode);
void RAM (PioDCOWorker)(PioDco *pDCO);
#endif