diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c66b85..d4534ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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,33 +23,30 @@ 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}/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}/debug/logutils.c ${CMAKE_CURRENT_LIST_DIR}/test.c ) 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_uart(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}/gpstime - ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required + ${CMAKE_CURRENT_LIST_DIR}/.. ) # Add any user requested libraries @@ -63,7 +58,7 @@ target_link_libraries( hardware_timer hardware_clocks hardware_pio - hardware_vreg + #hardware_vreg ) pico_add_extra_outputs(pico-hf-oscillator-test) diff --git a/debug/logutils.c b/debug/logutils.c new file mode 100644 index 0000000..39ac59d --- /dev/null +++ b/debug/logutils.c @@ -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 +#include +#include +#include + +#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"); +} diff --git a/debug/logutils.h b/debug/logutils.h new file mode 100644 index 0000000..0f88db9 --- /dev/null +++ b/debug/logutils.h @@ -0,0 +1,6 @@ +#ifndef LOGUTILS_H_ +#define LOGUTILS_H_ + +void StampPrintf(const char* pformat, ...); + +#endif diff --git a/doc/pico-hf-oscillator.pdf b/doc/pico-hf-oscillator.pdf new file mode 100644 index 0000000..a398faa Binary files /dev/null and b/doc/pico-hf-oscillator.pdf differ diff --git a/doc/pico-hf-oscillator.png b/doc/pico-hf-oscillator.png new file mode 100644 index 0000000..1af48a7 Binary files /dev/null and b/doc/pico-hf-oscillator.png differ diff --git a/gpstime/GPStime.c b/gpstime/GPStime.c index 6ef19e3..adbdde7 100644 --- a/gpstime/GPStime.c +++ b/gpstime/GPStime.c @@ -63,22 +63,25 @@ 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. + // Set up our UART with the required speed & assign pins. uart_init(uart_id ? uart1 : uart0, uart_baud); - - // Set the TX and RX pins by using the function select on the GPIO - // Set datasheet for more information on function select 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; @@ -90,13 +93,16 @@ GPStimeContext *GPStimeInit(int uart_id, int uart_baud, int 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); + 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); @@ -107,7 +113,9 @@ void GPStimeDestroy(GPStimeContext **pp) *pp = NULL; } -void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events) +/// @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) @@ -145,7 +153,7 @@ void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events) } } -/// @brief Calculates current unixtime using information available. +/// @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. @@ -177,7 +185,8 @@ int GPStimeGetTime(const GPStimeContext *pg, uint32_t *u32_tmdst) return 0; } -void __not_in_flash_func (GPStimeUartRxIsr)() +/// @brief UART FIFO ISR. Processes another N chars receiver from GPS rec. +void RAM (GPStimeUartRxIsr)() { if(spGPStimeContext) { @@ -194,11 +203,17 @@ void __not_in_flash_func (GPStimeUartRxIsr)() { spGPStimeContext->_u8_ixw = 0; spGPStimeContext->_i32_error_count -= GPStimeProcNMEAsentence(spGPStimeContext); - //printf("err: %ld\n", spGPStimeContext->_i32_error_count); } } } +/// @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); @@ -207,7 +222,6 @@ int GPStimeProcNMEAsentence(GPStimeContext *pg) if(prmc) { 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) @@ -259,15 +273,17 @@ int GPStimeProcNMEAsentence(GPStimeContext *pg) pg->_time_data._u32_utime_nmea_last = GPStime2UNIX(prmc + u8ixcollector[8], prmc + u8ixcollector[0]); pg->_time_data._u64_sysclk_nmea_last = tm_fix; } + + ++pg->_time_data._u32_nmea_gprmc_count; } return 0; - - /* - "$GPRMC,105954.000,A,3150.6731,N,11711.9399,E,0.00,96.10,250313,,,A*53"; - */ } +/// @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); @@ -291,17 +307,18 @@ uint32_t GPStime2UNIX(const char *pdate, const char *ptime) return 0; } +/// @brief Dumps the GPS data struct to stdio. +/// @param pd Ptr to Context. 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); + 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); } diff --git a/gpstime/GPStime.h b/gpstime/GPStime.h index 3c24c2d..aecb82f 100644 --- a/gpstime/GPStime.h +++ b/gpstime/GPStime.h @@ -89,6 +89,7 @@ typedef struct 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. */ @@ -120,8 +121,8 @@ void GPStimeDestroy(GPStimeContext **pp); int GPStimeProcNMEAsentence(GPStimeContext *pg); -void __not_in_flash_func (GPStimePPScallback)(uint gpio, uint32_t events); -void __not_in_flash_func (GPStimeUartRxIsr)(); +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); diff --git a/lib/utility.h b/lib/utility.h index df46099..b5edcb4 100644 --- a/lib/utility.h +++ b/lib/utility.h @@ -27,4 +27,11 @@ 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 diff --git a/piodco/piodco.c b/piodco/piodco.c index f7c7d83..fc2170e 100644 --- a/piodco/piodco.c +++ b/piodco/piodco.c @@ -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. @@ -101,11 +101,11 @@ int PioDCOInit(PioDco *pdco, int gpio, int cpuclkhz) /// @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); @@ -121,6 +121,38 @@ int PioDCOSetFreq(PioDco *pdco, uint32_t ui32_frq_hz, uint32_t ui32_frq_millihz) 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) @@ -163,10 +195,9 @@ void RAM (PioDCOWorker)(PioDco *pDCO) DCO cycle, corrected by accumulated error (feedback of the PLL). */ 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 @@ -181,9 +212,23 @@ void RAM (PioDCOWorker)(PioDco *pDCO) /// @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; - eDCOMODE_IDLE == emode ? PioDCOStop(pdco) : PioDCOStart(pdco); + + switch(emode) + { + case eDCOMODE_IDLE: + PioDCOStop(pdco); + break; + + case eDCOMODE_GPS_COMPENSATED: + PioDCOStart(pdco); + break; + + default: + break; + } } diff --git a/piodco/piodco.h b/piodco/piodco.h index 0831cc0..27c94c6 100644 --- a/piodco/piodco.h +++ b/piodco/piodco.h @@ -72,12 +72,12 @@ #include "defines.h" +#include "../gpstime/GPStime.h" + enum PioDcoMode { - eDCOMODE_IDLE = 0, - eDCOMODE_FREERUN = 1, - eDCOMODE_GPS_REFERENCE = 2, - eDCOMODE_THERMO_COMPENSATION = 3 + eDCOMODE_IDLE = 0, /* No output. */ + eDCOMODE_GPS_COMPENSATED= 2 /* Internally compensated, if GPS available. */ }; typedef struct @@ -97,10 +97,13 @@ 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); diff --git a/test.c b/test.c index 40c9533..ce51378 100644 --- a/test.c +++ b/test.c @@ -79,14 +79,14 @@ #include "pico/stdio/driver.h" #include "./lib/assert.h" +#include "./debug/logutils.h" #include "hwdefs.h" #include #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. */ @@ -210,6 +210,47 @@ 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(500); + + i32_compensation_millis = + PioDCOGetFreqShiftMilliHertz(&DCO, (uint64_t)(ku32_freq * 1000LL)); + + StampPrintf("GPS solution status: %s | GPS-based freq compensation: %ld milliHz", + pGPS->_time_data._u8_is_solution_active ? "Active" : "No solution", + i32_compensation_millis); + + gpio_put(PICO_DEFAULT_LED_PIN, 0); + sleep_ms(500); + + if(0 == ++tick % 30) + GPStimeDump(&(pGPS->_time_data)); + } +} + int main() { const uint32_t clkhz = PLL_SYS_MHZ * 1000000L; @@ -218,14 +259,8 @@ int main() stdio_init_all(); sleep_ms(1000); printf("Start\n"); - - GPStimeContext *pGPS = GPStimeInit(0, 9600, 2); - for(;;) - { - GPStimeDump(&(pGPS->_time_data)); - sleep_ms(1000); - } - + +/* for(;;) { char ch = uart_getc(uart0); @@ -236,22 +271,17 @@ int main() stdio_set_driver_enabled(&stdio_uart, true); } } - +*/ 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; -}