diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f8ffdc..87d5dc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ pico_generate_pio_header(pico-wspr-tx ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillat target_sources(pico-wspr-tx PUBLIC ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/lib/assert.c ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco/piodco.c + ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/gpstime/GPStime.c ${CMAKE_CURRENT_LIST_DIR}/TxChannel/TxChannel.c ${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty/WSPRutility.c ${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty/nhash.c @@ -47,13 +48,14 @@ target_sources(pico-wspr-tx PUBLIC pico_set_program_name(pico-wspr-tx "pico-wspr-tx") pico_set_program_version(pico-wspr-tx "0.1") -pico_enable_stdio_uart(pico-wspr-tx 0) +pico_enable_stdio_uart(pico-wspr-tx 1) pico_enable_stdio_usb(pico-wspr-tx 1) # Add the standard include files to the build target_include_directories(pico-wspr-tx PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco + ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/debug ${CMAKE_CURRENT_LIST_DIR}/TxChannel ${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon ${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty diff --git a/TxChannel/TxChannel.h b/TxChannel/TxChannel.h index bfdfe7b..2951e45 100644 --- a/TxChannel/TxChannel.h +++ b/TxChannel/TxChannel.h @@ -70,6 +70,7 @@ typedef struct PioDco *_p_oscillator; uint32_t _u32_dialfreqhz; + int _i_tx_gpio; } TxChannelContext; diff --git a/WSPRbeacon/WSPRbeacon.c b/WSPRbeacon/WSPRbeacon.c index 19c7a13..e6f2876 100644 --- a/WSPRbeacon/WSPRbeacon.c +++ b/WSPRbeacon/WSPRbeacon.c @@ -58,9 +58,13 @@ /// @param pgridsquare Maidenhead locator, 7 chr max. /// @param txpow_dbm TX power, db`mW. /// @param pdco Ptr to working DCO. -/// @return the context. +/// @param dial_freq_hz The begin of working WSPR passband. +/// @param shift_freq_hz The shift of tx freq. relative to dial_freq_hz. +/// @param gpio Pico's GPIO pin of RF output. +/// @return Ptr to the new context. WSPRbeaconContext *WSPRbeaconInit(const char *pcallsign, const char *pgridsquare, int txpow_dbm, - PioDco *pdco) + PioDco *pdco, uint32_t dial_freq_hz, uint32_t shift_freq_hz, + int gpio) { assert_(pdco); @@ -70,9 +74,12 @@ WSPRbeaconContext *WSPRbeaconInit(const char *pcallsign, const char *pgridsquare strncpy(p->_pu8_callsign, pcallsign, sizeof(p->_pu8_callsign)); strncpy(p->_pu8_locator, pgridsquare, sizeof(p->_pu8_locator)); p->_u8_txpower = txpow_dbm; + p->_pTX = TxChannelInit(682667, 0, pdco); assert_(p->_pTX); + p->_pTX->_u32_dialfreqhz = dial_freq_hz + shift_freq_hz; + p->_pTX->_i_tx_gpio = gpio; return p; } @@ -112,3 +119,62 @@ int WSPRbeaconSendPacket(const WSPRbeaconContext *pctx) return 0; } + +int WSPRbeaconTxScheduler(WSPRbeaconContext *pctx, int verbose) +{ + assert_(pctx); + + const uint64_t u64tmnow = GetUptime64(); + const uint32_t is_GPS_available = pctx->_pTX->_p_oscillator->_pGPStime->_time_data._u32_nmea_gprmc_count; + const uint32_t is_GPS_active = pctx->_pTX->_p_oscillator->_pGPStime->_time_data._u8_is_solution_active; + const uint32_t is_GPS_override = pctx->_txSched._u8_tx_GPS_past_time == YES; + + const uint64_t u64_GPS_last_age_sec + = (u64tmnow - pctx->_pTX->_p_oscillator->_pGPStime->_time_data._u64_sysclk_nmea_last) / 1000000ULL; + +#if 1 + StampPrintf("%llu %lu %lu %lu %llu", u64tmnow, is_GPS_available, is_GPS_active, is_GPS_override, u64_GPS_last_age_sec); + //return 0; +#endif + + if(!is_GPS_available) + { + StampPrintf("Waiting for GPS receiver..."); + return -1; + } + + if(is_GPS_active || (is_GPS_override && u64_GPS_last_age_sec < 2 * HOUR)) + { + const uint32_t u32_unixtime_now + = pctx->_pTX->_p_oscillator->_pGPStime->_time_data._u32_utime_nmea_last + u64_GPS_last_age_sec; + + const int isec_of_hour = u32_unixtime_now % HOUR; + const int islot_number = isec_of_hour / (2 * MINUTE); + const int islot_modulo = islot_number % pctx->_txSched._u8_tx_slot_skip; + + static int itx_trigger = 0; + if(ZERO == islot_modulo) + { + if(!itx_trigger) + { + itx_trigger = 1; + StampPrintf("Start transmission."); + + //WSPRbeaconSetDialFreq(pWB, WSPR_DIAL_FREQ_HZ + WSPR_SHIFT_FREQ_HZ); + WSPRbeaconCreatePacket(pctx); + + PioDCOStart(pctx->_pTX->_p_oscillator); + sleep_ms(100); + WSPRbeaconSendPacket(pctx); + } + } + else + { + itx_trigger = 0; + StampPrintf("NO transmission slot."); + PioDCOStop(pctx->_pTX->_p_oscillator); + } + } + + return 0; +} diff --git a/WSPRbeacon/WSPRbeacon.h b/WSPRbeacon/WSPRbeacon.h index d6e0165..57e4f03 100644 --- a/WSPRbeacon/WSPRbeacon.h +++ b/WSPRbeacon/WSPRbeacon.h @@ -55,6 +55,17 @@ #include #include #include +#include + +typedef struct +{ + uint8_t _u8_tx_slot_skip; /* 0=1tx0skip, 1=1tx1skip, 2=1tx2skip, ... */ + uint8_t _u8_tx_GPS_mandatory; /* No tx when no active GPS solution. */ + uint8_t _u8_tx_GPS_past_time; /* Override _u8_tx_GPS_mandatory if there + was solution in the past. */ + uint8_t _u8_tx_heating_pause_min; /* No tx during this interval from start. */ + +} WSPRbeaconSchedule; typedef struct { @@ -66,12 +77,17 @@ typedef struct TxChannelContext *_pTX; + WSPRbeaconSchedule _txSched; + } WSPRbeaconContext; WSPRbeaconContext *WSPRbeaconInit(const char *pcallsign, const char *pgridsquare, int txpow_dbm, - PioDco *pdco); + PioDco *pdco, uint32_t dial_freq_hz, uint32_t shift_freq_hz, + int gpio); void WSPRbeaconSetDialFreq(WSPRbeaconContext *pctx, uint32_t freq_hz); int WSPRbeaconCreatePacket(WSPRbeaconContext *pctx); int WSPRbeaconSendPacket(const WSPRbeaconContext *pctx); +int WSPRbeaconTxScheduler(WSPRbeaconContext *pctx, int verbose); + #endif diff --git a/core1.c b/core1.c index 4fe6108..178e71e 100644 --- a/core1.c +++ b/core1.c @@ -56,23 +56,30 @@ #include #include "defines.h" -extern PioDco DCO; +#include + +extern WSPRbeaconContext *pWSPR; /// @brief The code of dedicated core' program running HF oscillator. void Core1Entry() { + assert_(pWSPR); + const uint32_t clkhz = PLL_SYS_MHZ * MHz; - const uint32_t freq_hz = WSPR_DIAL_FREQ_HZ + WSPR_SHIFT_FREQ_HZ; + const uint32_t freq_hz = pWSPR->_pTX->_u32_dialfreqhz; + + PioDco *p = pWSPR->_pTX->_p_oscillator; + assert_(p); /* Initialize DCO */ - assert_(0 == PioDCOInit(&DCO, 6, clkhz)); + assert_(0 == PioDCOInit(p, pWSPR->_pTX->_i_tx_gpio, clkhz)); /* Run DCO. */ - PioDCOStart(&DCO); + //PioDCOStart(p); /* Set initial freq. */ - assert_(0 == PioDCOSetFreq(&DCO, freq_hz, 0u)); + assert_(0 == PioDCOSetFreq(p, freq_hz, 0U)); /* Run the main DCO algorithm. It spins forever. */ - PioDCOWorker(&DCO); + PioDCOWorker(p); } diff --git a/debug/logutils.c b/debug/logutils.c index 39ac59d..29a6739 100644 --- a/debug/logutils.c +++ b/debug/logutils.c @@ -76,6 +76,7 @@ void StampPrintf(const char* pformat, ...) const uint32_t tm_sec = (uint32_t)(tm_us / 1000000ULL); tm_us -= (uint64_t)tm_sec * 1000000ULL; + stdio_set_driver_enabled(&stdio_uart, false); printf("%02lud%02lu:%02lu:%02lu.%06llu [%04lu] ", tm_day, tm_hour, tm_min, tm_sec, tm_us, sTick++); va_list argptr; @@ -84,4 +85,5 @@ void StampPrintf(const char* pformat, ...) va_end(argptr); printf("\n"); + stdio_set_driver_enabled(&stdio_uart, true); } diff --git a/defines.h b/defines.h index f45fb13..96fe430 100644 --- a/defines.h +++ b/defines.h @@ -70,6 +70,7 @@ #define YES 1 /* The answer is yes. */ #define OFF 0 /* Turn something off. */ #define ON 1 /* Turn something on. */ +#define ZERO 0 /* Something in zero state. */ #define RAM __not_in_flash_func /* Place time-critical func in RAM */ #define RAM_A __not_in_flash("A") /* Place time-critical var in RAM */ @@ -92,4 +93,9 @@ //#define WSPR_FREQ_STEP_MILHZ 1465UL #define WSPR_FREQ_STEP_MILHZ 2930UL +#define MINUTE 60 +#define HOUR (60 * MINUTE) + +#define GPS_PPS_PIN 2 + #endif diff --git a/main.c b/main.c index dbaf692..2e68798 100644 --- a/main.c +++ b/main.c @@ -53,32 +53,66 @@ #include #include -#include "defines.h" - #include "pico/multicore.h" #include "pico-hf-oscillator/lib/assert.h" #include "pico-hf-oscillator/defines.h" +#include "defines.h" #include - #include - #include "debug/logutils.h" -PioDco DCO; - void InitPicoHW(void); void Core1Entry(void); +WSPRbeaconContext *pWSPR; + int main() { - DEBUGPRINTF("\n"); - sleep_ms(1000); - DEBUGPRINTF("Pico-WSPR-tx start."); + StampPrintf("\n"); + sleep_ms(5000); + StampPrintf("R2BDY Pico-WSPR-tx start."); InitPicoHW(); - DEBUGPRINTF("WSPR beacon init..."); - WSPRbeaconContext *pWB = WSPRbeaconInit("R2BDY", "KO85", 6, &DCO); + StampPrintf("WSPR beacon init..."); + + PioDco DCO; + GPStimeContext *pGPS = GPStimeInit(0, 9600, GPS_PPS_PIN); + assert_(pGPS); + DCO._pGPStime = pGPS; + + WSPRbeaconContext *pWB = WSPRbeaconInit( + "R2BDY", /* the Callsign. */ + "KO85", /* the QTH locator. */ + 10, /* Tx power, dbm. */ + &DCO, /* the PioDCO object. */ + 7040000UL, /* the dial frequency. */ + 55UL, /* the carrier freq. shift relative to dial freq. */ + 6 /* RF output GPIO pin. */ + ); + assert_(pWB); + pWSPR = pWB; + + pWB->_txSched._u8_tx_GPS_mandatory = YES; /* Send WSPR signals only when GPS solution is active. */ + pWB->_txSched._u8_tx_GPS_past_time = NO; /* No relying on GPS sp;ution in the past. */ + pWB->_txSched._u8_tx_slot_skip = 1; /* 1 slot tx, 1 slot idle, etc. */ + pWB->_txSched._u8_tx_heating_pause_min = 1; /* Give 1 minute pre-heating ere first transmition. */ + + StampPrintf("PioDco ADDR: %p", pWSPR->_pTX->_p_oscillator); + + StampPrintf("RF oscillator start..."); + sleep_ms(500); + multicore_launch_core1(Core1Entry); + + for(;;) + { + WSPRbeaconTxScheduler(pWB, YES); + + StampPrintf("."); + + sleep_ms(1000); + } +/* WSPRbeaconSetDialFreq(pWB, WSPR_DIAL_FREQ_HZ + WSPR_SHIFT_FREQ_HZ); DEBUGPRINTF("OK"); @@ -99,4 +133,5 @@ int main() DEBUGPRINTF("tick."); sleep_ms(1000); } +*/ }