diff --git a/CMakeLists.txt b/CMakeLists.txt index d37c015..2c66b85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ 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/thirdparty/strnstr.c +# ${CMAKE_CURRENT_LIST_DIR}/lib/thirdparty/strptime.c +# ${CMAKE_CURRENT_LIST_DIR}/lib/thirdparty/timegm.c ${CMAKE_CURRENT_LIST_DIR}/piodco/piodco.c ${CMAKE_CURRENT_LIST_DIR}/gpstime/GPStime.c ${CMAKE_CURRENT_LIST_DIR}/test.c diff --git a/defines.h b/defines.h index 0aac468..7a9dada 100644 --- a/defines.h +++ b/defines.h @@ -78,4 +78,8 @@ #define ABS(x) ((x) > 0 ? (x) : -(x)) +#define INVERSE(x) ((x) = -(x)) + +#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) + #endif diff --git a/gpstime/GPStime.c b/gpstime/GPStime.c index 2e59962..6ef19e3 100644 --- a/gpstime/GPStime.c +++ b/gpstime/GPStime.c @@ -6,13 +6,13 @@ /////////////////////////////////////////////////////////////////////////////// // // -// gpstime.h - GPS time reference utilities for digital controlled radio freq +// gpstime.c - GPS time reference utilities for digital controlled radio freq // oscillator based on Raspberry Pi Pico. // // DESCRIPTION // // GPS time utilities for PioDco oscillator calculates a precise frequency -// between the local Pico oscillator and reference oscillator of GPS system. +// shift between the local Pico oscillator and reference oscill. 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 @@ -89,6 +89,12 @@ GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio) gpio_init(pps_gpio); gpio_set_dir(pps_gpio, GPIO_IN); gpio_set_irq_enabled_with_callback(pps_gpio, GPIO_IRQ_EDGE_RISE, true, &GPStimePPScallback); + + irq_set_exclusive_handler(UART0_IRQ, GPStimeUartRxIsr); + irq_set_enabled(UART0_IRQ, true); + uart_set_irq_enables(uart0, true, false); + + return pgt; } void GPStimeDestroy(GPStimeContext **pp) @@ -129,7 +135,7 @@ void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events) } } -#ifdef DEBUGLOG +#ifdef NOP 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, @@ -186,7 +192,9 @@ void __not_in_flash_func (GPStimeUartRxIsr)() if(spGPStimeContext->_is_sentence_ready) { + spGPStimeContext->_u8_ixw = 0; spGPStimeContext->_i32_error_count -= GPStimeProcNMEAsentence(spGPStimeContext); + //printf("err: %ld\n", spGPStimeContext->_i32_error_count); } } } @@ -195,28 +203,105 @@ int GPStimeProcNMEAsentence(GPStimeContext *pg) { assert_(pg); - uint8_t *prmc = strnstr(pg->_pbytebuff, "$GPRMC", sizeof(pg->_pbytebuff)); + uint8_t *prmc = (uint8_t *)strnstr((char *)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) + uint64_t tm_fix = GetUptime64(); + + uint8_t u8ixcollector[16] = {0}; + uint8_t chksum = 0; + for(uint8_t u8ix = 0, i = 0; u8ix != sizeof(pg->_pbytebuff); ++u8ix) { - chksum ^= pg->_pbytebuff[u8ix]; - if(',' == pg->_pbytebuff[u8ix]) + uint8_t *p = pg->_pbytebuff + u8ix; + chksum ^= *p; + if(',' == *p) { - pg->_pbytebuff[u8ix] = 0; - u8ixcollector[i++] = u8ix; - if(12 == i) + *p = 0; + u8ixcollector[i++] = u8ix + 1; + if('*' == *p || 12 == i) { break; } } } + + pg->_time_data._u8_is_solution_active = 'A' == prmc[u8ixcollector[1]]; + + if(pg->_time_data._u8_is_solution_active) + { + pg->_time_data._i64_lat_100k = (int64_t)(.5f + 1e5 * atof((const char *)prmc + u8ixcollector[2])); + if('N' == prmc[u8ixcollector[3]]) { } + else if('S' == prmc[u8ixcollector[3]]) + { + INVERSE(pg->_time_data._i64_lat_100k); + } + else + { + return -2; + } + + pg->_time_data._i64_lon_100k = (int64_t)(.5f + 1e5 * atof((const char *)prmc + u8ixcollector[4])); + if('E' == prmc[u8ixcollector[5]]) { } + else if('W' == prmc[u8ixcollector[5]]) + { + INVERSE(pg->_time_data._i64_lon_100k); + } + else + { + return -3; + } + + if('*' != prmc[u8ixcollector[11] + 1]) + { + return -4; + } + + pg->_time_data._u32_utime_nmea_last = GPStime2UNIX(prmc + u8ixcollector[8], prmc + u8ixcollector[0]); + pg->_time_data._u64_sysclk_nmea_last = tm_fix; + } } + + return 0; /* "$GPRMC,105954.000,A,3150.6731,N,11711.9399,E,0.00,96.10,250313,,,A*53"; */ +} + +uint32_t GPStime2UNIX(const char *pdate, const char *ptime) +{ + assert_(pdate); + assert_(ptime); + + if(strlen(pdate) == 6 && strlen(ptime) > 5) + { + struct tm ltm = {0}; + + ltm.tm_year = 100 + DecimalStr2ToNumber(pdate + 4); + ltm.tm_mon = DecimalStr2ToNumber(pdate + 2) - 1; + ltm.tm_mday = DecimalStr2ToNumber(pdate); + + ltm.tm_hour = DecimalStr2ToNumber(ptime); + ltm.tm_min = DecimalStr2ToNumber(ptime + 2); + ltm.tm_sec = DecimalStr2ToNumber(ptime + 4); + + return mktime(<m); + } + return 0; } + +void GPStimeDump(const GPStimeData *pd) +{ + assert_(pd); + + static int tick = 0; + + printf("%u\nActive:%u\n", tick++, pd->_u8_is_solution_active); + printf("NMEA utime last:%lu\n", pd->_u32_utime_nmea_last); + printf("SYSCKL NMEA last:%llu\n", pd->_u64_sysclk_nmea_last); + printf("Lat:%lld Lon:%lld\n", pd->_i64_lat_100k, pd->_i64_lon_100k); + printf("SYSCKL PPS last:%llu\n", pd->_u64_sysclk_pps_last); + printf("PPS period e6:%llu\n", (pd->_u64_pps_period_1M + (eSlidingLen>>1)) / eSlidingLen); + printf("FRQ corr ppb:%lld\n\n", pd->_i32_freq_shift_ppb); +} diff --git a/gpstime/GPStime.h b/gpstime/GPStime.h index 62b4d0e..3c24c2d 100644 --- a/gpstime/GPStime.h +++ b/gpstime/GPStime.h @@ -11,27 +11,22 @@ // // 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 +// shift between the local Pico oscillator and reference oscill. 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. -// I gracefully appreciate any thoughts or comments on this matter. +// I gracefully appreciate any thoughts or comments on that matter. // // PLATFORM // Raspberry Pi pico. -// A GPS receiver module which supports PPS (pulse per second) output. // // REVISION HISTORY // @@ -70,6 +65,7 @@ #include #include #include +#include #include "pico/stdlib.h" #include "hardware/uart.h" #include "../defines.h" @@ -122,12 +118,14 @@ typedef struct GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio); void GPStimeDestroy(GPStimeContext **pp); -//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); +uint32_t GPStime2UNIX(const char *pdate, const char *ptime); + +void GPStimeDump(const GPStimeData *pd); #endif diff --git a/lib/thirdparty/strnstr.c b/lib/thirdparty/strnstr.c new file mode 100644 index 0000000..ca12ba1 --- /dev/null +++ b/lib/thirdparty/strnstr.c @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2001 Mike Barcroft + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strstr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include +__FBSDID("$FreeBSD: src/lib/libc/string/strnstr.c,v 1.5 2009/02/03 17:58:20 danger Exp $"); + +#include + +/* + * Find the first occurrence of find in s, where the search is limited to the + * first slen characters of s. + */ +char * +strnstr(const char *s, const char *find, size_t slen) +{ + char c, sc; + size_t len; + + if ((c = *find++) != '\0') { + len = strlen(find); + do { + do { + if (slen-- < 1 || (sc = *s++) == '\0') + return (NULL); + } while (sc != c); + if (len > slen) + return (NULL); + } while (strncmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} diff --git a/lib/thirdparty/strnstr.h b/lib/thirdparty/strnstr.h new file mode 100644 index 0000000..059e691 --- /dev/null +++ b/lib/thirdparty/strnstr.h @@ -0,0 +1,7 @@ +#ifndef STRNSTR_H_ +#define STRNSTR_H_ + +char * +strnstr(const char *s, const char *find, size_t slen); + +#endif diff --git a/lib/utility.h b/lib/utility.h index af7f395..df46099 100644 --- a/lib/utility.h +++ b/lib/utility.h @@ -22,4 +22,9 @@ inline uint32_t PicoU64timeToSeconds(uint64_t u64tm) return u64tm / 1000000U; // No rounding deliberately! } +inline uint32_t DecimalStr2ToNumber(const char *p) +{ + return 10U * (p[0] - '0') + (p[1] - '0'); +} + #endif diff --git a/test.c b/test.c index 9ff7152..40c9533 100644 --- a/test.c +++ b/test.c @@ -216,13 +216,15 @@ int main() set_sys_clock_khz(clkhz / 1000L, true); stdio_init_all(); - - //uart_init(uart0, 9600); - //gpio_set_function(0, GPIO_FUNC_UART); - //gpio_set_function(1, GPIO_FUNC_UART); + sleep_ms(1000); + printf("Start\n"); GPStimeContext *pGPS = GPStimeInit(0, 9600, 2); - for(;;) {} + for(;;) + { + GPStimeDump(&(pGPS->_time_data)); + sleep_ms(1000); + } for(;;) {