kopia lustrzana https://github.com/RPiks/pico-hf-oscillator
Ongoing developments. Time diff is calculated quite reliable. Debugging!
rodzic
65aaacca12
commit
f0e79b50bc
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue