kopia lustrzana https://github.com/RPiks/pico-WSPR-tx
Porównaj commity
5 Commity
Autor | SHA1 | Data |
---|---|---|
roman | d11ef6b23f | |
Roman Piksaykin | 70a86638df | |
roman | 4f3e17e5a1 | |
roman | d9bac8da61 | |
roman | d28d20062a |
|
@ -22,7 +22,7 @@ project(pico-wspr-tx C CXX ASM)
|
|||
|
||||
pico_sdk_init()
|
||||
add_executable(pico-wspr-tx)
|
||||
pico_generate_pio_header(pico-wspr-tx ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco/dco.pio)
|
||||
pico_generate_pio_header(pico-wspr-tx ${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco/dco2.pio)
|
||||
|
||||
target_sources(pico-wspr-tx PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/lib/assert.c
|
||||
|
@ -31,6 +31,7 @@ target_sources(pico-wspr-tx PUBLIC
|
|||
${CMAKE_CURRENT_LIST_DIR}/TxChannel/TxChannel.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty/WSPRutility.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty/nhash.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty/maidenhead.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/WSPRbeacon.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/debug/logutils.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/init.c
|
||||
|
|
|
@ -3,7 +3,7 @@ WSPR beacon for Raspberry Pi Pico, based on pico-hf-oscillator PLL DCO library a
|
|||
The GPS reference is used to compensate Pico's clock drift. GPS is also used to schedule WSPR transmissions.
|
||||
|
||||
![image](https://github.com/RPiks/pico-WSPR-tx/assets/47501785/3f835a9d-fa42-4eb8-ba93-ea72033d9e62)
|
||||
The example of this WSPR beacon (QRPX power level) reception. Max QRB is ~3400 km.
|
||||
The example of this WSPR beacon (QRPX power level) reception. Last max QRB is ~3400 km on 40m band.
|
||||
|
||||
![image](https://github.com/RPiks/pico-WSPR-tx/assets/47501785/a86280b9-71cb-4bb2-8b3c-0e33d2499aca)
|
||||
High spectrum quality and less than 1Hz frequency drift.
|
||||
|
@ -28,7 +28,7 @@ My QRZ page is https://www.qrz.com/db/R2BDY
|
|||
|
||||
6. Load the .uf2 file (2) into the Pico.
|
||||
|
||||
7. Initialy the operating HF band is 40 meter (WSPR dial frequency is 7040 kHz).
|
||||
7. Initialy the operating HF band is 17 meter (WSPR dial frequency is 18106 kHz).
|
||||
|
||||
8. Provide the feedback by clicking like on the github page of the project :).
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ TxChannelContext *TxChannelInit(const uint32_t bit_period_us, uint8_t timer_alar
|
|||
/// @return A count of bytes.
|
||||
uint8_t TxChannelPending(TxChannelContext *pctx)
|
||||
{
|
||||
return 256 + pctx->_ix_input - pctx->_ix_output;
|
||||
return 256L + (int)pctx->_ix_input - (int)pctx->_ix_output;
|
||||
}
|
||||
|
||||
/// @brief Push a number of bytes to the output FIFO.
|
||||
|
@ -154,14 +154,3 @@ void TxChannelClear(TxChannelContext *pctx)
|
|||
{
|
||||
pctx->_ix_input = pctx->_ix_output = 0;
|
||||
}
|
||||
/*
|
||||
void TxChannelStart(TxChannelContext *pctx)
|
||||
{
|
||||
pctx->_b_allowtx = YES;
|
||||
}
|
||||
|
||||
void TxChannelStop(TxChannelContext *pctx)
|
||||
{
|
||||
pctx->_b_allowtx = NO;
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "WSPRbeacon.h"
|
||||
#include <WSPRutility.h>
|
||||
#include <maidenhead.h>
|
||||
|
||||
/// @brief Initializes a new WSPR beacon context.
|
||||
/// @param pcallsign HAM radio callsign, 12 chr max.
|
||||
|
@ -112,7 +113,7 @@ int WSPRbeaconSendPacket(const WSPRbeaconContext *pctx)
|
|||
{
|
||||
assert_(pctx);
|
||||
assert_(pctx->_pTX);
|
||||
assert_(pctx->_pTX->_u32_dialfreqhz > 1100 * kHz);
|
||||
assert_(pctx->_pTX->_u32_dialfreqhz > 500 * kHz);
|
||||
|
||||
TxChannelClear(pctx->_pTX);
|
||||
|
||||
|
@ -214,3 +215,30 @@ void WSPRbeaconDumpContext(const WSPRbeaconContext *pctx)
|
|||
StampPrintf("pps:%llu", pGPS->_time_data._u64_sysclk_pps_last);
|
||||
StampPrintf("ppb:%lld", pGPS->_time_data._i32_freq_shift_ppb);
|
||||
}
|
||||
|
||||
/// @brief Extracts maidenhead type QTH locator (such as KO85) using GPS coords.
|
||||
/// @param pctx Ptr to WSPR beacon context.
|
||||
/// @return ptr to string of QTH locator (static duration object inside get_mh).
|
||||
/// @remark It uses third-party project https://github.com/sp6q/maidenhead .
|
||||
char *WSPRbeaconGetLastQTHLocator(const WSPRbeaconContext *pctx)
|
||||
{
|
||||
assert_(pctx);
|
||||
assert_(pctx->_pTX);
|
||||
assert_(pctx->_pTX->_p_oscillator);
|
||||
assert_(pctx->_pTX->_p_oscillator->_pGPStime);
|
||||
|
||||
const double lat = 1e-5 * (double)pctx->_pTX->_p_oscillator->_pGPStime->_time_data._i64_lat_100k;
|
||||
const double lon = 1e-5 * (double)pctx->_pTX->_p_oscillator->_pGPStime->_time_data._i64_lon_100k;
|
||||
|
||||
return get_mh(lat, lon, 8);
|
||||
}
|
||||
|
||||
uint8_t WSPRbeaconIsGPSsolutionActive(const WSPRbeaconContext *pctx)
|
||||
{
|
||||
assert_(pctx);
|
||||
assert_(pctx->_pTX);
|
||||
assert_(pctx->_pTX->_p_oscillator);
|
||||
assert_(pctx->_pTX->_p_oscillator->_pGPStime);
|
||||
|
||||
return YES == pctx->_pTX->_p_oscillator->_pGPStime->_time_data._u8_is_solution_active;
|
||||
}
|
||||
|
|
|
@ -92,4 +92,7 @@ int WSPRbeaconTxScheduler(WSPRbeaconContext *pctx, int verbose);
|
|||
|
||||
void WSPRbeaconDumpContext(const WSPRbeaconContext *pctx);
|
||||
|
||||
char *WSPRbeaconGetLastQTHLocator(const WSPRbeaconContext *pctx);
|
||||
uint8_t WSPRbeaconIsGPSsolutionActive(const WSPRbeaconContext *pctx);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#include "maidenhead.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char letterize(int x) {
|
||||
return (char) x + 65;
|
||||
}
|
||||
|
||||
char* get_mh(double lat, double lon, int size) {
|
||||
static char locator[11];
|
||||
double LON_F[]={20,2.0,0.083333,0.008333,0.0003472083333333333};
|
||||
double LAT_F[]={10,1.0,0.0416665,0.004166,0.0001735833333333333};
|
||||
int i;
|
||||
lon += 180;
|
||||
lat += 90;
|
||||
|
||||
if (size <= 0 || size > 10) size = 6;
|
||||
size/=2; size*=2;
|
||||
|
||||
for (i = 0; i < size/2; i++){
|
||||
if (i % 2 == 1) {
|
||||
locator[i*2] = (char) (lon/LON_F[i] + '0');
|
||||
locator[i*2+1] = (char) (lat/LAT_F[i] + '0');
|
||||
} else {
|
||||
locator[i*2] = letterize((int) (lon/LON_F[i]));
|
||||
locator[i*2+1] = letterize((int) (lat/LAT_F[i]));
|
||||
}
|
||||
lon = fmod(lon, LON_F[i]);
|
||||
lat = fmod(lat, LAT_F[i]);
|
||||
}
|
||||
locator[i*2]=0;
|
||||
return locator;
|
||||
}
|
||||
|
||||
char* complete_mh(char* locator) {
|
||||
static char locator2[11] = "LL55LL55LL";
|
||||
int len = strlen(locator);
|
||||
if (len >= 10) return locator;
|
||||
memcpy(locator2, locator, strlen(locator));
|
||||
return locator2;
|
||||
}
|
||||
|
||||
double mh2lon(char* locator) {
|
||||
double field, square, subsquare, extsquare, precsquare;
|
||||
int len = strlen(locator);
|
||||
if (len > 10) return 0;
|
||||
if (len < 10) locator = complete_mh(locator);
|
||||
field = (locator[0] - 'A') * 20.0;
|
||||
square = (locator[2] - '0') * 2.0;
|
||||
subsquare = (locator[4] - 'A') / 12.0;
|
||||
extsquare = (locator[6] - '0') / 120.0;
|
||||
precsquare = (locator[8] - 'A') / 2880.0;
|
||||
return field + square + subsquare + extsquare + precsquare - 180;
|
||||
}
|
||||
|
||||
double mh2lat(char* locator) {
|
||||
double field, square, subsquare, extsquare, precsquare;
|
||||
int len = strlen(locator);
|
||||
if (len > 10) return 0;
|
||||
if (len < 10) locator = complete_mh(locator);
|
||||
field = (locator[1] - 'A') * 10.0;
|
||||
square = (locator[3] - '0') * 1.0;
|
||||
subsquare = (locator[5] - 'A') / 24.0;
|
||||
extsquare = (locator[7] - '0') / 240.0;
|
||||
precsquare = (locator[9] - 'A') / 5760.0;
|
||||
return field + square + subsquare + extsquare + precsquare - 90;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef MAIDENHEAD_H
|
||||
#define MAIDENHEAD_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
char* get_mh(double lat, double lon, int size);
|
||||
char* complete_mh(char* locator);
|
||||
double mh2lon(char* locator);
|
||||
double mh2lat(char* locator);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
2
build.sh
2
build.sh
|
@ -1,6 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
mkdir build
|
||||
git submodule init
|
||||
git submodule update
|
||||
git submodule foreach git pull origin main
|
||||
cd build
|
||||
cmake ..
|
||||
|
|
5
core1.c
5
core1.c
|
@ -65,7 +65,6 @@ void Core1Entry()
|
|||
assert_(pWSPR);
|
||||
|
||||
const uint32_t clkhz = PLL_SYS_MHZ * MHz;
|
||||
const uint32_t freq_hz = pWSPR->_pTX->_u32_dialfreqhz;
|
||||
|
||||
PioDco *p = pWSPR->_pTX->_p_oscillator;
|
||||
assert_(p);
|
||||
|
@ -74,8 +73,8 @@ void Core1Entry()
|
|||
assert_(0 == PioDCOInit(p, pWSPR->_pTX->_i_tx_gpio, clkhz));
|
||||
|
||||
/* Set initial freq. */
|
||||
assert_(0 == PioDCOSetFreq(p, freq_hz, 0U));
|
||||
assert_(0 == PioDCOSetFreq(p, pWSPR->_pTX->_u32_dialfreqhz, 0U));
|
||||
|
||||
/* Run the main DCO algorithm. It spins forever. */
|
||||
PioDCOWorker(p);
|
||||
PioDCOWorker2(p);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
#ifndef DEFINESWSPR_H
|
||||
#define DEFINESWSPR_H
|
||||
|
||||
//#define DEBUG
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUGPRINTF(x) StampPrintf(x);
|
||||
|
@ -78,6 +78,9 @@
|
|||
/* A macro for arithmetic right shifts, with casting of the argument. */
|
||||
#define iSAR(arg, rcount) (((int32_t)(arg)) >> (rcount))
|
||||
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
/* A macro of multiplication guarantees of doing so using 1 ASM command. */
|
||||
#define iMUL32ASM(a,b) __mul_instruction((int32_t)(a), (int32_t)(b))
|
||||
|
||||
|
|
59
main.c
59
main.c
|
@ -66,6 +66,13 @@
|
|||
#include <logutils.h>
|
||||
#include <protos.h>
|
||||
|
||||
#define CONFIG_GPS_SOLUTION_IS_MANDATORY NO
|
||||
#define CONFIG_GPS_RELY_ON_PAST_SOLUTION NO
|
||||
#define CONFIG_SCHEDULE_SKIP_SLOT_COUNT 5
|
||||
#define CONFIG_WSPR_DIAL_FREQUENCY 18106000UL //24926000UL // 28126000UL //7040000UL
|
||||
#define CONFIG_CALLSIGN "R2BDY"
|
||||
#define CONFIG_LOCATOR4 "KO85"
|
||||
|
||||
WSPRbeaconContext *pWSPR;
|
||||
|
||||
int main()
|
||||
|
@ -81,21 +88,20 @@ int main()
|
|||
StampPrintf("WSPR beacon init...");
|
||||
|
||||
WSPRbeaconContext *pWB = WSPRbeaconInit(
|
||||
"R2BDY", /* the Callsign. */
|
||||
"KO85", /* the QTH locator. */
|
||||
16, /* Tx power, dbm. */
|
||||
CONFIG_CALLSIGN,/* the Callsign. */
|
||||
CONFIG_LOCATOR4,/* the default QTH locator if GPS isn't used. */
|
||||
12, /* Tx power, dbm. */
|
||||
&DCO, /* the PioDCO object. */
|
||||
7040000UL, /* the dial frequency. */
|
||||
CONFIG_WSPR_DIAL_FREQUENCY,
|
||||
55UL, /* the carrier freq. shift relative to dial freq. */
|
||||
RFOUT_PIN /* 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 = YES; /* ?relying on GPS solution in the past. */
|
||||
pWB->_txSched._u8_tx_slot_skip = 2; /* 1 slot tx, 1 slot idle, etc. */
|
||||
pWB->_txSched._u8_tx_heating_pause_min = 1; /* Give 1 minute pre-heating ere first transmition. */
|
||||
pWB->_txSched._u8_tx_GPS_mandatory = CONFIG_GPS_SOLUTION_IS_MANDATORY;
|
||||
pWB->_txSched._u8_tx_GPS_past_time = CONFIG_GPS_RELY_ON_PAST_SOLUTION;
|
||||
pWB->_txSched._u8_tx_slot_skip = CONFIG_SCHEDULE_SKIP_SLOT_COUNT;
|
||||
|
||||
multicore_launch_core1(Core1Entry);
|
||||
StampPrintf("RF oscillator started.");
|
||||
|
@ -106,7 +112,42 @@ int main()
|
|||
int tick = 0;
|
||||
for(;;)
|
||||
{
|
||||
WSPRbeaconTxScheduler(pWB, YES);
|
||||
/*
|
||||
if(WSPRbeaconIsGPSsolutionActive(pWB))
|
||||
{
|
||||
const char *pgps_qth = WSPRbeaconGetLastQTHLocator(pWB);
|
||||
if(pgps_qth)
|
||||
{
|
||||
strncpy(pWB->_pu8_locator, pgps_qth, 4);
|
||||
pWB->_pu8_locator[5] = 0x00;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if(pWB->_txSched._u8_tx_GPS_mandatory)
|
||||
{
|
||||
WSPRbeaconTxScheduler(pWB, YES);
|
||||
}
|
||||
else
|
||||
{
|
||||
StampPrintf("Omitting GPS solution, start tx now.");
|
||||
PioDCOStart(pWB->_pTX->_p_oscillator);
|
||||
WSPRbeaconCreatePacket(pWB);
|
||||
sleep_ms(100);
|
||||
WSPRbeaconSendPacket(pWB);
|
||||
StampPrintf("The system will be halted when tx is completed.");
|
||||
for(;;)
|
||||
{
|
||||
if(!TxChannelPending(pWB->_pTX))
|
||||
{
|
||||
PioDCOStop(pWB->_pTX->_p_oscillator);
|
||||
StampPrintf("System halted.");
|
||||
}
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 1);
|
||||
sleep_ms(500);
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 0);
|
||||
}
|
||||
}
|
||||
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 1);
|
||||
sleep_ms(100);
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 01dbdefbaf1964246bfb66383ceeccea63aa6c60
|
||||
Subproject commit 9fbd9fa407423c89af9c4878340e71446c948f66
|
Ładowanie…
Reference in New Issue