kopia lustrzana https://github.com/RPiks/pico-WSPR-tx
WIP: It creates an example WSPR packet using 3rd-party libs.
rodzic
5b10603cd2
commit
6c5ad458ce
|
@ -1,53 +0,0 @@
|
||||||
#include "bitemitter.h"
|
|
||||||
|
|
||||||
BitEmitterContext *pBEC = NULL;
|
|
||||||
|
|
||||||
void __not_in_flash_func (BitEmitterISR)(void)
|
|
||||||
{
|
|
||||||
if(!pBEC)
|
|
||||||
{
|
|
||||||
pBEC->_tm_future_call += 500LL;
|
|
||||||
goto EXIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pBEC->_pf_modulator(pBEC->_bits_per_sample,
|
|
||||||
(pBEC->_u8byte_buffer[pBEC->_ix_output] >> pBEC->_ixbit_output)
|
|
||||||
& ((1 << pBEC->_bits_per_sample) - 1));
|
|
||||||
|
|
||||||
pBEC->_ixbit_output += pBEC->_bits_per_sample;
|
|
||||||
if(8 == pBEC->_ixbit_output)
|
|
||||||
{
|
|
||||||
++pBEC->_ix_output;
|
|
||||||
}
|
|
||||||
|
|
||||||
pBEC->_tm_future_call += pBEC->_bit_period_us;
|
|
||||||
|
|
||||||
EXIT:
|
|
||||||
hw_clear_bits(&timer_hw->intr, 1U<<pBEC->_timer_alarm_num);
|
|
||||||
timer_hw->alarm[pBEC->_timer_alarm_num] = (uint32_t)pBEC->_tm_future_call;
|
|
||||||
}
|
|
||||||
|
|
||||||
BitEmitterContext *BitEmitterInit(const uint32_t bit_period_us, uint8_t timer_alarm_num,
|
|
||||||
uint8_t bits_per_sample, void *pfmodulator)
|
|
||||||
{
|
|
||||||
assert_(pfmodulator);
|
|
||||||
assert_(bit_period_us > 10);
|
|
||||||
|
|
||||||
BitEmitterContext *p = calloc(1, sizeof(BitEmitterContext));
|
|
||||||
assert_(p);
|
|
||||||
|
|
||||||
p->_bit_period_us = bit_period_us;
|
|
||||||
p->_timer_alarm_num = timer_alarm_num;
|
|
||||||
p->_bits_per_sample = bits_per_sample;
|
|
||||||
p->_pf_modulator = pfmodulator;
|
|
||||||
|
|
||||||
hw_set_bits(&timer_hw->inte, 1U << p->_timer_alarm_num);
|
|
||||||
irq_set_exclusive_handler(TIMER_IRQ_0, BitEmitterISR);
|
|
||||||
irq_set_priority(TIMER_IRQ_0, 0x00);
|
|
||||||
irq_set_enabled(TIMER_IRQ_0, true);
|
|
||||||
|
|
||||||
p->_tm_future_call = timer_hw->timerawl + 500LL;
|
|
||||||
timer_hw->alarm[p->_timer_alarm_num] = (uint32_t)p->_tm_future_call;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
|
@ -31,20 +31,27 @@ add_executable(pico-wspr-tx)
|
||||||
|
|
||||||
target_sources(pico-wspr-tx PUBLIC
|
target_sources(pico-wspr-tx PUBLIC
|
||||||
${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/lib/assert.c
|
${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/lib/assert.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/BitEmitter/bitemitter.c
|
${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/WSPRbeacon.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/debug/logutils.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/main.c
|
${CMAKE_CURRENT_LIST_DIR}/main.c
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_set_program_name(pico-wspr-tx "pico-wspr-tx")
|
pico_set_program_name(pico-wspr-tx "pico-wspr-tx")
|
||||||
pico_set_program_version(pico-wspr-tx "0.1")
|
pico_set_program_version(pico-wspr-tx "0.1")
|
||||||
|
|
||||||
pico_enable_stdio_uart(pico-wspr-tx 1)
|
pico_enable_stdio_uart(pico-wspr-tx 0)
|
||||||
pico_enable_stdio_usb(pico-wspr-tx 0)
|
pico_enable_stdio_usb(pico-wspr-tx 1)
|
||||||
|
|
||||||
# Add the standard include files to the build
|
# Add the standard include files to the build
|
||||||
target_include_directories(pico-wspr-tx PRIVATE
|
target_include_directories(pico-wspr-tx PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco
|
${CMAKE_CURRENT_LIST_DIR}/pico-hf-oscillator/piodco
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/TxChannel
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/WSPRbeacon/thirdparty
|
||||||
${CMAKE_CURRENT_LIST_DIR}/..
|
${CMAKE_CURRENT_LIST_DIR}/..
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Roman Piksaykin [piksaykin@gmail.com], R2BDY
|
||||||
|
// https://www.qrz.com/db/r2bdy
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// TxChannel.c - Produces a time-accurate `bit` stream.
|
||||||
|
// Invokes a `modulator` function.
|
||||||
|
// DESCRIPTION
|
||||||
|
// Receives data asynchronously. Calls low level modulator function
|
||||||
|
// synchronously according to params.
|
||||||
|
//
|
||||||
|
// 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 "TxChannel.h"
|
||||||
|
|
||||||
|
TxChannelContext *pTX = NULL;
|
||||||
|
|
||||||
|
void __not_in_flash_func (TxChannelISR)(void)
|
||||||
|
{
|
||||||
|
if(!pTX)
|
||||||
|
{
|
||||||
|
pTX->_tm_future_call += 500LL;
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
pTX->_pf_modulator(FREQ_STEP_MILLIHERTZ, pTX->_u8byte_buffer[pTX->_ix_output++]);
|
||||||
|
pTX->_tm_future_call += pTX->_bit_period_us;
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
hw_clear_bits(&timer_hw->intr, 1U<<pTX->_timer_alarm_num);
|
||||||
|
timer_hw->alarm[pTX->_timer_alarm_num] = (uint32_t)pTX->_tm_future_call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Initializes a TxChannel context. Starts ISR.
|
||||||
|
/// @param bit_period_us Period of data bits, BPS speed = 1e6/bit_period_us.
|
||||||
|
/// @param timer_alarm_num Pico-specific hardware timer resource id.
|
||||||
|
/// @param pfmodulator Ptr to low level real-time modulator function.
|
||||||
|
/// @return the Context.
|
||||||
|
TxChannelContext *TxChannelInit(const uint32_t bit_period_us, uint8_t timer_alarm_num,
|
||||||
|
void *pfmodulator)
|
||||||
|
{
|
||||||
|
assert_(pfmodulator);
|
||||||
|
assert_(bit_period_us > 10);
|
||||||
|
|
||||||
|
TxChannelContext *p = calloc(1, sizeof(TxChannelContext));
|
||||||
|
assert_(p);
|
||||||
|
|
||||||
|
p->_bit_period_us = bit_period_us;
|
||||||
|
p->_timer_alarm_num = timer_alarm_num;
|
||||||
|
p->_pf_modulator = pfmodulator;
|
||||||
|
|
||||||
|
hw_set_bits(&timer_hw->inte, 1U << p->_timer_alarm_num);
|
||||||
|
irq_set_exclusive_handler(TIMER_IRQ_0, TxChannelISR);
|
||||||
|
irq_set_priority(TIMER_IRQ_0, 0x00);
|
||||||
|
irq_set_enabled(TIMER_IRQ_0, true);
|
||||||
|
|
||||||
|
p->_tm_future_call = timer_hw->timerawl + 500LL;
|
||||||
|
timer_hw->alarm[p->_timer_alarm_num] = (uint32_t)p->_tm_future_call;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
|
@ -6,22 +6,23 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// BitEmitter.h - Produces a time-accurate bit stream.
|
// TxChannel.h - Produces a time-accurate stream.
|
||||||
//
|
// Invokes a `modulator` function.
|
||||||
// DESCRIPTION
|
// DESCRIPTION
|
||||||
// Receives data asynchronously. Calls low level bit tx funcs synchronously
|
// Receives data asynchronously. Calls low level modulator function
|
||||||
// in time according to params.
|
// synchronously according to params.
|
||||||
//
|
//
|
||||||
// HOWTOSTART
|
// HOWTOSTART
|
||||||
// .
|
// -
|
||||||
//
|
//
|
||||||
// PLATFORM
|
// PLATFORM
|
||||||
// Raspberry Pi pico.
|
// Raspberry Pi pico.
|
||||||
//
|
//
|
||||||
// REVISION HISTORY
|
// REVISION HISTORY
|
||||||
//
|
// -
|
||||||
// Rev 0.1 18 Nov 2023
|
//
|
||||||
// Initial release.
|
// PROJECT PAGE
|
||||||
|
// https://github.com/RPiks/pico-WSPR-tx
|
||||||
//
|
//
|
||||||
// LICENCE
|
// LICENCE
|
||||||
// MIT License (http://www.opensource.org/licenses/mit-license.php)
|
// MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
@ -55,24 +56,25 @@
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "../pico-hf-oscillator/lib/assert.h"
|
#include "../pico-hf-oscillator/lib/assert.h"
|
||||||
|
|
||||||
|
#define FREQ_STEP_MILLIHERTZ 1465
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint64_t _tm_future_call;
|
uint64_t _tm_future_call;
|
||||||
uint32_t _bit_period_us;
|
uint32_t _bit_period_us;
|
||||||
uint8_t _bits_per_sample;
|
|
||||||
|
|
||||||
uint8_t _timer_alarm_num;
|
uint8_t _timer_alarm_num;
|
||||||
|
|
||||||
uint8_t _u8byte_buffer[256];
|
uint8_t _u8byte_buffer[256];
|
||||||
uint8_t _ix_input, _ix_output;
|
uint8_t _ix_input, _ix_output;
|
||||||
uint8_t _ixbit_output;
|
|
||||||
|
|
||||||
int (*_pf_modulator)(uint8_t bits_per_sample, uint8_t sample_val);
|
int (*_pf_modulator)(uint32_t frq_step, uint8_t shift_val);
|
||||||
|
int (*_pf_setPTT)(uint8_t bptt_state);
|
||||||
|
|
||||||
} BitEmitterContext;
|
} TxChannelContext;
|
||||||
|
|
||||||
BitEmitterContext *BitEmitterInit(const uint32_t bit_period_us, uint8_t timer_alarm_num,
|
TxChannelContext *TxChannelInit(const uint32_t bit_period_us, uint8_t timer_alarm_num,
|
||||||
uint8_t bits_per_sample, void *pfmodulator);
|
void *pfmodulator);
|
||||||
void __not_in_flash_func (BitEmitterISR)(void);
|
void __not_in_flash_func (TxChannelISR)(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "WSPRbeacon.h"
|
||||||
|
|
||||||
|
#include <WSPRutility.h>
|
||||||
|
|
||||||
|
WSPRbeaconContext *WSPRbeaconInit(const char *pcallsign, const char *pgridsquare, int txpow_dbm,
|
||||||
|
void *pfsk4modulator)
|
||||||
|
{
|
||||||
|
WSPRbeaconContext *p = calloc(1, sizeof(WSPRbeaconContext));
|
||||||
|
assert_(p);
|
||||||
|
|
||||||
|
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, pfsk4modulator);
|
||||||
|
assert_(p->_pTX);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSPRbeaconSetDialFreq(WSPRbeaconContext *pctx, uint32_t freq_hz)
|
||||||
|
{
|
||||||
|
assert_(pctx);
|
||||||
|
pctx->_u32_dialfreqhz = freq_hz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WSPRbeaconCreatePacket(WSPRbeaconContext *pctx)
|
||||||
|
{
|
||||||
|
assert_(pctx);
|
||||||
|
|
||||||
|
wspr_encode(pctx->_pu8_callsign, pctx->_pu8_locator, pctx->_u8_txpower, pctx->_pu8_outbuf);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef WSPRBEACON_H_
|
||||||
|
#define WSPRBEACON_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <TxChannel.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t _pu8_callsign[12];
|
||||||
|
uint8_t _pu8_locator[7];
|
||||||
|
uint8_t _u8_txpower;
|
||||||
|
|
||||||
|
uint8_t _pu8_outbuf[256];
|
||||||
|
|
||||||
|
TxChannelContext *_pTX;
|
||||||
|
uint32_t _u32_dialfreqhz;
|
||||||
|
|
||||||
|
} WSPRbeaconContext;
|
||||||
|
|
||||||
|
WSPRbeaconContext *WSPRbeaconInit(const char *pcallsign, const char *pgridsquare, int txpow_dbm,
|
||||||
|
void *pfsk4modulator);
|
||||||
|
void WSPRbeaconSetDialFreq(WSPRbeaconContext *pctx, uint32_t freq_hz);
|
||||||
|
int WSPRbeaconCreatePacket(WSPRbeaconContext *pctx);
|
||||||
|
int WSPRbeaconSendPacket(const WSPRbeaconContext *pctx);
|
||||||
|
|
||||||
|
//int WSPRbeaconFSK4mod(uint8_t bits_per_sample, uint8_t sample_val);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,517 @@
|
||||||
|
/*
|
||||||
|
* JTEncode.cpp - JT65/JT9/WSPR/FSQ encoder library for Arduino
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2021 Jason Milldrum <milldrum@gmail.com>
|
||||||
|
*
|
||||||
|
* Based on the algorithms presented in the WSJT software suite.
|
||||||
|
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
|
||||||
|
* process that helped me to understand all of this.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "WSPRutility.h"
|
||||||
|
|
||||||
|
static char callsign[12];
|
||||||
|
static char locator[7];
|
||||||
|
static int8_t power;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wspr_encode(const char * call, const char * loc, const uint8_t dbm, uint8_t * symbols)
|
||||||
|
*
|
||||||
|
* Takes a callsign, grid locator, and power level and returns a WSPR symbol
|
||||||
|
* table for a Type 1, 2, or 3 message.
|
||||||
|
*
|
||||||
|
* call - Callsign (12 characters maximum).
|
||||||
|
* loc - Maidenhead grid locator (6 characters maximum).
|
||||||
|
* dbm - Output power in dBm.
|
||||||
|
* symbols - Array of channel symbols to transmit returned by the method.
|
||||||
|
* Ensure that you pass a uint8_t array of at least size WSPR_SYMBOL_COUNT to the method.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void wspr_encode(const char * call, const char * loc, const int8_t dbm, uint8_t * symbols)
|
||||||
|
{
|
||||||
|
char call_[13];
|
||||||
|
char loc_[7];
|
||||||
|
uint8_t dbm_ = dbm;
|
||||||
|
strcpy(call_, call);
|
||||||
|
strcpy(loc_, loc);
|
||||||
|
|
||||||
|
// Ensure that the message text conforms to standards
|
||||||
|
// --------------------------------------------------
|
||||||
|
wspr_message_prep(call_, loc_, dbm_);
|
||||||
|
|
||||||
|
// Bit packing
|
||||||
|
// -----------
|
||||||
|
uint8_t c[11];
|
||||||
|
wspr_bit_packing(c);
|
||||||
|
|
||||||
|
// Convolutional Encoding
|
||||||
|
// ---------------------
|
||||||
|
uint8_t s[WSPR_SYMBOL_COUNT];
|
||||||
|
convolve(c, s, 11, WSPR_BIT_COUNT);
|
||||||
|
|
||||||
|
// Interleaving
|
||||||
|
// ------------
|
||||||
|
wspr_interleave(s);
|
||||||
|
|
||||||
|
// Merge with sync vector
|
||||||
|
// ----------------------
|
||||||
|
wspr_merge_sync_vector(s, symbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wspr_message_prep(char * call, char * loc, int8_t dbm)
|
||||||
|
{
|
||||||
|
// Callsign validation and padding
|
||||||
|
// -------------------------------
|
||||||
|
|
||||||
|
// Ensure that the only allowed characters are digits, uppercase letters, slash, and angle brackets
|
||||||
|
uint8_t i;
|
||||||
|
for(i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
if(call[i] != '/' && call[i] != '<' && call[i] != '>')
|
||||||
|
{
|
||||||
|
call[i] = toupper(call[i]);
|
||||||
|
if(!(isdigit(call[i]) || isupper(call[i])))
|
||||||
|
{
|
||||||
|
call[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call[12] = 0;
|
||||||
|
|
||||||
|
strncpy(callsign, call, 12);
|
||||||
|
|
||||||
|
// Grid locator validation
|
||||||
|
if(strlen(loc) == 4 || strlen(loc) == 6)
|
||||||
|
{
|
||||||
|
for(i = 0; i <= 1; i++)
|
||||||
|
{
|
||||||
|
loc[i] = toupper(loc[i]);
|
||||||
|
if((loc[i] < 'A' || loc[i] > 'R'))
|
||||||
|
{
|
||||||
|
strncpy(loc, "AA00AA", 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 2; i <= 3; i++)
|
||||||
|
{
|
||||||
|
if(!(isdigit(loc[i])))
|
||||||
|
{
|
||||||
|
strncpy(loc, "AA00AA", 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(loc, "AA00AA", 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen(loc) == 6)
|
||||||
|
{
|
||||||
|
for(i = 4; i <= 5; i++)
|
||||||
|
{
|
||||||
|
loc[i] = toupper(loc[i]);
|
||||||
|
if((loc[i] < 'A' || loc[i] > 'X'))
|
||||||
|
{
|
||||||
|
strncpy(loc, "AA00AA", 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(locator, loc, 7);
|
||||||
|
|
||||||
|
// Power level validation
|
||||||
|
// Only certain increments are allowed
|
||||||
|
if(dbm > 60)
|
||||||
|
{
|
||||||
|
dbm = 60;
|
||||||
|
}
|
||||||
|
//const uint8_t VALID_DBM_SIZE = 28;
|
||||||
|
const int8_t valid_dbm[VALID_DBM_SIZE] =
|
||||||
|
{-30, -27, -23, -20, -17, -13, -10, -7, -3,
|
||||||
|
0, 3, 7, 10, 13, 17, 20, 23, 27, 30, 33, 37, 40,
|
||||||
|
43, 47, 50, 53, 57, 60};
|
||||||
|
for(i = 0; i < VALID_DBM_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(dbm == valid_dbm[i])
|
||||||
|
{
|
||||||
|
power = dbm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we got this far, we have an invalid power level, so we'll round down
|
||||||
|
for(i = 1; i < VALID_DBM_SIZE; i++)
|
||||||
|
{
|
||||||
|
if(dbm < valid_dbm[i] && dbm >= valid_dbm[i - 1])
|
||||||
|
{
|
||||||
|
power = valid_dbm[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wspr_bit_packing(uint8_t * c)
|
||||||
|
{
|
||||||
|
uint32_t n, m;
|
||||||
|
|
||||||
|
// Determine if type 1, 2 or 3 message
|
||||||
|
char* slash_avail = strchr(callsign, (int)'/');
|
||||||
|
if(callsign[0] == '<')
|
||||||
|
{
|
||||||
|
// Type 3 message
|
||||||
|
char base_call[13];
|
||||||
|
memset(base_call, 0, 13);
|
||||||
|
uint32_t init_val = 146;
|
||||||
|
char* bracket_avail = strchr(callsign, (int)'>');
|
||||||
|
int call_len = bracket_avail - callsign - 1;
|
||||||
|
strncpy(base_call, callsign + 1, call_len);
|
||||||
|
uint32_t hash = nhash_(base_call, &call_len, &init_val);
|
||||||
|
hash &= 32767;
|
||||||
|
|
||||||
|
// Convert 6 char grid square to "callsign" format for transmission
|
||||||
|
// by putting the first character at the end
|
||||||
|
char temp_loc = locator[0];
|
||||||
|
locator[0] = locator[1];
|
||||||
|
locator[1] = locator[2];
|
||||||
|
locator[2] = locator[3];
|
||||||
|
locator[3] = locator[4];
|
||||||
|
locator[4] = locator[5];
|
||||||
|
locator[5] = temp_loc;
|
||||||
|
|
||||||
|
n = wspr_code(locator[0]);
|
||||||
|
n = n * 36 + wspr_code(locator[1]);
|
||||||
|
n = n * 10 + wspr_code(locator[2]);
|
||||||
|
n = n * 27 + (wspr_code(locator[3]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(locator[4]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(locator[5]) - 10);
|
||||||
|
|
||||||
|
m = (hash * 128) - (power + 1) + 64;
|
||||||
|
}
|
||||||
|
else if(slash_avail == (void *)0)
|
||||||
|
{
|
||||||
|
// Type 1 message
|
||||||
|
pad_callsign(callsign);
|
||||||
|
n = wspr_code(callsign[0]);
|
||||||
|
n = n * 36 + wspr_code(callsign[1]);
|
||||||
|
n = n * 10 + wspr_code(callsign[2]);
|
||||||
|
n = n * 27 + (wspr_code(callsign[3]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(callsign[4]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(callsign[5]) - 10);
|
||||||
|
|
||||||
|
m = ((179 - 10 * (locator[0] - 'A') - (locator[2] - '0')) * 180) +
|
||||||
|
(10 * (locator[1] - 'A')) + (locator[3] - '0');
|
||||||
|
m = (m * 128) + power + 64;
|
||||||
|
}
|
||||||
|
else if(slash_avail)
|
||||||
|
{
|
||||||
|
// Type 2 message
|
||||||
|
int slash_pos = slash_avail - callsign;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
// Determine prefix or suffix
|
||||||
|
if(callsign[slash_pos + 2] == ' ' || callsign[slash_pos + 2] == 0)
|
||||||
|
{
|
||||||
|
// Single character suffix
|
||||||
|
char base_call[7];
|
||||||
|
memset(base_call, 0, 7);
|
||||||
|
strncpy(base_call, callsign, slash_pos);
|
||||||
|
for(i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
base_call[i] = toupper(base_call[i]);
|
||||||
|
if(!(isdigit(base_call[i]) || isupper(base_call[i])))
|
||||||
|
{
|
||||||
|
base_call[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pad_callsign(base_call);
|
||||||
|
|
||||||
|
n = wspr_code(base_call[0]);
|
||||||
|
n = n * 36 + wspr_code(base_call[1]);
|
||||||
|
n = n * 10 + wspr_code(base_call[2]);
|
||||||
|
n = n * 27 + (wspr_code(base_call[3]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[4]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[5]) - 10);
|
||||||
|
|
||||||
|
char x = callsign[slash_pos + 1];
|
||||||
|
if(x >= 48 && x <= 57)
|
||||||
|
{
|
||||||
|
x -= 48;
|
||||||
|
}
|
||||||
|
else if(x >= 65 && x <= 90)
|
||||||
|
{
|
||||||
|
x -= 55;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = 38;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = 60000 - 32768 + x;
|
||||||
|
|
||||||
|
m = (m * 128) + power + 2 + 64;
|
||||||
|
}
|
||||||
|
else if(callsign[slash_pos + 3] == ' ' || callsign[slash_pos + 3] == 0)
|
||||||
|
{
|
||||||
|
// Two-digit numerical suffix
|
||||||
|
char base_call[7];
|
||||||
|
memset(base_call, 0, 7);
|
||||||
|
strncpy(base_call, callsign, slash_pos);
|
||||||
|
for(i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
base_call[i] = toupper(base_call[i]);
|
||||||
|
if(!(isdigit(base_call[i]) || isupper(base_call[i])))
|
||||||
|
{
|
||||||
|
base_call[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pad_callsign(base_call);
|
||||||
|
|
||||||
|
n = wspr_code(base_call[0]);
|
||||||
|
n = n * 36 + wspr_code(base_call[1]);
|
||||||
|
n = n * 10 + wspr_code(base_call[2]);
|
||||||
|
n = n * 27 + (wspr_code(base_call[3]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[4]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[5]) - 10);
|
||||||
|
|
||||||
|
// TODO: needs validation of digit
|
||||||
|
m = 10 * (callsign[slash_pos + 1] - 48) + callsign[slash_pos + 2] - 48;
|
||||||
|
m = 60000 + 26 + m;
|
||||||
|
m = (m * 128) + power + 2 + 64;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Prefix
|
||||||
|
char prefix[4];
|
||||||
|
char base_call[7];
|
||||||
|
memset(prefix, 0, 4);
|
||||||
|
memset(base_call, 0, 7);
|
||||||
|
strncpy(prefix, callsign, slash_pos);
|
||||||
|
strncpy(base_call, callsign + slash_pos + 1, 7);
|
||||||
|
|
||||||
|
if(prefix[2] == ' ' || prefix[2] == 0)
|
||||||
|
{
|
||||||
|
// Right align prefix
|
||||||
|
prefix[3] = 0;
|
||||||
|
prefix[2] = prefix[1];
|
||||||
|
prefix[1] = prefix[0];
|
||||||
|
prefix[0] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
base_call[i] = toupper(base_call[i]);
|
||||||
|
if(!(isdigit(base_call[i]) || isupper(base_call[i])))
|
||||||
|
{
|
||||||
|
base_call[i] = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pad_callsign(base_call);
|
||||||
|
|
||||||
|
n = wspr_code(base_call[0]);
|
||||||
|
n = n * 36 + wspr_code(base_call[1]);
|
||||||
|
n = n * 10 + wspr_code(base_call[2]);
|
||||||
|
n = n * 27 + (wspr_code(base_call[3]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[4]) - 10);
|
||||||
|
n = n * 27 + (wspr_code(base_call[5]) - 10);
|
||||||
|
|
||||||
|
m = 0;
|
||||||
|
for(uint8_t i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
m = 37 * m + wspr_code(prefix[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m >= 32768)
|
||||||
|
{
|
||||||
|
m -= 32768;
|
||||||
|
m = (m * 128) + power + 2 + 64;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m = (m * 128) + power + 1 + 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callsign is 28 bits, locator/power is 22 bits.
|
||||||
|
// A little less work to start with the least-significant bits
|
||||||
|
c[3] = (uint8_t)((n & 0x0f) << 4);
|
||||||
|
n = n >> 4;
|
||||||
|
c[2] = (uint8_t)(n & 0xff);
|
||||||
|
n = n >> 8;
|
||||||
|
c[1] = (uint8_t)(n & 0xff);
|
||||||
|
n = n >> 8;
|
||||||
|
c[0] = (uint8_t)(n & 0xff);
|
||||||
|
|
||||||
|
c[6] = (uint8_t)((m & 0x03) << 6);
|
||||||
|
m = m >> 2;
|
||||||
|
c[5] = (uint8_t)(m & 0xff);
|
||||||
|
m = m >> 8;
|
||||||
|
c[4] = (uint8_t)(m & 0xff);
|
||||||
|
m = m >> 8;
|
||||||
|
c[3] |= (uint8_t)(m & 0x0f);
|
||||||
|
c[7] = 0;
|
||||||
|
c[8] = 0;
|
||||||
|
c[9] = 0;
|
||||||
|
c[10] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void convolve(uint8_t * c, uint8_t * s, uint8_t message_size, uint8_t bit_size)
|
||||||
|
{
|
||||||
|
uint32_t reg_0 = 0;
|
||||||
|
uint32_t reg_1 = 0;
|
||||||
|
uint32_t reg_temp = 0;
|
||||||
|
uint8_t input_bit, parity_bit;
|
||||||
|
uint8_t bit_count = 0;
|
||||||
|
uint8_t i, j, k;
|
||||||
|
|
||||||
|
for(i = 0; i < message_size; i++)
|
||||||
|
{
|
||||||
|
for(j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
// Set input bit according the MSB of current element
|
||||||
|
input_bit = (((c[i] << j) & 0x80) == 0x80) ? 1 : 0;
|
||||||
|
|
||||||
|
// Shift both registers and put in the new input bit
|
||||||
|
reg_0 = reg_0 << 1;
|
||||||
|
reg_1 = reg_1 << 1;
|
||||||
|
reg_0 |= (uint32_t)input_bit;
|
||||||
|
reg_1 |= (uint32_t)input_bit;
|
||||||
|
|
||||||
|
// AND Register 0 with feedback taps, calculate parity
|
||||||
|
reg_temp = reg_0 & 0xf2d05351;
|
||||||
|
parity_bit = 0;
|
||||||
|
for(k = 0; k < 32; k++)
|
||||||
|
{
|
||||||
|
parity_bit = parity_bit ^ (reg_temp & 0x01);
|
||||||
|
reg_temp = reg_temp >> 1;
|
||||||
|
}
|
||||||
|
s[bit_count] = parity_bit;
|
||||||
|
bit_count++;
|
||||||
|
|
||||||
|
// AND Register 1 with feedback taps, calculate parity
|
||||||
|
reg_temp = reg_1 & 0xe4613c47;
|
||||||
|
parity_bit = 0;
|
||||||
|
for(k = 0; k < 32; k++)
|
||||||
|
{
|
||||||
|
parity_bit = parity_bit ^ (reg_temp & 0x01);
|
||||||
|
reg_temp = reg_temp >> 1;
|
||||||
|
}
|
||||||
|
s[bit_count] = parity_bit;
|
||||||
|
bit_count++;
|
||||||
|
if(bit_count >= bit_size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wspr_interleave(uint8_t * s)
|
||||||
|
{
|
||||||
|
uint8_t d[WSPR_BIT_COUNT];
|
||||||
|
uint8_t rev, index_temp, i, j, k;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
for(j = 0; j < 255; j++)
|
||||||
|
{
|
||||||
|
// Bit reverse the index
|
||||||
|
index_temp = j;
|
||||||
|
rev = 0;
|
||||||
|
|
||||||
|
for(k = 0; k < 8; k++)
|
||||||
|
{
|
||||||
|
if(index_temp & 0x01)
|
||||||
|
{
|
||||||
|
rev = rev | (1 << (7 - k));
|
||||||
|
}
|
||||||
|
index_temp = index_temp >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rev < WSPR_BIT_COUNT)
|
||||||
|
{
|
||||||
|
d[rev] = s[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i >= WSPR_BIT_COUNT)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(s, d, WSPR_BIT_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
const uint8_t sync_vector[WSPR_SYMBOL_COUNT] =
|
||||||
|
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
|
||||||
|
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0,
|
||||||
|
0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
|
||||||
|
0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0,
|
||||||
|
1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
|
||||||
|
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,
|
||||||
|
1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0};
|
||||||
|
|
||||||
|
for(i = 0; i < WSPR_SYMBOL_COUNT; i++)
|
||||||
|
{
|
||||||
|
symbols[i] = sync_vector[i] + (2 * g[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t wspr_code(char c)
|
||||||
|
{
|
||||||
|
// Validate the input then return the proper integer code.
|
||||||
|
// Change character to a space if the char is not allowed.
|
||||||
|
|
||||||
|
if(isdigit(c))
|
||||||
|
{
|
||||||
|
return (uint8_t)(c - 48);
|
||||||
|
}
|
||||||
|
else if(c == ' ')
|
||||||
|
{
|
||||||
|
return 36;
|
||||||
|
}
|
||||||
|
else if(c >= 'A' && c <= 'Z')
|
||||||
|
{
|
||||||
|
return (uint8_t)(c - 55);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 36;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pad_callsign(char * call)
|
||||||
|
{
|
||||||
|
// If only the 2nd character is a digit, then pad with a space.
|
||||||
|
// If this happens, then the callsign will be truncated if it is
|
||||||
|
// longer than 6 characters.
|
||||||
|
if(isdigit(call[1]) && isupper(call[2]))
|
||||||
|
{
|
||||||
|
// memmove(call + 1, call, 6);
|
||||||
|
call[5] = call[4];
|
||||||
|
call[4] = call[3];
|
||||||
|
call[3] = call[2];
|
||||||
|
call[2] = call[1];
|
||||||
|
call[1] = call[0];
|
||||||
|
call[0] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the 3rd charcter in the callsign must be a digit
|
||||||
|
// if(call[2] < '0' || call[2] > '9')
|
||||||
|
// {
|
||||||
|
// // return 1;
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* JTEncode.cpp - JT65/JT9/WSPR/FSQ encoder library for Arduino
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2021 Jason Milldrum <milldrum@gmail.com>
|
||||||
|
*
|
||||||
|
* Based on the algorithms presented in the WSJT software suite.
|
||||||
|
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
|
||||||
|
* process that helped me to understand all of this.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef WSPR_UTILITY_H_
|
||||||
|
#define WSPR_UTILITY_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "nhash.h"
|
||||||
|
|
||||||
|
#define WSPR_SYMBOL_COUNT 162
|
||||||
|
#define WSPR_BIT_COUNT 162
|
||||||
|
#define VALID_DBM_SIZE 28
|
||||||
|
|
||||||
|
void wspr_encode(const char * call, const char * loc, const int8_t dbm, uint8_t * symbols);
|
||||||
|
void wspr_message_prep(char * call, char * loc, int8_t dbm);
|
||||||
|
void wspr_bit_packing(uint8_t * c);
|
||||||
|
void convolve(uint8_t * c, uint8_t * s, uint8_t message_size, uint8_t bit_size);
|
||||||
|
void wspr_interleave(uint8_t * s);
|
||||||
|
void wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols);
|
||||||
|
uint8_t wspr_code(char c);
|
||||||
|
void pad_callsign(char * call);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,384 @@
|
||||||
|
/*
|
||||||
|
*-------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* This file is part of the WSPR application, Weak Signal Propagation Reporter
|
||||||
|
*
|
||||||
|
* File Name: nhash.c
|
||||||
|
* Description: Functions to produce 32-bit hashes for hash table lookup
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008-2014 Joseph Taylor, K1JT
|
||||||
|
* License: GPL-3
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||||
|
* Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Files: lookup3.c
|
||||||
|
* Copyright: Copyright (C) 2006 Bob Jenkins <bob_jenkins@burtleburtle.net>
|
||||||
|
* License: public-domain
|
||||||
|
* You may use this code any way you wish, private, educational, or commercial.
|
||||||
|
* It's free.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
These are functions for producing 32-bit hashes for hash table lookup.
|
||||||
|
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||||
|
are externally useful functions. Routines to test the hash are included
|
||||||
|
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||||
|
the public domain. It has no warranty.
|
||||||
|
|
||||||
|
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||||
|
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||||
|
little-endian machines. Intel and AMD are little-endian machines.
|
||||||
|
On second thought, you probably want hashlittle2(), which is identical to
|
||||||
|
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||||
|
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||||
|
|
||||||
|
If you want to find a hash of, say, exactly 7 integers, do
|
||||||
|
a = i1; b = i2; c = i3;
|
||||||
|
mix(a,b,c);
|
||||||
|
a += i4; b += i5; c += i6;
|
||||||
|
mix(a,b,c);
|
||||||
|
a += i7;
|
||||||
|
final(a,b,c);
|
||||||
|
then use c as the hash value. If you have a variable length array of
|
||||||
|
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||||
|
a character string), use hashlittle(). If you have several byte arrays, or
|
||||||
|
a mix of things, see the comments above hashlittle().
|
||||||
|
|
||||||
|
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||||
|
then mix those integers. This is fast (you can do a lot more thorough
|
||||||
|
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||||
|
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SELF_TEST 1
|
||||||
|
|
||||||
|
#include <stdio.h> /* defines printf for tests */
|
||||||
|
#include <time.h> /* defines time_t for timings in the test */
|
||||||
|
#ifdef Win32
|
||||||
|
#include "win_stdint.h" /* defines uint32_t etc */
|
||||||
|
#else
|
||||||
|
#include <stdint.h> /* defines uint32_t etc */
|
||||||
|
#endif
|
||||||
|
//#include <sys/param.h> /* attempt to define endianness */
|
||||||
|
//#ifdef linux
|
||||||
|
//# include <endian.h> /* attempt to define endianness */
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
#define HASH_LITTLE_ENDIAN 1
|
||||||
|
|
||||||
|
#define hashsize(n) ((uint32_t)1<<(n))
|
||||||
|
#define hashmask(n) (hashsize(n)-1)
|
||||||
|
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
mix -- mix 3 32-bit values reversibly.
|
||||||
|
|
||||||
|
This is reversible, so any information in (a,b,c) before mix() is
|
||||||
|
still in (a,b,c) after mix().
|
||||||
|
|
||||||
|
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||||
|
mix() in reverse, there are at least 32 bits of the output that
|
||||||
|
are sometimes the same for one pair and different for another pair.
|
||||||
|
This was tested for:
|
||||||
|
* pairs that differed by one bit, by two bits, in any combination
|
||||||
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||||
|
(a,b,c).
|
||||||
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||||
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||||
|
is commonly produced by subtraction) look like a single 1-bit
|
||||||
|
difference.
|
||||||
|
* the base values were pseudorandom, all zero but one bit set, or
|
||||||
|
all zero plus a counter that starts at zero.
|
||||||
|
|
||||||
|
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||||
|
satisfy this are
|
||||||
|
4 6 8 16 19 4
|
||||||
|
9 15 3 18 27 15
|
||||||
|
14 9 3 7 17 3
|
||||||
|
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||||
|
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||||
|
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||||
|
the operations, constants, and arrangements of the variables.
|
||||||
|
|
||||||
|
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||||
|
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||||
|
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||||
|
avalanche in c.
|
||||||
|
|
||||||
|
This allows some parallelism. Read-after-writes are good at doubling
|
||||||
|
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||||
|
direction as the goal of parallelism. I did what I could. Rotates
|
||||||
|
seem to cost as much as shifts on every machine I could lay my hands
|
||||||
|
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||||
|
rotates.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define mix(a,b,c) \
|
||||||
|
{ \
|
||||||
|
a -= c; a ^= rot(c, 4); c += b; \
|
||||||
|
b -= a; b ^= rot(a, 6); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 8); b += a; \
|
||||||
|
a -= c; a ^= rot(c,16); c += b; \
|
||||||
|
b -= a; b ^= rot(a,19); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 4); b += a; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||||
|
|
||||||
|
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||||
|
produce values of c that look totally different. This was tested for
|
||||||
|
* pairs that differed by one bit, by two bits, in any combination
|
||||||
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||||
|
(a,b,c).
|
||||||
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||||
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||||
|
is commonly produced by subtraction) look like a single 1-bit
|
||||||
|
difference.
|
||||||
|
* the base values were pseudorandom, all zero but one bit set, or
|
||||||
|
all zero plus a counter that starts at zero.
|
||||||
|
|
||||||
|
These constants passed:
|
||||||
|
14 11 25 16 4 14 24
|
||||||
|
12 14 25 16 4 14 24
|
||||||
|
and these came close:
|
||||||
|
4 8 15 26 3 22 24
|
||||||
|
10 8 15 26 3 22 24
|
||||||
|
11 8 15 26 3 22 24
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define final(a,b,c) \
|
||||||
|
{ \
|
||||||
|
c ^= b; c -= rot(b,14); \
|
||||||
|
a ^= c; a -= rot(c,11); \
|
||||||
|
b ^= a; b -= rot(a,25); \
|
||||||
|
c ^= b; c -= rot(b,16); \
|
||||||
|
a ^= c; a -= rot(c,4); \
|
||||||
|
b ^= a; b -= rot(a,14); \
|
||||||
|
c ^= b; c -= rot(b,24); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
hashlittle() -- hash a variable-length key into a 32-bit value
|
||||||
|
k : the key (the unaligned variable-length array of bytes)
|
||||||
|
length : the length of the key, counting by bytes
|
||||||
|
initval : can be any 4-byte value
|
||||||
|
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||||
|
the return value. Two keys differing by one or two bits will have
|
||||||
|
totally different hash values.
|
||||||
|
|
||||||
|
The best hash table sizes are powers of 2. There is no need to do
|
||||||
|
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||||
|
use a bitmask. For example, if you need only 10 bits, do
|
||||||
|
h = (h & hashmask(10));
|
||||||
|
In which case, the hash table should have hashsize(10) elements.
|
||||||
|
|
||||||
|
If you are hashing n strings (uint8_t **)k, do it like this:
|
||||||
|
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
|
||||||
|
|
||||||
|
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||||
|
code any way you wish, private, educational, or commercial. It's free.
|
||||||
|
|
||||||
|
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||||
|
acceptable. Do NOT use for cryptographic purposes.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
//uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
||||||
|
#ifdef STDCALL
|
||||||
|
uint32_t __stdcall NHASH( const void *key, size_t *length0, uint32_t *initval0)
|
||||||
|
#else
|
||||||
|
uint32_t nhash_( const void *key, int *length0, uint32_t *initval0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint32_t a,b,c; /* internal state */
|
||||||
|
size_t length;
|
||||||
|
uint32_t initval;
|
||||||
|
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||||
|
|
||||||
|
length=*length0;
|
||||||
|
initval=*initval0;
|
||||||
|
|
||||||
|
/* Set up the internal state */
|
||||||
|
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
|
||||||
|
|
||||||
|
u.ptr = key;
|
||||||
|
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||||
|
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||||
|
const uint8_t *k8;
|
||||||
|
|
||||||
|
k8=0; //Silence compiler warning
|
||||||
|
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0];
|
||||||
|
b += k[1];
|
||||||
|
c += k[2];
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- handle the last (probably partial) block */
|
||||||
|
/*
|
||||||
|
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||||
|
* then masks off the part it's not allowed to read. Because the
|
||||||
|
* string is aligned, the masked-off tail is in the same word as the
|
||||||
|
* rest of the string. Every machine with memory protection I've seen
|
||||||
|
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||||
|
* still catch it and complain. The masking trick does make the hash
|
||||||
|
* noticably faster for short strings (like English words).
|
||||||
|
*/
|
||||||
|
#ifndef VALGRIND
|
||||||
|
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 8 : b+=k[1]; a+=k[0]; break;
|
||||||
|
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
|
||||||
|
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
|
||||||
|
case 5 : b+=k[1]&0xff; a+=k[0]; break;
|
||||||
|
case 4 : a+=k[0]; break;
|
||||||
|
case 3 : a+=k[0]&0xffffff; break;
|
||||||
|
case 2 : a+=k[0]&0xffff; break;
|
||||||
|
case 1 : a+=k[0]&0xff; break;
|
||||||
|
case 0 : return c; /* zero length strings require no mixing */
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* make valgrind happy */
|
||||||
|
|
||||||
|
k8 = (const uint8_t *)k;
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||||
|
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||||
|
case 9 : c+=k8[8]; /* fall through */
|
||||||
|
case 8 : b+=k[1]; a+=k[0]; break;
|
||||||
|
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||||
|
case 5 : b+=k8[4]; /* fall through */
|
||||||
|
case 4 : a+=k[0]; break;
|
||||||
|
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||||
|
case 1 : a+=k8[0]; break;
|
||||||
|
case 0 : return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !valgrind */
|
||||||
|
|
||||||
|
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||||
|
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||||
|
const uint8_t *k8;
|
||||||
|
|
||||||
|
/*--------------- all but last block: aligned reads and different mixing */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0] + (((uint32_t)k[1])<<16);
|
||||||
|
b += k[2] + (((uint32_t)k[3])<<16);
|
||||||
|
c += k[4] + (((uint32_t)k[5])<<16);
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- handle the last (probably partial) block */
|
||||||
|
k8 = (const uint8_t *)k;
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||||
|
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||||
|
case 10: c+=k[4];
|
||||||
|
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 9 : c+=k8[8]; /* fall through */
|
||||||
|
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=k[2];
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 5 : b+=k8[4]; /* fall through */
|
||||||
|
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=k[0];
|
||||||
|
break;
|
||||||
|
case 1 : a+=k8[0];
|
||||||
|
break;
|
||||||
|
case 0 : return c; /* zero length requires no mixing */
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { /* need to read the key one byte at a time */
|
||||||
|
const uint8_t *k = (const uint8_t *)key;
|
||||||
|
|
||||||
|
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0];
|
||||||
|
a += ((uint32_t)k[1])<<8;
|
||||||
|
a += ((uint32_t)k[2])<<16;
|
||||||
|
a += ((uint32_t)k[3])<<24;
|
||||||
|
b += k[4];
|
||||||
|
b += ((uint32_t)k[5])<<8;
|
||||||
|
b += ((uint32_t)k[6])<<16;
|
||||||
|
b += ((uint32_t)k[7])<<24;
|
||||||
|
c += k[8];
|
||||||
|
c += ((uint32_t)k[9])<<8;
|
||||||
|
c += ((uint32_t)k[10])<<16;
|
||||||
|
c += ((uint32_t)k[11])<<24;
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||||
|
switch(length) /* all the case statements fall through */
|
||||||
|
{
|
||||||
|
case 12: c+=((uint32_t)k[11])<<24; /* fall through */
|
||||||
|
case 11: c+=((uint32_t)k[10])<<16; /* fall through */
|
||||||
|
case 10: c+=((uint32_t)k[9])<<8; /* fall through */
|
||||||
|
case 9 : c+=k[8]; /* fall through */
|
||||||
|
case 8 : b+=((uint32_t)k[7])<<24; /* fall through */
|
||||||
|
case 7 : b+=((uint32_t)k[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=((uint32_t)k[5])<<8; /* fall through */
|
||||||
|
case 5 : b+=k[4]; /* fall through */
|
||||||
|
case 4 : a+=((uint32_t)k[3])<<24; /* fall through */
|
||||||
|
case 3 : a+=((uint32_t)k[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=((uint32_t)k[1])<<8; /* fall through */
|
||||||
|
case 1 : a+=k[0];
|
||||||
|
break;
|
||||||
|
case 0 : return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final(a,b,c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//uint32_t __stdcall NHASH(const void *key, size_t length, uint32_t initval)
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NHASH_H_
|
||||||
|
#define NHASH_H_
|
||||||
|
|
||||||
|
uint32_t nhash_( const void *, int *, uint32_t *);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,85 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Roman Piksaykin [piksaykin@gmail.com], R2BDY
|
||||||
|
// https://www.qrz.com/db/r2bdy
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// BitEmitter.h - Produces a time-accurate bit stream. Invokes a modulator
|
||||||
|
// Invokes a `modulator` function.
|
||||||
|
// DESCRIPTION
|
||||||
|
// Receives data asynchronously. Calls low level modulator function
|
||||||
|
// synchronously according to params.
|
||||||
|
//
|
||||||
|
// HOWTOSTART
|
||||||
|
// -
|
||||||
|
//
|
||||||
|
// PLATFORM
|
||||||
|
// Raspberry Pi pico.
|
||||||
|
//
|
||||||
|
// REVISION HISTORY
|
||||||
|
// -
|
||||||
|
//
|
||||||
|
// 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
|
12
defines.h
12
defines.h
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef DEFINESWSPR_H
|
||||||
|
#define DEFINESWSPR_H
|
||||||
|
|
||||||
|
#define DEBUG
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define DEBUGPRINTF(x) StampPrintf(x);
|
||||||
|
#else
|
||||||
|
#define DEBUGPRINTF(x) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Plik binarny nie jest wyświetlany.
55
main.c
55
main.c
|
@ -47,16 +47,67 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
#include "pico/multicore.h"
|
#include "pico/multicore.h"
|
||||||
#include "pico-hf-oscillator/defines.h"
|
|
||||||
#include "pico-hf-oscillator/piodco/piodco.h"
|
|
||||||
#include "pico-hf-oscillator/lib/assert.h"
|
#include "pico-hf-oscillator/lib/assert.h"
|
||||||
|
#include "pico-hf-oscillator/defines.h"
|
||||||
|
#include <piodco.h>
|
||||||
|
|
||||||
|
#include <WSPRbeacon.h>
|
||||||
|
|
||||||
|
#include "debug/logutils.h"
|
||||||
|
|
||||||
|
int FSK4mod(uint32_t frq_step_millihz, uint8_t shift_index);
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
DEBUGPRINTF("\n");
|
||||||
|
sleep_ms(1000);
|
||||||
|
DEBUGPRINTF("Pico-WSPR-tx start.");
|
||||||
|
|
||||||
|
DEBUGPRINTF("WSPR beacon init...");
|
||||||
|
WSPRbeaconContext *pWB = WSPRbeaconInit("R2BDY", "KO85", 6, FSK4mod);
|
||||||
|
DEBUGPRINTF("OK");
|
||||||
|
|
||||||
|
DEBUGPRINTF("Create packet...");
|
||||||
|
WSPRbeaconCreatePacket(pWB);
|
||||||
|
DEBUGPRINTF("OK");
|
||||||
|
|
||||||
|
sleep_ms(100);
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
const int j = i + row * 16;
|
||||||
|
printf("%X ", pWB->_pu8_outbuf[j]);
|
||||||
|
if(161 == j)
|
||||||
|
{
|
||||||
|
row = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
if(-1 == row)
|
||||||
|
break;
|
||||||
|
++row;
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
DEBUGPRINTF("tick.");
|
||||||
|
sleep_ms(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FSK4mod(uint32_t frq_step_millihz, uint8_t shift_index)
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit 37dbc57481edd722a719de7c8ac4c83b1843cc65
|
Subproject commit 494ca2fb2f922366921c3deea0c2b03432c9b867
|
Ładowanie…
Reference in New Issue