Merge branch 'gpstest'

wip4speed
roman 2023-11-26 23:40:03 +03:00
commit 3886eb4408
17 zmienionych plików z 814 dodań i 36 usunięć

Wyświetl plik

@ -9,8 +9,6 @@ set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
# set(PICO_SDK_PATH "/home/jabba/pico-sdk")
set(PICO_BOARD pico CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
@ -25,15 +23,16 @@ project(pico-hf-oscillator-test C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(pico-hf-oscillator-test)
pico_generate_pio_header(pico-hf-oscillator-test ${CMAKE_CURRENT_LIST_DIR}/piodco/dco.pio)
target_sources(pico-hf-oscillator-test PUBLIC
${CMAKE_CURRENT_LIST_DIR}/lib/assert.c
${CMAKE_CURRENT_LIST_DIR}/lib/assert.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}/debug/logutils.c
${CMAKE_CURRENT_LIST_DIR}/test.c
)
@ -41,14 +40,17 @@ pico_set_program_name(pico-hf-oscillator-test "pico-hf-oscillator-test")
pico_set_program_version(pico-hf-oscillator-test "0.9")
pico_enable_stdio_uart(pico-hf-oscillator-test 1)
pico_enable_stdio_usb(pico-hf-oscillator-test 0)
pico_enable_stdio_usb(pico-hf-oscillator-test 1)
# Add the standard include files to the build
target_include_directories(pico-hf-oscillator-test PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
${CMAKE_CURRENT_LIST_DIR}/gpstime
${CMAKE_CURRENT_LIST_DIR}/..
)
add_compile_options(-Wall)
# Add any user requested libraries
target_link_libraries(
pico-hf-oscillator-test
@ -58,7 +60,7 @@ target_link_libraries(
hardware_timer
hardware_clocks
hardware_pio
hardware_vreg
#hardware_vreg
)
pico_add_extra_outputs(pico-hf-oscillator-test)

87
debug/logutils.c 100644
Wyświetl plik

@ -0,0 +1,87 @@
///////////////////////////////////////////////////////////////////////////////
//
// Roman Piksaykin [piksaykin@gmail.com], R2BDY
// https://www.qrz.com/db/r2bdy
//
///////////////////////////////////////////////////////////////////////////////
//
//
// logutils.h - A set of utilities for logging/debugging.
//
// DESCRIPTION
// -
//
// HOWTOSTART
// -
//
// PLATFORM
// Raspberry Pi pico.
//
// REVISION HISTORY
// -
//
// PROJECT PAGE
// https://github.com/RPiks/pico-WSPR-tx
//
// LICENCE
// MIT License (http://www.opensource.org/licenses/mit-license.php)
//
// Copyright (c) 2023 by Roman Piksaykin
//
// Permission is hereby granted, free of charge,to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction,including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "hardware/clocks.h"
#include "pico/stdlib.h"
void StampPrintf(const char* pformat, ...)
{
static uint32_t sTick = 0;
if(!sTick)
{
stdio_init_all();
}
uint64_t tm_us = to_us_since_boot(get_absolute_time());
const uint32_t tm_day = (uint32_t)(tm_us / 86400000000ULL);
tm_us -= (uint64_t)tm_day * 86400000000ULL;
const uint32_t tm_hour = (uint32_t)(tm_us / 3600000000ULL);
tm_us -= (uint64_t)tm_hour * 3600000000ULL;
const uint32_t tm_min = (uint32_t)(tm_us / 60000000ULL);
tm_us -= (uint64_t)tm_min * 60000000ULL;
const uint32_t tm_sec = (uint32_t)(tm_us / 1000000ULL);
tm_us -= (uint64_t)tm_sec * 1000000ULL;
printf("%02lud%02lu:%02lu:%02lu.%06llu [%04lu] ", tm_day, tm_hour, tm_min, tm_sec, tm_us, sTick++);
va_list argptr;
va_start(argptr, pformat);
vprintf(pformat, argptr);
va_end(argptr);
printf("\n");
}

6
debug/logutils.h 100644
Wyświetl plik

@ -0,0 +1,6 @@
#ifndef LOGUTILS_H_
#define LOGUTILS_H_
void StampPrintf(const char* pformat, ...);
#endif

Wyświetl plik

@ -67,12 +67,19 @@
#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))
#define INVERSE(x) ((x) = -(x))
#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
#endif

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 344 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 219 KiB

327
gpstime/GPStime.c 100644
Wyświetl plik

@ -0,0 +1,327 @@
///////////////////////////////////////////////////////////////////////////////
//
// Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD
// https://www.qrz.com/db/r2bdy
//
///////////////////////////////////////////////////////////////////////////////
//
//
// 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
// 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 (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 that matter.
//
// PLATFORM
// Raspberry Pi pico.
//
// REVISION HISTORY
//
// Rev 0.1 25 Nov 2023 Initial release
//
// PROJECT PAGE
// https://github.com/RPiks/pico-hf-oscillator
//
// LICENCE
// MIT License (http://www.opensource.org/licenses/mit-license.php)
//
// Copyright (c) 2023 by Roman Piksaykin
//
// Permission is hereby granted, free of charge,to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction,including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
#include "GPStime.h"
static GPStimeContext *spGPStimeContext = NULL;
static GPStimeData *spGPStimeData = NULL;
/// @brief Initializes GPS time module Context.
/// @param uart_id UART id to which GPS receiver is connected, 0 OR 1.
/// @param uart_baud UART baudrate, 115200 max.
/// @param pps_gpio GPIO pin of PPS (second pulse) from GPS receiver.
/// @return the new GPS time Context.
GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int pps_gpio)
{
ASSERT_(0 == uart_id || 1 == uart_id);
ASSERT_(uart_baud <= 115200);
ASSERT_(pps_gpio < 29);
// Set up our UART with the required speed & assign pins.
uart_init(uart_id ? uart1 : uart0, uart_baud);
gpio_set_function(uart_id ? 8 : 12, GPIO_FUNC_UART);
gpio_set_function(uart_id ? 9 : 13, GPIO_FUNC_UART);
GPStimeContext *pgt = calloc(1, sizeof(GPStimeContext));
ASSERT_(pgt);
pgt->_uart_id = uart_id;
pgt->_uart_baudrate = uart_baud;
pgt->_pps_gpio = pps_gpio;
spGPStimeContext = pgt;
spGPStimeData = &pgt->_time_data;
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);
uart_set_hw_flow(uart_id ? uart1 : uart0, false, false);
uart_set_format(uart_id ? uart1 : uart0, 8, 1, UART_PARITY_NONE);
uart_set_fifo_enabled(uart_id ? uart1 : uart0, false);
irq_set_exclusive_handler(uart_id ? UART1_IRQ : UART0_IRQ, GPStimeUartRxIsr);
irq_set_enabled(uart_id ? UART1_IRQ : UART0_IRQ, true);
uart_set_irq_enables(uart_id ? uart1 : uart0, true, false);
return pgt;
}
/// @brief Deinits the GPS module and destroys allocated resources.
/// @param pp Ptr to Ptr of the Context.
/// @attention *NOT* implemented completely so far. !FIXME!
void GPStimeDestroy(GPStimeContext **pp)
{
ASSERT_(pp);
ASSERT_(*pp);
uart_deinit((*pp)->_uart_id ? uart1 : uart0);
free(*pp);
*pp = NULL;
}
/// @brief The PPS interrupt service subroutine.
/// @param gpio The GPIO pin of Pico which is connected to PPS output of GPS rec.
void RAM (GPStimePPScallback)(uint gpio, uint32_t events)
{
const uint64_t tm64 = GetUptime64();
if(spGPStimeData)
{
spGPStimeData->_u64_sysclk_pps_last = tm64;
++spGPStimeData->_ix_last;
spGPStimeData->_ix_last %= eSlidingLen;
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(ABS(dt_per_window - eCLKperTimeMark * eSlidingLen) < eMaxCLKdevPPM * eSlidingLen)
{
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;
}
}
#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,
spGPStimeData->_i32_freq_shift_ppb);
#endif
}
}
/// @brief Calculates current unixtime using data 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;
}
/// @brief UART FIFO ISR. Processes another N chars receiver from GPS rec.
void RAM (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->_u8_ixw = 0;
spGPStimeContext->_i32_error_count -= GPStimeProcNMEAsentence(spGPStimeContext);
}
}
}
/// @brief Processes a NMEA sentence GPRMC.
/// @param pg Ptr to Context.
/// @return 0 OK.
/// @return -2 Error: bad lat format.
/// @return -3 Error: bad lon format.
/// @return -4 Error: no final '*' char ere checksum value.
/// @attention Checksum validation is not implemented so far. !FIXME!
int GPStimeProcNMEAsentence(GPStimeContext *pg)
{
assert_(pg);
uint8_t *prmc = (uint8_t *)strnstr((char *)pg->_pbytebuff, "$GPRMC,", sizeof(pg->_pbytebuff));
if(prmc)
{
++pg->_time_data._u32_nmea_gprmc_count;
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)
{
uint8_t *p = pg->_pbytebuff + u8ix;
chksum ^= *p;
if(',' == *p)
{
*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;
}
/// @brief Converts GPS time and date strings to unix time.
/// @param pdate Date string, 6 chars in work.
/// @param ptime Time string, 6 chars in work.
/// @return Unix timestamp (epoch). 0 if bad imput format.
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(&ltm);
}
return 0;
}
/// @brief Dumps the GPS data struct to stdio.
/// @param pd Ptr to Context.
void GPStimeDump(const GPStimeData *pd)
{
assert_(pd);
printf("\nGPS solution is active:%u\n", pd->_u8_is_solution_active);
printf("GPRMC count:%lu\n", pd->_u32_nmea_gprmc_count);
printf("NMEA unixtime last:%lu\n", pd->_u32_utime_nmea_last);
printf("NMEA sysclock last:%llu\n", pd->_u64_sysclk_nmea_last);
printf("GPS Latitude:%lld Longtitude:%lld\n", pd->_i64_lat_100k, pd->_i64_lon_100k);
printf("PPS sysclock last:%llu\n", pd->_u64_sysclk_pps_last);
printf("PPS period *1e6:%llu\n", (pd->_u64_pps_period_1M + (eSlidingLen>>1)) / eSlidingLen);
printf("FRQ correction ppb:%lld\n\n", pd->_i32_freq_shift_ppb);
}

132
gpstime/GPStime.h 100644
Wyświetl plik

@ -0,0 +1,132 @@
///////////////////////////////////////////////////////////////////////////////
//
// Roman Piksaykin [piksaykin@gmail.com], R2BDY, PhD
// https://www.qrz.com/db/r2bdy
//
///////////////////////////////////////////////////////////////////////////////
//
//
// gpstime.h - 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
// 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 (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 that matter.
//
// PLATFORM
// Raspberry Pi pico.
//
// REVISION HISTORY
//
// Rev 0.1 25 Nov 2023 Initial release
//
// PROJECT PAGE
// https://github.com/RPiks/pico-hf-oscillator
//
// LICENCE
// MIT License (http://www.opensource.org/licenses/mit-license.php)
//
// Copyright (c) 2023 by Roman Piksaykin
//
// Permission is hereby granted, free of charge,to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction,including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY,WHETHER IN AN ACTION OF CONTRACT,TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////
#ifndef GPSTIME_H_
#define GPSTIME_H_
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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_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. */
uint32_t _u32_nmea_gprmc_count; /* The count of $GPRMC sentences received */
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[eSlidingLen]; /* A sliding window to store PPS periods. */
uint8_t _ix_last; /* An index of last write to sliding window. */
int64_t _i32_freq_shift_ppb; /* Calcd frequency shift, parts per billion. */
} GPStimeData;
typedef struct
{
int _uart_id;
int _uart_baudrate;
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);
int GPStimeProcNMEAsentence(GPStimeContext *pg);
void RAM (GPStimePPScallback)(uint gpio, uint32_t events);
void RAM (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

65
lib/thirdparty/strnstr.c vendored 100644
Wyświetl plik

@ -0,0 +1,65 @@
/*-
* Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/string/strnstr.c,v 1.5 2009/02/03 17:58:20 danger Exp $");
#include <string.h>
/*
* 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);
}

7
lib/thirdparty/strnstr.h vendored 100644
Wyświetl plik

@ -0,0 +1,7 @@
#ifndef STRNSTR_H_
#define STRNSTR_H_
char *
strnstr(const char *s, const char *find, size_t slen);
#endif

37
lib/utility.h 100644
Wyświetl plik

@ -0,0 +1,37 @@
#ifndef UTILITY_H_
#define UTILITY_H_
#include <stdint.h>
#include "pico/stdlib.h"
inline uint64_t GetUptime64(void)
{
const uint32_t lo = timer_hw->timelr;
const uint32_t hi = timer_hw->timehr;
return ((uint64_t)hi << 32U) | lo;
}
inline uint32_t GetTime32(void)
{
return timer_hw->timelr;
}
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');
}
inline void PRN32(uint32_t *val)
{
*val ^= *val << 13;
*val ^= *val >> 17;
*val ^= *val << 5;
}
#endif

Wyświetl plik

@ -69,7 +69,7 @@
#include "build/dco.pio.h"
int32_t si32precise_cycles;
int32_t si32precise_cycles; /* External in order to support ISR. */
/// @brief Initializes DCO context and prepares PIO hardware.
/// @param pdco Ptr to DCO context.
@ -95,18 +95,17 @@ 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;
}
/// @brief Sets DCO working frequency in Hz: Fout = ui32_frq_hz + ui32_frq_millihz * 1e-3.
/// @param pdco Ptr to DCO context.
/// @param ui32_frq_hz The `coarse` part of frequency [Hz].
/// @param i32_frq_hz The `coarse` part of frequency [Hz]. Might be negative.
/// @param ui32_frq_millihz The `fine` part of frequency [Hz].
/// @return 0 if OK. -1 invalid freq.
/// @attention The func can be called while DCO running.
int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, uint32_t ui32_frq_millihz)
int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, int32_t ui32_frq_millihz)
{
assert_(pdco);
assert(pdco->_clkfreq_hz);
@ -117,14 +116,43 @@ 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;
}
/// @brief Obtains the frequency shift [milliHz] which is calculated for a given frequency.
/// @param pdco Ptr to Context.
/// @param u64_desired_frq_millihz The frequency for which we want to calculate correction.
/// @return The value of correction we need to subtract from desired freq. to compensate
/// @return Pico's reference clock shift. 2854974.
int32_t PioDCOGetFreqShiftMilliHertz(const PioDco *pdco, uint64_t u64_desired_frq_millihz)
{
assert_(pdco);
if(!pdco->_pGPStime)
{
return 0U;
}
static int64_t i64_last_correction = 0;
const int64_t dt = pdco->_pGPStime->_time_data._i32_freq_shift_ppb; /* Parts per billion. */
if(dt)
{
i64_last_correction = dt;
}
int32_t i32ret_millis;
if(i64_last_correction)
{
int64_t i64corr_coeff = (u64_desired_frq_millihz + 500000LL) / 1000000LL;
i32ret_millis = (i64_last_correction * i64corr_coeff + 50000LL) / 1000000LL;
return i32ret_millis;
}
return 0U;
}
/// @brief Starts the DCO.
/// @param pdco Ptr to DCO context.
void PioDCOStart(PioDco *pdco)
@ -151,12 +179,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,12 +193,11 @@ 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.
This forms a phase locked loop which provides precise freq
on long run. */
/* RPix: Calculate the difference betwixt calculated value scaled to
fine resolution back and precise value of DCO cycles per CPU CLK cycle.
This forms a phase locked loop which provides precise freq */
i32acc_error += (i32wc<<24) - i32reg;
/* RPix: Set PIO array contents corrected by pio program delay
@ -183,3 +208,27 @@ 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.
/// @attention Not actual so far. User-independent freq. correction not impl'd yet. !FIXME!
void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode)
{
assert_(pdco);
pdco->_mode = emode;
switch(emode)
{
case eDCOMODE_IDLE:
PioDCOStop(pdco);
break;
case eDCOMODE_GPS_COMPENSATED:
PioDCOStart(pdco);
break;
default:
break;
}
}

Wyświetl plik

@ -72,8 +72,18 @@
#include "defines.h"
#include "../gpstime/GPStime.h"
enum PioDcoMode
{
eDCOMODE_IDLE = 0, /* No output. */
eDCOMODE_GPS_COMPENSATED= 2 /* Internally compensated, if GPS available. */
};
typedef struct
{
enum PioDcoMode _mode; /* Running mode. */
PIO _pio; /* Worker PIO on this DCO. */
int _gpio; /* Pico' GPIO for DCO output. */
@ -87,14 +97,19 @@ typedef struct
uint32_t _clkfreq_hz; /* CPU CLK freq, Hz. */
GPStimeContext *_pGPStime; /* Ptr to GPS time context. */
} PioDco;
int PioDCOInit(PioDco *pdco, int gpio, int cpuclkhz);
int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, uint32_t ui32_frq_millihz);
int PioDCOSetFreq(PioDco *pdco, uint32_t u32_frq_hz, int32_t u32_frq_millihz);
int32_t PioDCOGetFreqShiftMilliHertz(const PioDco *pdco, uint64_t u64_desired_frq_millihz);
void PioDCOStart(PioDco *pdco);
void PioDCOStop(PioDco *pdco);
void PioDCOSetMode(PioDco *pdco, enum PioDcoMode emode);
void RAM (PioDCOWorker)(PioDco *pDCO);
#endif

62
test.c
Wyświetl plik

@ -68,6 +68,7 @@
///////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "defines.h"
@ -75,14 +76,17 @@
#include "build/dco.pio.h"
#include "hardware/vreg.h"
#include "pico/multicore.h"
#include "pico/stdio/driver.h"
#include "./lib/assert.h"
#include "./debug/logutils.h"
#include "hwdefs.h"
#include <GPStime.h>
#define GEN_FRQ_HZ 9400000L
PioDco DCO;
void PRN32(uint32_t *val);
PioDco DCO; /* External in order to access in both cores. */
/* This is the code of dedicated core.
We deal with extremely precise real-time task. */
@ -206,26 +210,66 @@ void RAM (SpinnerWide4FSKTest)(void)
}
}
/* This example sets the OUT frequency to 5.555 MHz.
Next every ~1 sec the shift of the OUT frequency relative to GPS
reference is calculated and the OUT frequency is corrected.
The example is working only when GPS receiver provides an
accurate PPS output (pulse per second). If no such option,
the correction parameter is zero.
*/
void RAM (SpinnerGPSreferenceTest)(void)
{
const uint32_t ku32_freq = 5555000UL;
const int kigps_pps_pin = 2;
int32_t i32_compensation_millis = 0;
GPStimeContext *pGPS = GPStimeInit(0, 9600, kigps_pps_pin);
assert_(pGPS);
DCO._pGPStime = pGPS;
int tick = 0;
for(;;)
{
PioDCOSetFreq(&DCO, ku32_freq, -2*i32_compensation_millis);
/* LED signal */
gpio_put(PICO_DEFAULT_LED_PIN, 1);
sleep_ms(2500);
i32_compensation_millis =
PioDCOGetFreqShiftMilliHertz(&DCO, (uint64_t)(ku32_freq * 1000LL));
gpio_put(PICO_DEFAULT_LED_PIN, 0);
sleep_ms(2500);
if(0 == ++tick % 6)
{
stdio_set_driver_enabled(&stdio_uart, false);
GPStimeDump(&(pGPS->_time_data));
stdio_set_driver_enabled(&stdio_uart, true);
}
}
}
int main()
{
const uint32_t clkhz = PLL_SYS_MHZ * 1000000L;
set_sys_clock_khz(clkhz / 1000L, true);
stdio_init_all();
sleep_ms(1000);
printf("Start\n");
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
multicore_launch_core1(core1_entry);
//SpinnerSweepTest();
SpinnerMFSKTest();
//SpinnerMFSKTest();
//SpinnerRTTYTest();
//SpinnerMilliHertzTest();
//SpinnerWide4FSKTest();
SpinnerGPSreferenceTest();
}
void PRN32(uint32_t *val)
{
*val ^= *val << 13;
*val ^= *val >> 17;
*val ^= *val << 5;
}