kopia lustrzana https://github.com/RPiks/pico-hf-oscillator
Merge branch 'gpstest'
commit
3886eb4408
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef LOGUTILS_H_
|
||||
#define LOGUTILS_H_
|
||||
|
||||
void StampPrintf(const char* pformat, ...);
|
||||
|
||||
#endif
|
13
defines.h
13
defines.h
|
@ -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 |
Plik binarny nie jest wyświetlany.
|
@ -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(<m);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef STRNSTR_H_
|
||||
#define STRNSTR_H_
|
||||
|
||||
char *
|
||||
strnstr(const char *s, const char *find, size_t slen);
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
62
test.c
|
@ -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;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue