kopia lustrzana https://github.com/DL7AD/pecanpico10
Added ?APRSP, ?GPIO and ?RESET queries
Reorganized APRS messaging Updated APRS demodulation librarypull/1/head
rodzic
192a930992
commit
974f3a74d7
|
@ -364,7 +364,7 @@ sysinterval_t track_cycle_time = TIME_S2I(600); // Tracking cycle (all periphera
|
|||
bool keep_cam_switched_on = false; // Keep camera switched on and initialized, this makes image capturing faster but takes a lot of power over long time
|
||||
uint16_t gps_on_vbat = 5000; // Battery voltage threshold at which GPS is switched on
|
||||
uint16_t gps_off_vbat = 5000; // Battery voltage threshold at which GPS is switched off
|
||||
uint16_t gps_onper_vbat = 1000; // Battery voltage threshold at which GPS is kept switched on all time. This value must be larger
|
||||
uint16_t gps_onper_vbat = 5000; // Battery voltage threshold at which GPS is kept switched on all time. This value must be larger
|
||||
// than gps_on_vbat and gps_off_vbat otherwise this value has no effect. Value 0 disables this feature
|
||||
|
||||
|
||||
|
@ -379,72 +379,94 @@ void start_user_modules(void)
|
|||
|
||||
/* -------------------------------------------------- POSITION TRANSMISSION -------------------------------------------------- */
|
||||
|
||||
// Module POSITION, APRS 2m AFSK
|
||||
config[0].power = 127; // Transmission Power
|
||||
config[0].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[0].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[0].trigger.type = TRIG_NEW_POINT; // Transmit when tracking manager samples new tracking point
|
||||
chsnprintf(config[0].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
|
||||
config[0].aprs_conf.ssid = 12; // APRS SSID
|
||||
config[0].aprs_conf.symbol = SYM_DIGIPEATER; // APRS Symbol
|
||||
chsnprintf(config[0].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
config[0].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
|
||||
chsnprintf(config[0].aprs_conf.tel_comment, 64, "http://dl7ad.duckdns.org/DL7AD-12");// Telemetry comment
|
||||
// Primary Position Thread configuration
|
||||
config[0].power = 127; // Transmission Power
|
||||
config[0].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[0].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[0].trigger.type = TRIG_NEW_POINT; // Transmit when tracking manager samples new tracking point
|
||||
chsnprintf(config[0].aprs_conf.callsign, 10, "DL7AD-12"); // APRS Callsign
|
||||
config[0].aprs_conf.symbol = SYM_DIGIPEATER; // APRS Symbol
|
||||
chsnprintf(config[0].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
config[0].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
|
||||
chsnprintf(config[0].aprs_conf.tel_comment, 64,
|
||||
"http://dl7ad.duckdns.org/DL7AD-12"); // Telemetry comment
|
||||
start_position_thread(&config[0]);
|
||||
|
||||
// Secondary Position Thread configuration
|
||||
config[1].power = 127; // Transmission Power
|
||||
config[1].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[1].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[1].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[1].trigger.type = TRIG_NEW_POINT; // Transmit when tracking manager samples new tracking point
|
||||
chsnprintf(config[1].aprs_conf.callsign, 10, "DL7AD-12"); // APRS Callsign
|
||||
config[1].aprs_conf.symbol = SYM_DIGIPEATER; // APRS Symbol
|
||||
chsnprintf(config[1].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
config[1].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
config[1].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
|
||||
chsnprintf(config[1].aprs_conf.tel_comment, 64,
|
||||
"http://dl7ad.duckdns.org/DL7AD-12"); // Telemetry comment
|
||||
//start_position_thread(&config[1]);
|
||||
|
||||
// Digipeater/Receiver Thread configuration
|
||||
config[2].power = 127; // Transmission Power
|
||||
config[2].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[2].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[2].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
chsnprintf(config[2].aprs_conf.callsign, 10, "DL7AD-12"); // APRS Callsign
|
||||
chsnprintf(config[2].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
config[2].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
//start_position_thread(&config[2]);
|
||||
|
||||
|
||||
/* ---------------------------------------------------- IMAGE TRANSMISSION --------------------------------------------------- */
|
||||
|
||||
// Module IMAGE, APRS 2m AFSK low-duty cycle
|
||||
config[3].power = 127; // Transmission Power
|
||||
config[3].modulation = MOD_AFSK; // Protocol APRS/SSDV (AFSK)
|
||||
config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz
|
||||
config[3].packet_spacing = 10000; // Packet spacing in ms
|
||||
config[3].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
|
||||
chsnprintf(config[3].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
|
||||
config[3].aprs_conf.ssid = 14; // APRS SSID
|
||||
config[3].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[3].ssdv_conf.res = RES_QVGA; // Resolution QVGA
|
||||
//config[3].redundantTx = true; // Redundant transmission (transmit packets twice)
|
||||
config[3].ssdv_conf.quality = 4; // Image quality
|
||||
// Primary Image Thread configuration
|
||||
config[3].power = 127; // Transmission Power
|
||||
config[3].modulation = MOD_AFSK; // Protocol APRS/SSDV (AFSK)
|
||||
config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz
|
||||
config[3].packet_spacing = 10000; // Packet spacing in ms
|
||||
config[3].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
|
||||
chsnprintf(config[3].aprs_conf.callsign, 10, "DL7AD-14"); // APRS Callsign
|
||||
config[3].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[3].ssdv_conf.res = RES_QVGA; // Resolution QVGA
|
||||
//config[3].redundantTx = true; // Redundant transmission (transmit packets twice)
|
||||
config[3].ssdv_conf.quality = 4; // Image quality
|
||||
//start_image_thread(&config[3]);
|
||||
|
||||
// Module IMAGE, APRS 2m 2FSK
|
||||
config[4].power = 127; // Transmission Power
|
||||
config[4].modulation = MOD_2FSK; // Protocol APRS/SSDV (2FSK)
|
||||
config[4].fsk_conf.speed = 9600; // 2FSK Speed
|
||||
config[4].frequency.type = FREQ_STATIC; // Static frequency allocation
|
||||
config[4].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
|
||||
config[4].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
|
||||
chsnprintf(config[4].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
|
||||
config[4].aprs_conf.ssid = 14; // APRS SSID
|
||||
config[4].aprs_conf.preamble = 100; // APRS Preamble (100ms)
|
||||
config[4].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[4].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[4].ssdv_conf.res = RES_VGA; // Resolution VGA
|
||||
config[4].ssdv_conf.quality = 4; // Image quality
|
||||
// Secondary Image Thread configuration
|
||||
config[4].power = 127; // Transmission Power
|
||||
config[4].modulation = MOD_2FSK; // Protocol APRS/SSDV (2FSK)
|
||||
config[4].fsk_conf.speed = 9600; // 2FSK Speed
|
||||
config[4].frequency.type = FREQ_STATIC; // Static frequency allocation
|
||||
config[4].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
|
||||
config[4].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
|
||||
chsnprintf(config[4].aprs_conf.callsign, 10, "DL7AD-14"); // APRS Callsign
|
||||
config[4].aprs_conf.preamble = 100; // APRS Preamble (100ms)
|
||||
config[4].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
|
||||
config[4].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
|
||||
config[4].ssdv_conf.res = RES_VGA; // Resolution VGA
|
||||
config[4].ssdv_conf.quality = 4; // Image quality
|
||||
//start_image_thread(&config[4]);
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------- LOG TRANSMISSION ---------------------------------------------------- */
|
||||
|
||||
// Module LOG, APRS 2m AFSK
|
||||
config[6].power = 127; // Transmission Power
|
||||
config[6].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[6].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[6].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[6].init_delay = 5000; // Module startup delay (5 seconds)
|
||||
config[6].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 180 seconds)
|
||||
config[6].trigger.timeout = 60; // Timeout 60 sec
|
||||
chsnprintf(config[6].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
|
||||
config[6].aprs_conf.ssid = 15; // APRS SSID
|
||||
config[6].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
// Log Thread configuration
|
||||
config[6].power = 127; // Transmission Power
|
||||
config[6].modulation = MOD_AFSK; // Protocol APRS (AFSK)
|
||||
config[6].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
config[6].frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
config[6].init_delay = 5000; // Module startup delay (5 seconds)
|
||||
config[6].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 180 seconds)
|
||||
config[6].trigger.timeout = 60; // Timeout 60 sec
|
||||
chsnprintf(config[6].aprs_conf.callsign, 10, "DL7AD-12"); // APRS Callsign
|
||||
config[6].aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
//start_logging_thread(&config[6]);
|
||||
}
|
||||
|
||||
|
|
|
@ -150,16 +150,6 @@ void printConfig(BaseSequentialStream *chp, int argc, char *argv[])
|
|||
|
||||
void send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[])
|
||||
{
|
||||
aprs_conf_t aprs_conf;
|
||||
chsnprintf(aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
|
||||
aprs_conf.ssid = 14; // APRS SSID
|
||||
chsnprintf(aprs_conf.path, 16, "WIDE1-1"); // APRS Path
|
||||
aprs_conf.preamble = 200; // APRS Preamble (200ms)
|
||||
|
||||
freq_conf_t frequency;
|
||||
frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
|
||||
frequency.hz = 144800000; // Default frequency 144.800 MHz
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
chprintf(chp, "Argument missing!\r\n");
|
||||
|
@ -170,8 +160,8 @@ void send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[])
|
|||
chprintf(chp, "Destination: %s\r\n", argv[0]);
|
||||
chprintf(chp, "Message: %s\r\n", argv[1]);
|
||||
|
||||
packet_t packet = aprs_encode_message(&aprs_conf, argv[0], argv[1], false);
|
||||
transmitOnRadio(packet, &frequency, 127, MOD_AFSK);
|
||||
packet_t packet = aprs_encode_message(&(config[2].aprs_conf), argv[0], argv[1], false);
|
||||
transmitOnRadio(packet, &(config[2].frequency), 127, MOD_AFSK);
|
||||
|
||||
chprintf(chp, "Message sent!\r\n");
|
||||
}
|
||||
|
@ -182,6 +172,6 @@ void test_rx(BaseSequentialStream *chp, int argc, char *argv[])
|
|||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
startReceiver();
|
||||
receiveAFSK(config[2].frequency.hz, 0x2F);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,38 +10,39 @@
|
|||
#include "si446x.h"
|
||||
#include "fcs_calc.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// Thread
|
||||
static thread_t* feeder_thd = NULL;
|
||||
static THD_WORKING_AREA(si_fifo_feeder_wa, 4096);
|
||||
#include "pktconf.h"
|
||||
#include "aprs.h"
|
||||
|
||||
// Mutex
|
||||
static mutex_t radio_mtx; // Radio mutex
|
||||
static bool nextTransmissionWaiting; // Flag that informs the feeder thread to keep the radio switched on
|
||||
static bool radio_mtx_init = false;
|
||||
|
||||
// Modulation and buffer
|
||||
// Feeder thread variables
|
||||
static thread_t* feeder_thd = NULL;
|
||||
static THD_WORKING_AREA(si_fifo_feeder_wa, 4096);
|
||||
static packet_t radio_packet;
|
||||
static uint32_t radio_freq;
|
||||
static uint8_t radio_pwr;
|
||||
|
||||
// Si446x variables
|
||||
static uint32_t outdiv;
|
||||
static int16_t lastTemp = 0x7FFF;
|
||||
static bool radioInitialized;
|
||||
|
||||
// Receiver thread variables
|
||||
static thread_t* si446x_rx_thd = NULL;
|
||||
static THD_WORKING_AREA(si446x_rx_wa, 8192);
|
||||
static packet_rx_t *packetHandler;
|
||||
|
||||
static uint32_t rx_frequency;
|
||||
static uint8_t rx_rssi;
|
||||
static mod_t rx_mod;
|
||||
|
||||
|
||||
|
||||
static int16_t Si446x_getTemperature(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* =================================================================== SPI communication ==================================================================== */
|
||||
|
||||
static const SPIConfig ls_spicfg = {
|
||||
|
@ -673,7 +674,6 @@ static bool Si446x_transmit(uint32_t frequency, int8_t power, uint16_t size, uin
|
|||
Si446x_setReadyState();
|
||||
}
|
||||
|
||||
TRACE_INFO("SI > Wait for CCA to drop");
|
||||
Si446x_setProperty8(Si446x_MODEM_RSSI_THRESH, rssi);
|
||||
Si446x_setFrequency(frequency); // Set frequency
|
||||
Si446x_setPowerLevel(power); // Set power level
|
||||
|
@ -681,9 +681,11 @@ static bool Si446x_transmit(uint32_t frequency, int8_t power, uint16_t size, uin
|
|||
|
||||
// Wait until nobody is transmitting (until timeout)
|
||||
sysinterval_t t0 = chVTGetSystemTime();
|
||||
do {
|
||||
chThdSleep(TIME_MS2I(5));
|
||||
} while((Si446x_getState() != Si446x_STATE_RX || Si446x_getCCA()) && chVTGetSystemTime()-t0 < sql_timeout);
|
||||
if(Si446x_getState() != Si446x_STATE_RX || Si446x_getCCA()) {
|
||||
TRACE_INFO("SI > Wait for CCA to drop");
|
||||
while((Si446x_getState() != Si446x_STATE_RX || Si446x_getCCA()) && chVTGetSystemTime()-t0 < sql_timeout)
|
||||
chThdSleep(TIME_MS2I(5));
|
||||
}
|
||||
|
||||
// Transmit
|
||||
TRACE_INFO("SI > Tune Si446x (TX)");
|
||||
|
@ -732,6 +734,7 @@ static bool Si446x_receive(uint32_t frequency, uint8_t rssi, mod_t mod)
|
|||
static bool Si4464_restoreRX(void)
|
||||
{
|
||||
TRACE_INFO("SI > Restore RX");
|
||||
pktStartDataReception(packetHandler); // Start packet handler again
|
||||
return Si446x_receive(rx_frequency, rx_rssi, rx_mod);
|
||||
}
|
||||
|
||||
|
@ -784,7 +787,7 @@ void lockRadioByCamera(void)
|
|||
chThdWait(feeder_thd);
|
||||
}
|
||||
|
||||
/* ========================================================================== AFSK ========================================================================== */
|
||||
/* ==================================================================== AFSK Transmitter ==================================================================== */
|
||||
|
||||
#define PLAYBACK_RATE 13200
|
||||
#define BAUD_RATE 1200 /* APRS AFSK baudrate */
|
||||
|
@ -975,6 +978,10 @@ THD_FUNCTION(si_fifo_feeder_afsk, arg)
|
|||
void sendAFSK(packet_t packet, uint32_t freq, uint8_t pwr) {
|
||||
lockRadio();
|
||||
|
||||
// Stop packet handler (if started)
|
||||
if(packetHandler)
|
||||
pktStopDataReception(packetHandler);
|
||||
|
||||
// Initialize radio
|
||||
if(!radioInitialized)
|
||||
Si446x_init();
|
||||
|
@ -995,6 +1002,192 @@ void sendAFSK(packet_t packet, uint32_t freq, uint8_t pwr) {
|
|||
unlockRadio();
|
||||
}
|
||||
|
||||
/* ===================================================================== AFSK Receiver ====================================================================== */
|
||||
|
||||
#define LINE_LENGTH 40U
|
||||
|
||||
THD_FUNCTION(si_receiver, arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
chRegSetThreadName("radio_receiver");
|
||||
|
||||
/* Buffer and size params for serial terminal output. */
|
||||
char serial_buf[1024];
|
||||
int serial_out;
|
||||
|
||||
/*
|
||||
* Setup the parameters for the AFSK decoder thread.
|
||||
* TODO: Radio configuration to be implemented in pktOpenReceiveChannel().
|
||||
*/
|
||||
|
||||
radio_config_t afsk_radio = { PKT_RADIO_1 };
|
||||
char frameCounter = 'A';
|
||||
|
||||
/* set packet instance assignment(s). */
|
||||
pktInitReceiveChannels();
|
||||
|
||||
packetHandler = pktOpenReceiveChannel(DECODE_AFSK, &afsk_radio);
|
||||
chDbgAssert(packetHandler != NULL, "invalid packet type");
|
||||
|
||||
thread_t *the_decoder = ((AFSKDemodDriver *)packetHandler->link_controller)->decoder_thd;
|
||||
chDbgAssert(the_decoder != NULL, "no decoder assigned");
|
||||
|
||||
event_source_t *events = pktGetEventSource(packetHandler);
|
||||
|
||||
/* Start the decoder. */
|
||||
msg_t pstart = pktStartDataReception(packetHandler);
|
||||
|
||||
TRACE_DEBUG("RX > Starting decoder: start status %i, event source @ %x", pstart, events);
|
||||
|
||||
/* Main loop. */
|
||||
while (true) {
|
||||
frameCounter = ((frameCounter+1-'A') % 26) + 'A';
|
||||
|
||||
pkt_data_fifo_t *myPktFIFO = pktReceiveDataBufferTimeout(packetHandler, TIME_MS2I(1000));
|
||||
if(myPktFIFO == NULL) {
|
||||
continue;
|
||||
}
|
||||
/* Packet buffer sent via FIFO. */
|
||||
ax25char_t *frame_buffer = myPktFIFO->buffer;
|
||||
uint16_t frame_size = myPktFIFO->packet_size;
|
||||
eventmask_t the_events;
|
||||
packetHandler->frame_count++;
|
||||
if(pktIsBufferValidAX25Frame(myPktFIFO) == MSG_OK) {
|
||||
the_events = EVT_DIAG_OUT_END | EVT_PKT_OUT_END;
|
||||
uint16_t actualCRC = frame_buffer[frame_size - 2] | (frame_buffer[frame_size - 1] << 8);
|
||||
uint16_t computeCRC = calc_crc16(frame_buffer, 0, frame_size - 2);
|
||||
uint16_t magicCRC = calc_crc16(frame_buffer, 0, frame_size);
|
||||
if(magicCRC == CRC_INCLUSIVE_CONSTANT)
|
||||
packetHandler->valid_count++;
|
||||
float32_t good = (float32_t)packetHandler->valid_count / (float32_t)packetHandler->packet_count;
|
||||
/* Write out the buffer data.
|
||||
* TODO: Have a define to put diagnostic data into AX25 buffer object.
|
||||
*/
|
||||
TRACE_DEBUG(
|
||||
"RX %c > AFSK capture: status 0x%08x"
|
||||
", packet count %u frame count %u valid frames %u (%.2f%%) bytes %u"
|
||||
", CRCr %04x, CRCc %04x (%s), CRCm %04x",
|
||||
frameCounter,
|
||||
myPktFIFO->status,
|
||||
packetHandler->packet_count,
|
||||
packetHandler->frame_count,
|
||||
packetHandler->valid_count,
|
||||
(good * 100),
|
||||
frame_size,
|
||||
actualCRC,
|
||||
computeCRC,
|
||||
(actualCRC == computeCRC ? "good" : "bad"),
|
||||
magicCRC
|
||||
);
|
||||
|
||||
uint16_t bufpos;
|
||||
uint16_t bufpos_a = 0;
|
||||
serial_out = 0;
|
||||
/* Write out a buffer line as hex first. */
|
||||
for(bufpos = 0; bufpos < frame_size; bufpos++) {
|
||||
if((bufpos + 1) % LINE_LENGTH == 0) {
|
||||
serial_out += chsnprintf(&serial_buf[serial_out], sizeof(serial_buf)-serial_out, "%02x", frame_buffer[bufpos]);
|
||||
TRACE_DEBUG("RX %c > %s", frameCounter, serial_buf);
|
||||
serial_out = 0;
|
||||
/* Write out full line of converted ASCII under hex.*/
|
||||
bufpos_a = (bufpos + 1) - LINE_LENGTH;
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a) || (asciichar > 0x2f && asciichar < 0x3a) || (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
if((bufpos_a + 1) % LINE_LENGTH == 0) {
|
||||
serial_out += chsnprintf(&serial_buf[serial_out], sizeof(serial_buf)-serial_out, " %c", asciichar);
|
||||
TRACE_DEBUG("RX %c > %s", frameCounter, serial_buf);
|
||||
serial_out = 0;
|
||||
} else {
|
||||
serial_out += chsnprintf(&serial_buf[serial_out], sizeof(serial_buf)-serial_out, " %c ", asciichar);
|
||||
}
|
||||
} while(bufpos_a++ < bufpos);
|
||||
} else {
|
||||
serial_out += chsnprintf(&serial_buf[serial_out], sizeof(serial_buf)-serial_out, "%02x ", frame_buffer[bufpos]);
|
||||
}
|
||||
} /* End for(bufpos = 0; bufpos < frame_size; bufpos++). */
|
||||
|
||||
TRACE_DEBUG("RX %c > %s", frameCounter, serial_buf);
|
||||
serial_out = 0;
|
||||
/* Write out remaining partial line of converted ASCII under hex. */
|
||||
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a) || (asciichar > 0x2f && asciichar < 0x3a) || (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
serial_out += chsnprintf(&serial_buf[serial_out], sizeof(serial_buf)-serial_out, " %c ", asciichar);
|
||||
} while(++bufpos_a < bufpos);
|
||||
TRACE_DEBUG("RX %c > %s", frameCounter, serial_buf);
|
||||
|
||||
if(actualCRC == computeCRC) {
|
||||
|
||||
packet_t pp = ax25_from_frame(frame_buffer, bufpos-2);
|
||||
if(pp != NULL) {
|
||||
|
||||
aprs_decode_packet(pp);
|
||||
ax25_delete(pp);
|
||||
|
||||
} else {
|
||||
TRACE_DEBUG("RX %c > Error in packet", frameCounter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {/* End if valid frame. */
|
||||
the_events = EVT_DIAG_OUT_END;
|
||||
TRACE_DEBUG("RX %c > Invalid frame, status %x, bytes %u", frameCounter, myPktFIFO->status, myPktFIFO->packet_size);
|
||||
}
|
||||
|
||||
#if SUSPEND_HANDLING == RELEASE_ON_OUTPUT
|
||||
/*
|
||||
* Wait for end of transmission on diagnostic channel.
|
||||
*/
|
||||
eventmask_t evt = chEvtWaitAllTimeout(the_events, TIME_S2I(10));
|
||||
if (!evt) {
|
||||
TRACE_ERROR("RX > FAIL: Timeout waiting for EOT from serial channels");
|
||||
}
|
||||
chEvtSignal(the_decoder, EVT_SUSPEND_EXIT);
|
||||
#else
|
||||
(void)the_events;
|
||||
#endif
|
||||
pktReleaseDataBuffer(packetHandler, myPktFIFO);
|
||||
if(packetHandler->packet_count % 100 == 0 && packetHandler->packet_count != 0) {
|
||||
/* Stop the decoder. */
|
||||
msg_t pmsg = pktStopDataReception(packetHandler);
|
||||
TRACE_DEBUG("RX > Decoder STOP %i", pmsg);
|
||||
if(packetHandler->packet_count % 1000 == 0 && packetHandler->packet_count != 0) {
|
||||
chThdSleep(TIME_S2I(5));
|
||||
pmsg = pktCloseReceiveChannel(packetHandler);
|
||||
TRACE_DEBUG("RX > Decoder CLOSE %i\r\n", pmsg);
|
||||
chThdSleep(TIME_S2I(5));
|
||||
packetHandler = pktOpenReceiveChannel(DECODE_AFSK, &afsk_radio);
|
||||
TRACE_DEBUG("RX > Decoder OPEN %x", packetHandler);
|
||||
}
|
||||
chThdSleep(TIME_S2I(5));
|
||||
pmsg = pktStartDataReception(packetHandler);
|
||||
TRACE_DEBUG("RX > Decoder START %i", pmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void receiveAFSK(uint32_t freq, uint8_t rssi) {
|
||||
lockRadio();
|
||||
|
||||
|
@ -1002,8 +1195,13 @@ void receiveAFSK(uint32_t freq, uint8_t rssi) {
|
|||
if(!radioInitialized)
|
||||
Si446x_init();
|
||||
|
||||
// Start transceiver
|
||||
Si446x_receive(freq, rssi, MOD_AFSK);
|
||||
|
||||
// Start receiver thread
|
||||
if(si446x_rx_thd == NULL)
|
||||
si446x_rx_thd = chThdCreateStatic(si446x_rx_wa, sizeof(si446x_rx_wa), HIGHPRIO, si_receiver, NULL);
|
||||
|
||||
// Wait for the transmitter to start (because it is used as mutex)
|
||||
while(Si446x_getState() != Si446x_STATE_RX)
|
||||
chThdSleep(TIME_MS2I(1));
|
||||
|
@ -1055,6 +1253,10 @@ THD_FUNCTION(si_fifo_feeder_fsk, arg)
|
|||
void send2FSK(packet_t packet, uint32_t freq, uint8_t pwr) {
|
||||
lockRadio();
|
||||
|
||||
// Stop packet handler (if started)
|
||||
if(packetHandler)
|
||||
pktStopDataReception(packetHandler);
|
||||
|
||||
// Initialize radio
|
||||
if(!radioInitialized)
|
||||
Si446x_init();
|
||||
|
|
|
@ -174,7 +174,7 @@ void pktEnablePWM(AFSKDemodDriver *myDriver) {
|
|||
/* Enabling events on both edges of CCA.*/
|
||||
palEnableLineEvent(LINE_CCA, PAL_EVENT_MODE_BOTH_EDGES);
|
||||
|
||||
myDriver->icustate = PWM_ICU_ACTIVE;
|
||||
myDriver->icustate = PKT_PWM_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,7 +205,7 @@ void pktDisablePWM(AFSKDemodDriver *myDriver) {
|
|||
/* Disable CCA port event. */
|
||||
palDisableLineEventI(LINE_CCA);
|
||||
|
||||
myDriver->icustate = PWM_ICU_STOP;
|
||||
myDriver->icustate = PKT_PWM_STOP;
|
||||
|
||||
chSysUnlock();
|
||||
}
|
||||
|
@ -313,7 +313,6 @@ bool pktProcessAFSK(AFSKDemodDriver *myDriver, min_pwmcnt_t current_tone[]) {
|
|||
*/
|
||||
if(pktProcessAFSKFilteredSample(myDriver)) {
|
||||
/* Filter is ready so decoding can commence. */
|
||||
//bool ready = pktCheckAFSKSymbolTime(myDriver);
|
||||
if(pktCheckAFSKSymbolTime(myDriver)) {
|
||||
/* A symbol is ready to decode. */
|
||||
if(!pktDecodeAFSKSymbol(myDriver))
|
||||
|
@ -530,7 +529,7 @@ AFSKDemodDriver *pktCreateAFSKDecoder(packet_rx_t *pktHandler,
|
|||
|
||||
/* Set the link from ICU driver to AFSK demod driver. */
|
||||
myDriver->icudriver->link = myDriver;
|
||||
myDriver->icustate = PWM_ICU_IDLE;
|
||||
myDriver->icustate = PKT_PWM_READY;
|
||||
|
||||
myDriver->decoder_thd = chThdCreateFromHeap(NULL,
|
||||
THD_WORKING_AREA_SIZE(PKT_AFSK_DECODER_WA_SIZE),
|
||||
|
@ -816,16 +815,16 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
|
|||
}
|
||||
|
||||
if(myDriver->frame_state == FRAME_CLOSE) {
|
||||
uint16_t magicCRC =
|
||||
#if AFSK_COLLISION_RESTART == TRUE
|
||||
uint16_t theCRC =
|
||||
calc_crc16(myHandler->active_packet_object->buffer, 0,
|
||||
myHandler->active_packet_object->packet_size);
|
||||
|
||||
/* Close packet and send event. */
|
||||
eventflags_t evt = (magicCRC == CRC_INCLUSIVE_CONSTANT)
|
||||
? EVT_AX25_FRAME_RDY
|
||||
: EVT_AX25_CRC_ERROR;
|
||||
pktAddEventFlags(myHandler, evt);
|
||||
myDriver->active_demod_object->status |= evt;
|
||||
if((myHandler->active_packet_object->packet_size < AX25_MIN_FRAME)
|
||||
|| (theCRC != CRC_INCLUSIVE_CONSTANT)) {
|
||||
pktRestartAFSKDecoder(myDriver);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
myDriver->decoder_state = DECODER_CLOSE;
|
||||
break; /* From this case. */
|
||||
}
|
||||
|
@ -839,10 +838,8 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
|
|||
/* Data not received in time.
|
||||
* Stop any further ICU writes to queue.
|
||||
*/
|
||||
pktAddEventFlags(myHandler,
|
||||
EVT_PWM_FIFO_LOCK | EVT_PWM_STREAM_TIMEOUT);
|
||||
myDriver->active_demod_object->status
|
||||
|= (EVT_PWM_FIFO_LOCK | EVT_PWM_STREAM_TIMEOUT);
|
||||
pktAddEventFlags(myHandler, EVT_PWM_STREAM_TIMEOUT);
|
||||
myDriver->active_demod_object->status |= EVT_PWM_STREAM_TIMEOUT;
|
||||
myDriver->decoder_state = DECODER_TIMEOUT;
|
||||
break;
|
||||
} /* End case DECODER_ACTIVE. */
|
||||
|
@ -853,8 +850,6 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
|
|||
} /* End case DECODER_CLOSE. */
|
||||
|
||||
case DECODER_TIMEOUT: {
|
||||
pktAddEventFlags(myHandler, EVT_AFSK_DATA_TIMEOUT);
|
||||
myDriver->active_demod_object->status |= EVT_AFSK_DATA_TIMEOUT;
|
||||
myDriver->decoder_state = DECODER_SUSPEND;
|
||||
break;
|
||||
} /* End case DECODER_TIMEOUT. */
|
||||
|
@ -912,6 +907,10 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
|
|||
|
||||
case DECODER_SUSPEND: {
|
||||
if(myHandler->active_packet_object != NULL) {
|
||||
/* Lock the FIFO against further writes. */
|
||||
pktAddEventFlags(myHandler, EVT_AFSK_DECODE_DONE | EVT_PWM_FIFO_LOCK);
|
||||
myDriver->active_demod_object->status |=
|
||||
(EVT_AFSK_DECODE_DONE | EVT_PWM_FIFO_LOCK);
|
||||
myHandler->active_packet_object->status =
|
||||
myDriver->active_demod_object->status;
|
||||
#if USE_AFSK_PHASE_STATISTICS == TRUE
|
||||
|
@ -921,6 +920,16 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
|
|||
myHandler->active_packet_object->drift =
|
||||
statistics->drift_max;
|
||||
#endif
|
||||
uint16_t magicCRC =
|
||||
calc_crc16(myHandler->active_packet_object->buffer, 0,
|
||||
myHandler->active_packet_object->packet_size);
|
||||
|
||||
/* Close packet and send event. */
|
||||
eventflags_t evt = (magicCRC == CRC_INCLUSIVE_CONSTANT)
|
||||
? EVT_AX25_FRAME_RDY
|
||||
: EVT_AX25_CRC_ERROR;
|
||||
pktAddEventFlags(myHandler, evt);
|
||||
myDriver->active_demod_object->status |= evt;
|
||||
chFifoSendObject(myHandler->packet_fifo_pool,
|
||||
myHandler->active_packet_object);
|
||||
myHandler->active_packet_object = NULL;
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define AFSK_NO_ERROR 0
|
||||
#define AFSK_QSQRT_ERROR 1
|
||||
|
||||
#define AFSK_ERROR_TYPE AFSK_QSQRT_ERROR
|
||||
#define AFSK_ERROR_TYPE AFSK_NO_ERROR
|
||||
|
||||
#define PRE_FILTER_GEN_COEFF TRUE
|
||||
#define PRE_FILTER_LOW 925
|
||||
|
@ -109,6 +109,8 @@
|
|||
/* Statistic analysis enabling. */
|
||||
#define USE_AFSK_PHASE_STATISTICS FALSE
|
||||
|
||||
#define AFSK_COLLISION_RESTART TRUE
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
@ -262,6 +264,12 @@ typedef struct AFSK_data {
|
|||
/* Module macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static inline void pktRestartAFSKDecoder(AFSKDemodDriver *myDriver) {
|
||||
packet_rx_t *myHandler = myDriver->packet_handler;
|
||||
myDriver->frame_state = FRAME_OPEN;
|
||||
myHandler->active_packet_object->packet_size = 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#define EVT_PKT_OUT_END EVENT_MASK(1)
|
||||
|
||||
#define PKT_BUFFER_SIZE AX25_MAX_PACKET_LEN
|
||||
#define NUMBER_PKT_FIFOS 2U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
|
@ -327,8 +326,7 @@ static inline pkt_data_fifo_t *pktReceiveDataBufferTimeout(packet_rx_t *handler,
|
|||
static inline msg_t pktIsBufferValidAX25Frame(pkt_data_fifo_t *object) {
|
||||
chDbgAssert(object != NULL, "no pointer to packet object buffer");
|
||||
uint16_t frame_size = object->packet_size;
|
||||
if((object->status & EVT_AX25_FRAME_RDY
|
||||
|| object->status & EVT_AX25_CRC_ERROR)
|
||||
if((object->status & EVT_AFSK_DECODE_DONE)
|
||||
&& frame_size >= AX25_MIN_FRAME) {
|
||||
return MSG_OK;
|
||||
}
|
||||
|
|
|
@ -70,11 +70,13 @@ const ICUConfig pwm_icucfg = {
|
|||
|
||||
|
||||
/**
|
||||
* @brief Initialises and attaches Radio ICU channel.
|
||||
* @post The ICU is configured and started.
|
||||
* @brief Initialises and assigns ICU to a Radio.
|
||||
* @post The ICU is configured and started for a specified radio.
|
||||
* @post The ports and timers for CCA input are configured.
|
||||
*
|
||||
* @param[in] myICU pointer to a @p ICUDriver structure
|
||||
* @param[in] radio_id radio being started.
|
||||
*
|
||||
* @return Pointer to assigned ICUDriver object.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
|
@ -171,6 +173,11 @@ void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt) {
|
|||
chDbgAssert(myDemod != NULL, "no demod linked");
|
||||
chVTResetI(&myICU->pwm_timer);
|
||||
|
||||
/*
|
||||
* Turn off the squelch LED.
|
||||
*/
|
||||
pktWriteSquelchLED(PAL_LOW);
|
||||
|
||||
/* Stop the ICU notification (callback). */
|
||||
icuDisableNotificationsI(myICU);
|
||||
if(myDemod->active_radio_object != NULL) {
|
||||
|
@ -192,12 +199,99 @@ void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt) {
|
|||
} else {
|
||||
pktAddEventFlagsI(myHandler, evt);
|
||||
}
|
||||
myDemod->icustate = PKT_PWM_READY;
|
||||
}
|
||||
|
||||
/* Currently unused. */
|
||||
/**
|
||||
* @brief Opens the PWM stream from the ICU.
|
||||
* @post The ICU notification (callback) is enabled.
|
||||
* @post If an error occurs the PWM is not started and state is unchanged.
|
||||
* @post If the queue is full the yellow LED is lit.
|
||||
* @post If no error occurs the timers associated with PWM are started.
|
||||
* @post The seized FIFO is sent via the queue mailbox.
|
||||
* @post The ICU state is set to active.
|
||||
*
|
||||
* @param[in] myICU pointer to a @p ICUDriver structure
|
||||
* @param[in] event flags to be set as to why the channel is opened.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pktOpenPWMChannelI(ICUDriver *myICU, eventflags_t evt) {
|
||||
(void)myICU;
|
||||
(void)evt;
|
||||
AFSKDemodDriver *myDemod = myICU->link;
|
||||
packet_rx_t *myHandler = myDemod->packet_handler;
|
||||
|
||||
/* Turn on the squelch LED. */
|
||||
pktWriteSquelchLED(PAL_HIGH);
|
||||
|
||||
/* Turn off the overflow LED. */
|
||||
pktWriteOverflowLED(PAL_LOW);
|
||||
|
||||
if(myDemod->active_radio_object != NULL) {
|
||||
/* TODO: Work out correct handling.
|
||||
* Shouldn't happen unless CCA has not triggered an EXTI trailing edge.
|
||||
* For now just flag that an error condition happened.
|
||||
*/
|
||||
pktClosePWMChannelI(myICU, EVT_RADIO_CCA_FIFO_ERR);
|
||||
return;
|
||||
}
|
||||
/* Normal CCA handling. */
|
||||
radio_cca_fifo_t *myFIFO = chFifoTakeObjectI(myDemod->pwm_fifo_pool);
|
||||
if(myFIFO == NULL) {
|
||||
myDemod->active_radio_object = NULL;
|
||||
/* No FIFO available.
|
||||
* Send an event to any listener.
|
||||
* Disable ICU notifications.
|
||||
*/
|
||||
pktAddEventFlagsI(myHandler, EVT_PWM_FIFO_EMPTY);
|
||||
icuDisableNotificationsI(myICU);
|
||||
return;
|
||||
}
|
||||
|
||||
myDemod->active_radio_object = myFIFO;
|
||||
|
||||
/* Clear event/status bits. */
|
||||
myFIFO->status = 0;
|
||||
|
||||
/*
|
||||
* Initialize FIFO release control semaphore.
|
||||
* The decoder thread waits on the semaphore before releasing to pool.
|
||||
*/
|
||||
chSemObjectInit(&myFIFO->sem, 0);
|
||||
|
||||
/* Each FIFO entry has an embedded input queue with data buffer. */
|
||||
(void)iqObjectInit(&myFIFO->radio_pwm_queue,
|
||||
myFIFO->packed_buffer.pwm_bytes,
|
||||
sizeof(radio_pwm_buffer_t),
|
||||
NULL , NULL);
|
||||
|
||||
/*
|
||||
* Set the status of this FIFO.
|
||||
* Send the FIFO entry to the decoder thread.
|
||||
*/
|
||||
chFifoSendObjectI(myDemod->pwm_fifo_pool, myFIFO);
|
||||
myFIFO->status |= EVT_PWM_FIFO_SENT;
|
||||
|
||||
/*
|
||||
* Start the ICU activity timer.
|
||||
* After timeout shutdown ICU.
|
||||
* This reduces power consumption.
|
||||
*/
|
||||
chVTSetI(&myICU->icu_timer, TIME_S2I(10),
|
||||
(vtfunc_t)pktICUInactivityTimeout, myICU);
|
||||
|
||||
/*
|
||||
* Start the PWM activity timer.
|
||||
* This catches the condition where CCA raises but no RX data appears.
|
||||
*/
|
||||
chVTSetI(&myICU->pwm_timer, TIME_MS2I(50),
|
||||
(vtfunc_t)pktPWMInactivityTimeout, myICU);
|
||||
|
||||
icuStartCaptureI(myICU);
|
||||
icuEnableNotificationsI(myICU);
|
||||
pktAddEventFlagsI(myHandler, evt);
|
||||
myFIFO->status |= evt;
|
||||
|
||||
myDemod->icustate = PKT_PWM_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -271,99 +365,63 @@ void pktPWMInactivityTimeout(ICUDriver *myICU) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Timer callback when CCA de-glitch period expires.
|
||||
* @brief Timer callback when CCA leading edge de-glitch period expires.
|
||||
* @notes If CCA is still asserted then PWM capture will be enabled.
|
||||
*
|
||||
* @param[in] myICU pointer to a @p ICUDriver structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pktRadioCCATimer(ICUDriver *myICU) {
|
||||
void pktRadioCCALeadTimer(ICUDriver *myICU) {
|
||||
chSysLockFromISR();
|
||||
AFSKDemodDriver *myDemod = myICU->link;
|
||||
packet_rx_t *myHandler = myDemod->packet_handler;
|
||||
//AFSKDemodDriver *myDemod = myICU->link;
|
||||
//packet_rx_t *myHandler = myDemod->packet_handler;
|
||||
/* CCA de-glitch timer expired. */
|
||||
switch(palReadLine(LINE_CCA)) {
|
||||
case PAL_LOW: {
|
||||
/* We should not get here unless CCA trailing edge callback didn't happen. */
|
||||
/*
|
||||
* We shouldn't arrive here unless EXTI failed to assert interrupt.
|
||||
* In that case the trailing edge callback didn't happen.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case PAL_HIGH: {
|
||||
/* Turn on the squelch LED. */
|
||||
pktWriteSquelchLED(PAL_HIGH);
|
||||
pktOpenPWMChannelI(myICU, EVT_RADIO_CCA_OPEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Turn off the overflow LED. */
|
||||
pktWriteOverflowLED(PAL_LOW);
|
||||
/**
|
||||
* @brief Timer callback when CCA trailing edge de-glitch period expires.
|
||||
* @notes If CCA is still asserted then PWM capture will continue.
|
||||
* @notes If CCA is not asserted then PWM capture will be closed.
|
||||
*
|
||||
* @param[in] myICU pointer to a @p ICUDriver structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void pktRadioCCATrailTimer(ICUDriver *myICU) {
|
||||
chSysLockFromISR();
|
||||
//AFSKDemodDriver *myDemod = myICU->link;
|
||||
|
||||
if(myDemod->active_radio_object != NULL) {
|
||||
/* TODO: Work out correct handling.
|
||||
* Shouldn't happen unless CCA has not triggered an EXTI trailing edge.
|
||||
* For now just flag that an error condition happened.
|
||||
*/
|
||||
pktClosePWMChannelI(myICU, EVT_RADIO_CCA_FIFO_ERR);
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
/* Normal CCA handling. */
|
||||
radio_cca_fifo_t *myFIFO = chFifoTakeObjectI(myDemod->pwm_fifo_pool);
|
||||
if(myFIFO == NULL) {
|
||||
myDemod->active_radio_object = NULL;
|
||||
/* No FIFO available.
|
||||
* Send an event to any listener.
|
||||
* Disable ICU notifications.
|
||||
*/
|
||||
pktAddEventFlagsI(myHandler, EVT_PWM_FIFO_EMPTY);
|
||||
icuDisableNotificationsI(myICU);
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
/* CCA de-glitch timer for trailing edge expired. */
|
||||
switch(palReadLine(LINE_CCA)) {
|
||||
case PAL_LOW: {
|
||||
/*
|
||||
* The decoder operates asynchronously to and usually slower than PWM.
|
||||
* When the decoder ends it returns its FIFO object to the pool.
|
||||
* Closing PWM sets the FIFO management semaphore.
|
||||
*/
|
||||
pktClosePWMChannelI(myICU, EVT_RADIO_CCA_CLOSE);
|
||||
break;
|
||||
}
|
||||
|
||||
myDemod->active_radio_object = myFIFO;
|
||||
|
||||
/* Clear event/status bits. */
|
||||
myFIFO->status = 0;
|
||||
|
||||
/*
|
||||
* Initialize FIFO release control semaphore.
|
||||
* The decoder thread waits on the semaphore before releasing to pool.
|
||||
*/
|
||||
chSemObjectInit(&myFIFO->sem, 0);
|
||||
|
||||
/* Each FIFO entry has an embedded input queue with data buffer. */
|
||||
(void)iqObjectInit(&myFIFO->radio_pwm_queue,
|
||||
myFIFO->packed_buffer.pwm_bytes,
|
||||
sizeof(radio_pwm_buffer_t),
|
||||
NULL , NULL);
|
||||
|
||||
/*
|
||||
* Set the status of this FIFO.
|
||||
* Send the FIFO entry to the decoder thread.
|
||||
*/
|
||||
chFifoSendObjectI(myDemod->pwm_fifo_pool, myFIFO);
|
||||
myFIFO->status |= EVT_PWM_FIFO_SENT;
|
||||
|
||||
/*
|
||||
* Start the ICU activity timer.
|
||||
* After timeout shutdown ICU.
|
||||
* This reduces power consumption.
|
||||
*/
|
||||
chVTSetI(&myICU->icu_timer, TIME_S2I(10),
|
||||
(vtfunc_t)pktICUInactivityTimeout, myICU);
|
||||
|
||||
/*
|
||||
* Start the PWM activity timer.
|
||||
* This catches the condition where CCA raises but no RX data appears.
|
||||
*/
|
||||
chVTSetI(&myICU->pwm_timer, TIME_MS2I(50),
|
||||
(vtfunc_t)pktPWMInactivityTimeout, myICU);
|
||||
|
||||
icuStartCaptureI(myICU);
|
||||
icuEnableNotificationsI(myICU);
|
||||
pktAddEventFlagsI(myHandler, EVT_RADIO_CCA_OPEN);
|
||||
myFIFO->status |= EVT_RADIO_CCA_OPEN;
|
||||
|
||||
myDemod->icustate = PWM_ICU_ACTIVE;
|
||||
case PAL_HIGH: {
|
||||
/* CCA is active again so leave PWM open. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -373,7 +431,7 @@ void pktRadioCCATimer(ICUDriver *myICU) {
|
|||
|
||||
/**
|
||||
* @brief GPIO callback when CCA edge transitions.
|
||||
* @notes Will be de-glitched by the CCA timer.
|
||||
* @notes Both edges are de-glitched by the CCA timer.
|
||||
*
|
||||
* @param[in] myICU pointer to a @p ICUDriver structure
|
||||
*
|
||||
|
@ -382,54 +440,37 @@ void pktRadioCCATimer(ICUDriver *myICU) {
|
|||
void pktRadioCCAInput(ICUDriver *myICU) {
|
||||
chSysLockFromISR();
|
||||
AFSKDemodDriver *myDemod = myICU->link;
|
||||
packet_rx_t *myHandler = myDemod->packet_handler;
|
||||
|
||||
if(myDemod->icustate == PKT_PWM_STOP) {
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
/* CCA changed. */
|
||||
switch(palReadLine(LINE_CCA)) {
|
||||
case PAL_LOW: {
|
||||
if(myDemod->icustate == PWM_ICU_ACTIVE) {
|
||||
/* TODO: Add CCA trailing edge glitch handling.
|
||||
* Start timer and wait to determine if CCA is still low before closing PWM.
|
||||
if(myDemod->icustate == PKT_PWM_ACTIVE) {
|
||||
/* CCA trailing edge glitch handling.
|
||||
* Start timer and check if CCA remains low before closing PWM.
|
||||
*
|
||||
* TODO: Calculate de-glitch time as number of symbol times.
|
||||
*/
|
||||
chVTSetI(&myICU->cca_timer, TIME_MS2I(66),
|
||||
(vtfunc_t)pktRadioCCATrailTimer, myICU);
|
||||
}
|
||||
if(chVTIsArmedI(&myICU->cca_timer)) {
|
||||
/* CCA has dropped during timer so CCA is a glitch. */
|
||||
chVTResetI(&myICU->cca_timer);
|
||||
pktAddEventFlagsI(myHandler, EVT_RADIO_CCA_GLITCH);
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off the squelch LED.
|
||||
*/
|
||||
pktWriteSquelchLED(PAL_LOW);
|
||||
|
||||
if(myDemod->active_radio_object == NULL) {
|
||||
|
||||
/* CCA has dropped with no FIFO assigned.
|
||||
* This happens when CCA raises but all FIFOs are allocated.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The decoder operates asynchronously to and usually slower than PWM.
|
||||
* When the decoder ends it returns its FIFO object to the pool.
|
||||
* Closing PWM sets the FIFO management semaphore.
|
||||
*/
|
||||
pktClosePWMChannelI(myICU, EVT_RADIO_CCA_CLOSE);
|
||||
|
||||
/* Idle state. */
|
||||
break;
|
||||
} /* End case PAL_LOW. */
|
||||
|
||||
case PAL_HIGH: {
|
||||
if(myDemod->icustate == PWM_ICU_STOP)
|
||||
/* ICU is shut down. */
|
||||
if(chVTIsArmedI(&myICU->cca_timer)) {
|
||||
/* CAA has been re-asserted during trailing edge timer. */
|
||||
chVTResetI(&myICU->cca_timer);
|
||||
break;
|
||||
|
||||
}
|
||||
/* Else this is a leading edge of CCA for a new packet. */
|
||||
/* TODO: Calculate de-glitch time as number of symbol times. */
|
||||
chVTSetI(&myICU->cca_timer, TIME_MS2I(66),
|
||||
(vtfunc_t)pktRadioCCATimer, myICU);
|
||||
(vtfunc_t)pktRadioCCALeadTimer, myICU);
|
||||
break;
|
||||
}
|
||||
} /* End switch. */
|
||||
|
@ -477,8 +518,8 @@ void pktRadioICUPeriod(ICUDriver *myICU) {
|
|||
* but before squelch close could cause lingering ICU activity.
|
||||
*
|
||||
*/
|
||||
if((myDemod->active_radio_object->status & EVT_PWM_FIFO_LOCK) != 0) {
|
||||
pktClosePWMChannelI(myICU, EVT_PWM_STREAM_ABORT);
|
||||
if((myDemod->active_radio_object->status & EVT_AFSK_DECODE_DONE) != 0) {
|
||||
pktClosePWMChannelI(myICU, EVT_PWM_STREAM_CLOSED);
|
||||
chSysUnlockFromISR();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
|
||||
/* Definitions for ICU FIFO implemented using chfactory. */
|
||||
#define NUMBER_PWM_FIFOS 3
|
||||
#define PWM_BUFFER_SLOTS 4000
|
||||
|
||||
/**
|
||||
* ICU related definitions.
|
||||
*/
|
||||
|
@ -49,9 +44,10 @@
|
|||
/*===========================================================================*/
|
||||
|
||||
typedef enum ICUStates {
|
||||
PWM_ICU_IDLE,
|
||||
PWM_ICU_ACTIVE,
|
||||
PWM_ICU_STOP
|
||||
PKT_PWM_INIT = 0,
|
||||
PKT_PWM_READY,
|
||||
PKT_PWM_ACTIVE,
|
||||
PKT_PWM_STOP
|
||||
} rx_icu_state_t;
|
||||
|
||||
/* Types for ICU and PWM data. */
|
||||
|
@ -205,6 +201,8 @@ extern "C" {
|
|||
void pktSleepICUI(ICUDriver *myICU);
|
||||
msg_t pktQueuePWMDataI(ICUDriver *myICU);
|
||||
void pktClosePWMChannelI(ICUDriver *myICU, eventflags_t evt);
|
||||
void pktICUInactivityTimeout(ICUDriver *myICU);
|
||||
void pktPWMInactivityTimeout(ICUDriver *myICU);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
|
||||
#include "pktconf.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#if AFSK_DECODE_TYPE == AFSK_DSP_QCORR_DECODE
|
||||
|
||||
|
@ -208,7 +208,7 @@ q31_t push_qcorr_sample(AFSKDemodDriver *myDriver, bit_t sample) {
|
|||
#if AFSK_DEBUG_TYPE == AFSK_QCORR_FIR_DEBUG
|
||||
char buf[80];
|
||||
int out = chsnprintf(buf, sizeof(buf), "%X\r\n", scaledOut);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -262,12 +262,13 @@ bool process_qcorr_output(AFSKDemodDriver *myDriver) {
|
|||
int out = chsnprintf(buf, sizeof(buf), "%i, %i\r\n",
|
||||
myBin->cos_out, myBin->sin_out);
|
||||
}
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wait for initial data to appear from pre-filter. */
|
||||
if(++decoder->filter_valid < decoder->input_filter->filter_instance->numTaps)
|
||||
if(++decoder->filter_valid <
|
||||
PRE_FILTER_NUM_TAPS + DECODE_FILTER_LENGTH)
|
||||
return false;
|
||||
|
||||
/* Compute magnitude of bins. */
|
||||
|
@ -291,7 +292,7 @@ bool process_qcorr_output(AFSKDemodDriver *myDriver) {
|
|||
i, decoder->filter_bins[i].mag,
|
||||
decoder->current_n,
|
||||
decoder->current_n % decoder->decode_length);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -495,7 +496,7 @@ void calc_qcorr_magnitude(AFSKDemodDriver *myDriver) {
|
|||
int out = chsnprintf(buf, sizeof(buf), \
|
||||
"MAG SQRT failed bin %i, cosQ %X, sinQ %X, cos %f, sin %f, mag2 %f, mag %X, index %i\r\n", \
|
||||
i, myBin->cos_out, myBin->sin_out, cos, sin, mag2, raw_mag, decoder->current_n);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif /* AFSK_ERROR_TYPE == AFSK_SQRT_ERROR */
|
||||
|
||||
#else
|
||||
|
@ -515,7 +516,7 @@ void calc_qcorr_magnitude(AFSKDemodDriver *myDriver) {
|
|||
"mag2 %X, mag %X, index %i\r\n",
|
||||
i, myBin->cos_out, myBin->sin_out, cos, sin, mag2,
|
||||
decoder->filter_bins[i].raw_mag, decoder->current_n);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif /* AFSK_ERROR_TYPE == AFSK_SQRT_ERROR */
|
||||
#endif /* QCORR_MAG_USE_FLOAT */
|
||||
}
|
||||
|
@ -527,7 +528,7 @@ void calc_qcorr_magnitude(AFSKDemodDriver *myDriver) {
|
|||
} else {
|
||||
out = chsnprintf(buf, sizeof(buf), "%i\r\n", raw_mag);
|
||||
}
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +564,7 @@ void filter_qcorr_magnitude(AFSKDemodDriver *myDriver) {
|
|||
decoder->filter_bins[i].raw_mag,
|
||||
decoder->filter_bins[i].filtered_mag);
|
||||
}
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +604,7 @@ void evaluate_qcorr_tone(AFSKDemodDriver *myDriver) {
|
|||
char buf[200];
|
||||
int out = chsnprintf(buf, sizeof(buf), "%i, %i\r\n",
|
||||
mark, space);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -644,7 +645,7 @@ static void setup_qcorr_prefilter(qcorr_decoder_t *decoder) {
|
|||
char buf[80];
|
||||
int out = chsnprintf(buf, sizeof(buf),
|
||||
"PRE FILTER COEFF %f %x\r\n", coeff_total_f32, coeff_total_q31);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -682,7 +683,7 @@ void setup_qcorr_IQfilters(qcorr_decoder_t *decoder) {
|
|||
/ (float32_t)decoder->sample_rate;
|
||||
|
||||
gen_fir_iqf(cos_table, sin_table, decoder->decode_length,
|
||||
norm_freq, TD_WINDOW_CHEBYSCHEV);
|
||||
norm_freq, QCORR_IQ_WINDOW);
|
||||
/*
|
||||
* Create the Mark correlation filters.
|
||||
*/
|
||||
|
@ -707,7 +708,7 @@ void setup_qcorr_IQfilters(qcorr_decoder_t *decoder) {
|
|||
/ (float32_t)decoder->sample_rate;
|
||||
|
||||
gen_fir_iqf(cos_table, sin_table, decoder->decode_length,
|
||||
norm_freq, TD_WINDOW_CHEBYSCHEV);
|
||||
norm_freq, QCORR_IQ_WINDOW);
|
||||
|
||||
/*
|
||||
* Create the Space correlation filters.
|
||||
|
@ -775,7 +776,7 @@ static void setup_qcorr_magfilter(qcorr_decoder_t *decoder) {
|
|||
char buf[80];
|
||||
int out = chsnprintf(buf, sizeof(buf),
|
||||
"MAG FILTER COEFF %f %x\r\n", bin_coeff_total_f32, bin_coeff_total_q31);
|
||||
chnWrite(&SDU1, (uint8_t *)buf, out);
|
||||
chnWrite(diag_out, (uint8_t *)buf, out);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -37,11 +37,13 @@
|
|||
#define QCORR_PLL_SEARCH_RATE 0.5f
|
||||
#define QCORR_PLL_LOCKED_RATE 0.75f
|
||||
|
||||
/* Used for indexing of IQ filter sections. */
|
||||
#define QCORR_COS_INDEX 0U
|
||||
#define QCORR_SIN_INDEX 1U
|
||||
#define QCORR_IQ_WINDOW TD_WINDOW_CHEBYSCHEV
|
||||
|
||||
#define REPORT_QCORR_COEFFS FALSE
|
||||
/* Used for indexing of IQ filter sections. */
|
||||
#define QCORR_COS_INDEX 0U
|
||||
#define QCORR_SIN_INDEX 1U
|
||||
|
||||
#define REPORT_QCORR_COEFFS FALSE
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
|
|
|
@ -12,6 +12,32 @@
|
|||
|
||||
#include "pktconf.h"
|
||||
|
||||
/**
|
||||
* @file dbguart.c
|
||||
* @brief Serial channels for debug.
|
||||
*
|
||||
* @addtogroup IODevices
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if PKT_CFG_USE_SERIAL == TRUE
|
||||
const SerialConfig debug_config = {
|
||||
115200,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
/* Declare UART aliases. */
|
||||
BaseSequentialStream* diag_out = (BaseSequentialStream*) &SD3;
|
||||
BaseSequentialStream* pkt_out = (BaseSequentialStream*) &SD4;
|
||||
|
||||
void pktSerialStart() {
|
||||
pktConfigSerialDiag();
|
||||
pktConfigSerialPkt();
|
||||
sdStart(&SD4, &debug_config);
|
||||
sdStart(&SD3, &debug_config);
|
||||
}
|
||||
#endif /* PKT_CFG_USE_SERIAL */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
|
||||
extern BaseSequentialStream* diag_out;
|
||||
extern BaseSequentialStream* pkt_out;
|
||||
|
||||
extern const SerialConfig debug_config;
|
||||
|
||||
|
|
|
@ -42,21 +42,16 @@ static inline double window_T(double n, double x) {
|
|||
* @return coefficient for window shape at index j
|
||||
*/
|
||||
float32_t dsp_window(td_window_t type, size_t size, size_t j) {
|
||||
float32_t center;
|
||||
float32_t w;
|
||||
|
||||
center = 0.5 * (size - 1);
|
||||
|
||||
switch (type) {
|
||||
|
||||
case TD_WINDOW_COSINE:
|
||||
w = arm_cos_f32((j - center) / size * M_PI);
|
||||
//w = cos(j * M_PI / (size - 1));
|
||||
w = arm_cos_f32(j * M_PI / (size - 1));
|
||||
break;
|
||||
|
||||
case TD_WINDOW_SINE:
|
||||
w = arm_sin_f32((j - center) / size * M_PI);
|
||||
//w = sin(j * M_PI / (size - 1));
|
||||
w = arm_sin_f32(j * M_PI / (size - 1));
|
||||
break;
|
||||
|
||||
case TD_WINDOW_HAMMING:
|
||||
|
@ -105,7 +100,8 @@ float32_t dsp_window(td_window_t type, size_t size, size_t j) {
|
|||
double b = window_beta(N, a);
|
||||
double sum = 0;
|
||||
for(k = 0;k < M;k++) {
|
||||
sum += (k & 1 ? -1 : 1) * window_T(N, b * cos(M_PI * k/N)) * cos(2 * j * k * M_PI/N);
|
||||
sum += (k & 1 ? -1 : 1) * window_T(N, b * cos(M_PI * k/N))
|
||||
* cos(2 * j * k * M_PI/N);
|
||||
}
|
||||
sum /= window_T(N, b);
|
||||
sum -= .5;
|
||||
|
@ -123,13 +119,14 @@ float32_t dsp_window(td_window_t type, size_t size, size_t j) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate coefficients for a correlation filter
|
||||
* @post The coefficient arrays are populated
|
||||
* @brief Calculate coefficients for a correlation filter.
|
||||
* @post The coefficient arrays are populated.
|
||||
*
|
||||
* @param[in] pCos pointer I (cos) to coefficient result array
|
||||
* @param[in] pSin pointer Q (sin) to coefficient result array
|
||||
* @param[in] length number of taps in filter
|
||||
* @param[in] window window type to apply to coefficients
|
||||
* @param[in] pCos pointer to I (cos) coefficient result array.
|
||||
* @param[in] pSin pointer to Q (sin) coefficient result array.
|
||||
* @param[in] length number of taps in filter.
|
||||
* @param[in] norm_freq normalised filter frequency.
|
||||
* @param[in] window window shape to apply to coefficients.
|
||||
*
|
||||
*/
|
||||
void gen_fir_iqf(float32_t *pCos, float32_t *pSin,
|
||||
|
|
|
@ -28,48 +28,67 @@
|
|||
* @note Only used in buffers.
|
||||
*/
|
||||
typedef uint8_t ax25char_t;
|
||||
typedef int16_t ax25size_t;
|
||||
|
||||
/* AX25 data escape code. */
|
||||
#define AX25_ESC 0x1BU
|
||||
|
||||
/* AX25 definitions for packet data useage. */
|
||||
#define AX25_MAX_REPEATERS 8
|
||||
#define AX25_MIN_ADDRS 2 /* Destination & Source. */
|
||||
#define AX25_MAX_ADDRS 10 /* Destination & Source + 8 digipeaters. */
|
||||
|
||||
#define AX25_DESTINATION 0 /* Address positions in frame. */
|
||||
#define AX25_SOURCE 1
|
||||
#define AX25_REPEATER_1 2
|
||||
#define AX25_REPEATER_2 3
|
||||
#define AX25_REPEATER_3 4
|
||||
#define AX25_REPEATER_4 5
|
||||
#define AX25_REPEATER_5 6
|
||||
#define AX25_REPEATER_6 7
|
||||
#define AX25_REPEATER_7 8
|
||||
#define AX25_REPEATER_8 9
|
||||
/* Destination & Source. */
|
||||
#define AX25_MIN_ADDRS 2
|
||||
/*
|
||||
* Destination & Source + 8 digipeater addresses.
|
||||
* A destination address may specify a generic APRS digipeater path.
|
||||
* In such case the digipeater address fields are overridden by that path.
|
||||
*/
|
||||
#define AX25_MAX_ADDRS 10
|
||||
/* Address positions in frame. */
|
||||
#define AX25_DESTINATION 0
|
||||
#define AX25_SOURCE 1
|
||||
#define AX25_REPEATER_1 2
|
||||
#define AX25_REPEATER_2 3
|
||||
#define AX25_REPEATER_3 4
|
||||
#define AX25_REPEATER_4 5
|
||||
#define AX25_REPEATER_5 6
|
||||
#define AX25_REPEATER_6 7
|
||||
#define AX25_REPEATER_7 8
|
||||
#define AX25_REPEATER_8 9
|
||||
|
||||
/*
|
||||
* The maximum length should be 6 letters, dash, 2 digits and null.
|
||||
* The maximum address length should be 6 letters, dash, 2 digits and null.
|
||||
* Making a total of 10.
|
||||
* However, object labels can be 10 characters.
|
||||
* So add 2 extra bytes for safety.
|
||||
* So add 2 extra margin bytes.
|
||||
*/
|
||||
#define AX25_MAX_ADDR_LEN 12
|
||||
#define AX25_DS_ADDRESS_LEN 7
|
||||
#define AX25_MAX_ADDR_LEN 12
|
||||
#define AX25_CONTROL_LEN 1
|
||||
#define AX25_PROTOCOL_LEN 1
|
||||
#define AX25_CRC_LEN 2
|
||||
#define AX25_FLAG_LEN 1
|
||||
#define AX25_MIN_INFO_LEN 0
|
||||
|
||||
#define AX25_MIN_INFO_LEN 0
|
||||
|
||||
/* An AX.25 packet can have a control byte and no protocol. */
|
||||
#define AX25_MIN_PACKET_LEN (AX25_MIN_ADDRS * AX25_DS_ADDRESS_LEN \
|
||||
+ AX25_CONTROL_LEN)
|
||||
|
||||
/*
|
||||
* Maximum size for APRS.
|
||||
* The 2 bytes for the frame CRC are not included.
|
||||
* The payload excluding CRC.
|
||||
*/
|
||||
#define AX25_MAX_INFO_LEN 2048
|
||||
/* An AX.25 frame can have a control byte and no protocol. */
|
||||
#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 )
|
||||
#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN)
|
||||
#define AX25_CRC_LEN 2U
|
||||
#define AX25_MIN_FRAME ((AX25_MIN_PACKET_LEN) + AX25_CRC_LEN)
|
||||
#define AX25_MAX_FRAME ((AX25_MAX_PACKET_LEN) + AX25_CRC_LEN)
|
||||
#define AX25_MAX_INFO_LEN 2048
|
||||
|
||||
/* An AX.25 packet maximum - closing flag is not included. */
|
||||
#define AX25_MAX_PACKET_LEN (AX25_MAX_ADDRS * AX25_DS_ADDRESS_LEN \
|
||||
+ AX25_CONTROL_LEN \
|
||||
+ AX25_PROTOCOL_LEN \
|
||||
+ AX25_MAX_INFO_LEN \
|
||||
+ AX25_CRC_LEN)
|
||||
|
||||
#define AX25_MIN_FRAME ((AX25_MIN_PACKET_LEN) + AX25_CRC_LEN)
|
||||
#define AX25_MAX_FRAME ((AX25_MAX_PACKET_LEN) + AX25_CRC_LEN)
|
||||
|
||||
#endif /* IO_PROTOCOLS_AX25_H_ */
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
Aerospace Decoder - Copyright (C) 2018 Bob Anderson (VK2GJ)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ax25_dump.c
|
||||
* @brief Packet dump utility.
|
||||
*
|
||||
* @addtogroup DSP
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "pktconf.h"
|
||||
|
||||
/* Buffer and size params for serial terminal output. */
|
||||
char serial_buf[1024];
|
||||
int serial_out;
|
||||
|
||||
void pktDumpAX25Frame(ax25char_t *frame_buffer,
|
||||
ax25size_t frame_size, ax25_select_t which) {
|
||||
if(which == AX25_DUMP_ALL || which == AX25_DUMP_RAW) {
|
||||
ax25size_t bufpos;
|
||||
ax25size_t bufpos_a = 0;
|
||||
/* Write out a buffer line as hex first. */
|
||||
for(bufpos = 0; bufpos < frame_size; bufpos++) {
|
||||
if((bufpos + 1) % LINE_LENGTH == 0) {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"%02x\r\n", frame_buffer[bufpos]);
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
/* Write out full line of converted ASCII under hex.*/
|
||||
bufpos_a = (bufpos + 1) - LINE_LENGTH;
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a)
|
||||
|| (asciichar > 0x2f && asciichar < 0x3a)
|
||||
|| (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
if((bufpos_a + 1) % LINE_LENGTH == 0) {
|
||||
serial_out = chsnprintf(serial_buf,
|
||||
sizeof(serial_buf),
|
||||
" %c\r\n", asciichar);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf,
|
||||
sizeof(serial_buf),
|
||||
" %c ", asciichar);
|
||||
}
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
} while(bufpos_a++ < bufpos);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"%02x ", frame_buffer[bufpos]);
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
} /* End for(bufpos = 0; bufpos < frame_size; bufpos++). */
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "\r\n");
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
/* Write out remaining partial line of converted ASCII under hex. */
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a)
|
||||
|| (asciichar > 0x2f && asciichar < 0x3a)
|
||||
|| (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
" %c ", asciichar);
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
} while(++bufpos_a < bufpos);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "\r\n");
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
} /* End raw dump. */
|
||||
|
||||
if(which == AX25_DUMP_ALL || which == AX25_DUMP_APRS) {
|
||||
uint16_t magicCRC = calc_crc16(frame_buffer, 0, frame_size);
|
||||
if(magicCRC == CRC_INCLUSIVE_CONSTANT) {
|
||||
/* CRC is good => decode APRS packet */
|
||||
packet_t pp = ax25_from_frame(frame_buffer, frame_size - 2);
|
||||
if(pp != NULL) {
|
||||
char rec[1024];
|
||||
unsigned char *pinfo;
|
||||
ax25_format_addrs(pp, rec);
|
||||
ax25_get_info(pp, &pinfo);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"%s", rec);
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
for(uint32_t i=0; pinfo[i]; i++) {
|
||||
if(pinfo[i] < 32 || pinfo[i] > 126) {
|
||||
/* Printable char */
|
||||
serial_out = chsnprintf(serial_buf,
|
||||
sizeof(serial_buf),
|
||||
"<0x%02x>", pinfo[i]);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf,
|
||||
sizeof(serial_buf),
|
||||
"%c", pinfo[i]);
|
||||
}
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"\r\n");
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
ax25_delete(pp);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"APRS: Error in packet\r\n");
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"APRS: Bad CRC\r\n");
|
||||
chnWrite(diag_out, (uint8_t *)serial_buf, serial_out);
|
||||
} /* End CRC check. */
|
||||
} /* End APRS dump. */
|
||||
}
|
||||
|
||||
/** @} */
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
/*
|
||||
Aerospace Decoder - Copyright (C) 2018 Bob Anderson (VK2GJ)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ax25_dump.h
|
||||
* @brief Packet dump utility.
|
||||
*
|
||||
* @addtogroup DSP
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef IO_PROTOCOLS_AX25_DUMP_H_
|
||||
#define IO_PROTOCOLS_AX25_DUMP_H_
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define LINE_LENGTH 60U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
typedef enum AX25Dump {
|
||||
AX25_DUMP_NONE,
|
||||
AX25_DUMP_RAW,
|
||||
AX25_DUMP_APRS,
|
||||
AX25_DUMP_ALL
|
||||
} ax25_select_t;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void pktDumpAX25Frame(ax25char_t *frame_buffer, ax25size_t frame_size,
|
||||
ax25_select_t which);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module inline functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
|
||||
#endif /* IO_PROTOCOLS_AX25_DUMP_H_ */
|
||||
|
||||
/** @} */
|
|
@ -13,8 +13,8 @@
|
|||
#define HDLC_CODE_MASK 0xFFU
|
||||
#define HDLC_FLAG 0x7EU
|
||||
#define HDLC_ZERO 0x00U
|
||||
#define HDLC_FRAME_MASK_A 0x0000FFFFU
|
||||
#define HDLC_FRAME_OPEN_A 0x007E7E7EU
|
||||
#define HDLC_FRAME_MASK_A 0x000000FFU
|
||||
#define HDLC_FRAME_OPEN_A 0x7E7E7E7EU
|
||||
#define HDLC_FRAME_MASK_B 0x0000FFFFU
|
||||
#define HDLC_FRAME_OPEN_B 0x0000007EU
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "shell.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* For F103 ARM_MATH_CM3 set -DARM_MATH_CM3 in the makefile CDefines section.
|
||||
* For F413 ARM_MATH_CM4 set -DARM_MATH_CM4 in the makefile CDefines section.
|
||||
|
@ -65,16 +66,19 @@
|
|||
#define EVT_DECODER_STOP EVENT_MASK(EVT_PRIORITY_BASE + 16)
|
||||
#define EVT_RADIO_CCA_FIFO_ERR EVENT_MASK(EVT_PRIORITY_BASE + 17)
|
||||
#define EVT_AX25_BUFFER_FULL EVENT_MASK(EVT_PRIORITY_BASE + 18)
|
||||
#define EVT_AFSK_DATA_TIMEOUT EVENT_MASK(EVT_PRIORITY_BASE + 19)
|
||||
//#define EVT_AFSK_DATA_TIMEOUT EVENT_MASK(EVT_PRIORITY_BASE + 19)
|
||||
|
||||
#define EVT_AX25_CRC_ERROR EVENT_MASK(EVT_PRIORITY_BASE + 20)
|
||||
#define EVT_HDLC_RESET_RCVD EVENT_MASK(EVT_PRIORITY_BASE + 21)
|
||||
#define EVT_AX25_NO_BUFFER EVENT_MASK(EVT_PRIORITY_BASE + 22)
|
||||
#define EVT_ICU_SLEEP_TIMEOUT EVENT_MASK(EVT_PRIORITY_BASE + 23)
|
||||
|
||||
#define EVT_PWM_STREAM_ABORT EVENT_MASK(EVT_PRIORITY_BASE + 24)
|
||||
#define EVT_PWM_STREAM_CLOSED EVENT_MASK(EVT_PRIORITY_BASE + 24)
|
||||
#define EVT_PKT_CHANNEL_CLOSE EVENT_MASK(EVT_PRIORITY_BASE + 25)
|
||||
#define EVT_DECODER_ACK EVENT_MASK(EVT_PRIORITY_BASE + 26)
|
||||
#define EVT_AFSK_DECODE_DONE EVENT_MASK(EVT_PRIORITY_BASE + 27)
|
||||
|
||||
#define EVT_RADIO_CCA_SPIKE EVENT_MASK(EVT_PRIORITY_BASE + 28)
|
||||
|
||||
#define EVT_STATUS_CLEAR EVT_NONE
|
||||
|
||||
|
@ -143,6 +147,8 @@ typedef struct radioConfig {
|
|||
#include "rxhdlc.h"
|
||||
#include "rxpacket.h"
|
||||
#include "ihex_out.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "ax25_dump.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
/*===========================================================================*/
|
||||
|
||||
//#define LINE_OVERFLOW_LED LINE_LED3
|
||||
#define LINE_DECODER_LED LINE_IO_BLUE
|
||||
//#define LINE_SQUELCH_LED LINE_LED1
|
||||
//#define LINE_DECODER_LED LINE_IO_BLUE
|
||||
#define LINE_SQUELCH_LED LINE_IO_BLUE
|
||||
|
||||
#define LINE_CCA PAL_LINE(GPIOD, 2U)
|
||||
#define LINE_ICU PAL_LINE(GPIOB, 6U)
|
||||
|
@ -25,6 +25,13 @@
|
|||
|
||||
#define PWM_ICU ICUD4
|
||||
|
||||
/* Definitions for ICU FIFO implemented using chfactory. */
|
||||
#define NUMBER_PWM_FIFOS 4
|
||||
#define PWM_BUFFER_SLOTS 6000
|
||||
|
||||
/* Number of AX25 output buffers. */
|
||||
#define NUMBER_PKT_FIFOS 2U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Module pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
|
|
@ -21,10 +21,37 @@
|
|||
#include <string.h>
|
||||
#include "debug.h"
|
||||
#include "base91.h"
|
||||
#include "digipeater.h"
|
||||
#include "dedupe.h"
|
||||
|
||||
#define METER_TO_FEET(m) (((m)*26876) / 8192)
|
||||
|
||||
static uint16_t msg_id;
|
||||
char alias_re[] = "WIDE[4-7]-[1-7]|CITYD";
|
||||
char wide_re[] = "WIDE[1-7]-[1-7]";
|
||||
enum preempt_e preempt = PREEMPT_OFF;
|
||||
|
||||
static void aprs_printPacket(const char* prefix, packet_t pp)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
// Decode packet
|
||||
char rec[256];
|
||||
unsigned char *pinfo;
|
||||
ax25_format_addrs(pp, rec);
|
||||
ax25_get_info(pp, &pinfo);
|
||||
|
||||
// Print decoded packet
|
||||
uint32_t out = chsnprintf(buf, sizeof(buf), "%s > %s", prefix, rec);
|
||||
for(uint32_t i=0; pinfo[i]; i++) {
|
||||
if(pinfo[i] < 32 || pinfo[i] > 126) {
|
||||
out += chsnprintf(&buf[out], sizeof(buf)-out, "<0x%02x>", pinfo[i]);
|
||||
} else {
|
||||
out += chsnprintf(&buf[out], sizeof(buf)-out, "%c", pinfo[i]);
|
||||
}
|
||||
}
|
||||
TRACE_INFO("%s", buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit APRS position packet. The comments are filled with:
|
||||
|
@ -66,7 +93,7 @@ packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoin
|
|||
uint8_t origin = ORIGIN_PICO;
|
||||
|
||||
char xmit[256];
|
||||
uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s-%d>%s,%s:!", config->callsign, config->ssid, APRS_DEST_CALLSIGN, config->path);
|
||||
uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:!", config->callsign, APRS_DEST_CALLSIGN, config->path);
|
||||
|
||||
xmit[len+0] = (config->symbol >> 8) & 0xFF;
|
||||
xmit[len+1] = y3+33;
|
||||
|
@ -115,7 +142,7 @@ packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoin
|
|||
packet_t aprs_encode_data_packet(char packetType, const aprs_conf_t *config, uint8_t *data)
|
||||
{
|
||||
char xmit[256];
|
||||
chsnprintf(xmit, sizeof(xmit), "%s-%d>%s,%s:{{%c%s", config->callsign, config->ssid, APRS_DEST_CALLSIGN, config->path, packetType, data);
|
||||
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:{{%c%s", config->callsign, APRS_DEST_CALLSIGN, config->path, packetType, data);
|
||||
|
||||
return ax25_from_text(xmit, 1);
|
||||
}
|
||||
|
@ -127,27 +154,160 @@ packet_t aprs_encode_message(const aprs_conf_t *config, const char *receiver, co
|
|||
{
|
||||
char xmit[256];
|
||||
if(noCounter)
|
||||
chsnprintf(xmit, sizeof(xmit), "%s-%d>%s,%s::%-9s:%s", config->callsign, config->ssid, APRS_DEST_CALLSIGN, config->path, receiver, text);
|
||||
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", config->callsign, APRS_DEST_CALLSIGN, config->path, receiver, text);
|
||||
else
|
||||
chsnprintf(xmit, sizeof(xmit), "%s-%d>%s,%s::%-9s:%s{%d", config->callsign, config->ssid, APRS_DEST_CALLSIGN, config->path, receiver, text, ++msg_id);
|
||||
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", config->callsign, APRS_DEST_CALLSIGN, config->path, receiver, text, ++msg_id);
|
||||
|
||||
return ax25_from_text(xmit, 1);
|
||||
}
|
||||
|
||||
static bool aprs_decode_message(packet_t pp)
|
||||
{
|
||||
// Get Info field
|
||||
char src[256];
|
||||
unsigned char *pinfo;
|
||||
ax25_get_info(pp, &pinfo);
|
||||
ax25_format_addrs(pp, src);
|
||||
|
||||
// Decode destination callsign
|
||||
char dest[10];
|
||||
uint8_t i=0;
|
||||
|
||||
while(i < sizeof(dest)-1) {
|
||||
if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') {
|
||||
dest[i++] = 0;
|
||||
break;
|
||||
}
|
||||
dest[i] = pinfo[i+1];
|
||||
i++;
|
||||
}
|
||||
|
||||
// Decode source callsign
|
||||
for(uint32_t i=0; i < sizeof(src); i++) {
|
||||
if(src[i] == '>') {
|
||||
src[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find out if this message is meant for us
|
||||
if(pinfo[10] == ':' && !strcmp(config[2].aprs_conf.callsign, dest))
|
||||
{
|
||||
char msg_id_rx[8];
|
||||
memset(msg_id_rx, 0, sizeof(msg_id_rx));
|
||||
|
||||
// Cut off control chars
|
||||
for(uint16_t i=11; pinfo[i] != 0 && i<0xFFFF; i++) {
|
||||
if(pinfo[i] == '{') {
|
||||
// Copy ACK ID
|
||||
memcpy(msg_id_rx, &pinfo[i+1], sizeof(msg_id_rx)-1);
|
||||
// Cut off non-printable chars
|
||||
for(uint8_t j=0; j<sizeof(msg_id_rx); j++) {
|
||||
if(msg_id_rx[j] < 32 || msg_id_rx[j] > 126) {
|
||||
msg_id_rx[j] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pinfo[i] = 0; // Mark end of message
|
||||
}
|
||||
if(pinfo[i] == '\r' || pinfo[i] == '\n') {
|
||||
pinfo[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Trace
|
||||
TRACE_INFO("APRS > Received message from %s (ID=%s): %s", src, msg_id_rx, &pinfo[11]);
|
||||
|
||||
// Do control actions
|
||||
if(!strcmp((char*)&pinfo[11], "?GPIO PA8:1")) { // Switch on pin
|
||||
|
||||
TRACE_INFO("Message: GPIO query PA8 HIGH");
|
||||
palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palSetPad(GPIOA, 8);
|
||||
|
||||
} else if(!strcmp((char*)&pinfo[11], "?GPIO PA8:0")) { // Switch off pin
|
||||
|
||||
TRACE_INFO("Message: GPIO query PA8 LOW");
|
||||
palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palClearPad(GPIOA, 8);
|
||||
|
||||
} else if(!strcmp((char*)&pinfo[11], "?APRSP")) { // Transmit position
|
||||
|
||||
TRACE_INFO("Message: Position query");
|
||||
trackPoint_t* trackPoint = getLastTrackPoint();
|
||||
packet_t pp = aprs_encode_position(&(config[0].aprs_conf), trackPoint);
|
||||
transmitOnRadio(pp, &(config[2].frequency), config[2].power, config[2].modulation);
|
||||
|
||||
} else if(!strcmp((char*)&pinfo[11], "?RESET")) { // Transmit position
|
||||
|
||||
TRACE_INFO("Message: System Reset");
|
||||
char buf[16];
|
||||
chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx);
|
||||
packet_t pp = aprs_encode_message(&(config[2].aprs_conf), src, buf, true);
|
||||
transmitOnRadio(pp, &(config[2].frequency), config[2].power, config[2].modulation);
|
||||
chThdSleep(TIME_S2I(2)); // Give some time to send the message
|
||||
|
||||
NVIC_SystemReset();
|
||||
|
||||
} else {
|
||||
TRACE_ERROR("Command Message not understood");
|
||||
}
|
||||
|
||||
if(msg_id_rx[0]) { // Message ID has been sent which has to be acknowledged
|
||||
char buf[16];
|
||||
chsnprintf(buf, sizeof(buf), "ack%s", msg_id_rx);
|
||||
packet_t pp = aprs_encode_message(&(config[2].aprs_conf), src, buf, true);
|
||||
transmitOnRadio(pp, &(config[2].frequency), config[2].power, config[2].modulation);
|
||||
}
|
||||
|
||||
return false; // Mark that message dont has to be digipeated
|
||||
}
|
||||
|
||||
return true; // Mark that message has to be digipeated
|
||||
}
|
||||
|
||||
static void aprs_digipeat(packet_t pp)
|
||||
{
|
||||
packet_t result = digipeat_match (0, pp, config[2].aprs_conf.callsign, config[2].aprs_conf.callsign, alias_re, wide_re, 0, preempt, NULL);
|
||||
if(result != NULL) {
|
||||
aprs_printPacket("TX ", result);
|
||||
|
||||
dedupe_remember(result, 0);
|
||||
transmitOnRadio(result, &(config[2].frequency), config[2].power, config[2].modulation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit APRS telemetry configuration
|
||||
*/
|
||||
packet_t aprs_encode_telemetry_configuration(const aprs_conf_t *config, uint8_t type)
|
||||
{
|
||||
char dest[16];
|
||||
chsnprintf(dest, sizeof(dest), "%s-%d", config->callsign, config->ssid);
|
||||
switch(type)
|
||||
{
|
||||
case 0: return aprs_encode_message(config, dest, "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", true);
|
||||
case 1: return aprs_encode_message(config, dest, "UNIT.V,V,W,degC,Pa", true);
|
||||
case 2: return aprs_encode_message(config, dest, "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", true);
|
||||
case 3: return aprs_encode_message(config, dest, "BITS.11111111,", true);
|
||||
case 0: return aprs_encode_message(config, config->callsign, "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", true);
|
||||
case 1: return aprs_encode_message(config, config->callsign, "UNIT.V,V,W,degC,Pa", true);
|
||||
case 2: return aprs_encode_message(config, config->callsign, "EQNS.0,.001,0,0,.001,0,0,.001,-4.096,0,.1,-100,0,12.5,500", true);
|
||||
case 3: return aprs_encode_message(config, config->callsign, "BITS.11111111,", true);
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void aprs_decode_packet(packet_t pp)
|
||||
{
|
||||
bool digipeat = true;
|
||||
|
||||
aprs_printPacket("RX ", pp);
|
||||
|
||||
unsigned char *pinfo;
|
||||
ax25_get_info(pp, &pinfo);
|
||||
if(pinfo[0] == ':')
|
||||
{
|
||||
digipeat = aprs_decode_message(pp);
|
||||
}
|
||||
|
||||
if(digipeat)
|
||||
aprs_digipeat(pp);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,5 +55,7 @@ packet_t aprs_encode_telemetry_configuration(const aprs_conf_t *config, uint8_t
|
|||
packet_t aprs_encode_message(const aprs_conf_t *config, const char *receiver, const char *text, const bool noCounter);
|
||||
packet_t aprs_encode_data_packet(char packetType, const aprs_conf_t *config, uint8_t *data);
|
||||
|
||||
void aprs_decode_packet(packet_t pp);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include "tracking.h"
|
||||
#include "debug.h"
|
||||
#include "radio.h"
|
||||
#include "geofence.h"
|
||||
#include "si446x.h"
|
||||
#include "aprs.h"
|
||||
|
||||
// Thread
|
||||
static thread_t* si446x_rx_thd = NULL;
|
||||
static THD_WORKING_AREA(si446x_rx_wa, 32*1024);
|
||||
#include "geofence.h"
|
||||
|
||||
static const char *getModulation(uint8_t key) {
|
||||
const char *val[] = {"unknown", "2FSK", "AFSK"};
|
||||
|
@ -54,302 +47,3 @@ bool transmitOnRadio(packet_t packet, freq_conf_t *freq_conf, uint8_t pwr, mod_t
|
|||
return true;
|
||||
}
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> /* for isdigit, isupper */
|
||||
|
||||
#include "pktconf.h"
|
||||
#include "dedupe.h"
|
||||
#include "digipeater.h"
|
||||
|
||||
static void printAPRSpacket(packet_t pp)
|
||||
{
|
||||
char buf[1024];
|
||||
uint32_t out;
|
||||
|
||||
// Decode packet
|
||||
char rec[1024];
|
||||
unsigned char *pinfo;
|
||||
ax25_format_addrs(pp, rec);
|
||||
ax25_get_info(pp, &pinfo);
|
||||
|
||||
// Print decoded packet
|
||||
out = chsnprintf(buf, sizeof(buf), "%s", rec);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t*)buf, out);
|
||||
for(uint32_t i=0; pinfo[i]; i++) {
|
||||
if(pinfo[i] < 32 || pinfo[i] > 126) {
|
||||
out = chsnprintf(buf, sizeof(buf), "<0x%02x>", pinfo[i]);
|
||||
} else {
|
||||
out = chsnprintf(buf, sizeof(buf), "%c", pinfo[i]);
|
||||
}
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t*)buf, out);
|
||||
}
|
||||
out = chsnprintf(buf, sizeof(buf), "\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t*)buf, out);
|
||||
}
|
||||
|
||||
THD_FUNCTION(si_receiver, arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
chRegSetThreadName("radio_receiver");
|
||||
|
||||
receiveAFSK(144800000, 0x3F);
|
||||
|
||||
char mycall[] = "DL7AD-12";
|
||||
char alias_re[] = "WIDE[4-7]-[1-7]|CITYD";
|
||||
char wide_re[] = "WIDE[1-7]-[1-7]";
|
||||
enum preempt_e preempt = PREEMPT_OFF;
|
||||
|
||||
/* Buffer and size params for serial terminal output. */
|
||||
char serial_buf[1024];
|
||||
int serial_out;
|
||||
|
||||
/*
|
||||
* Setup the parameters for the AFSK decoder thread.
|
||||
* TODO: Radio configuration to be implemented in pktOpenReceiveChannel().
|
||||
*/
|
||||
|
||||
radio_config_t afsk_radio = { PKT_RADIO_1 };
|
||||
|
||||
/* set packet instance assignment(s). */
|
||||
pktInitReceiveChannels();
|
||||
|
||||
packet_rx_t *packetHandler = pktOpenReceiveChannel(DECODE_AFSK, &afsk_radio);
|
||||
chDbgAssert(packetHandler != NULL, "invalid packet type");
|
||||
|
||||
thread_t *the_decoder = ((AFSKDemodDriver *)packetHandler->link_controller)->decoder_thd;
|
||||
chDbgAssert(the_decoder != NULL, "no decoder assigned");
|
||||
|
||||
event_source_t *events = pktGetEventSource(packetHandler);
|
||||
|
||||
/* Start the decoder. */
|
||||
msg_t pstart = pktStartDataReception(packetHandler);
|
||||
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Starting decoder: start status %i, event source @ %x\r\n", pstart, events);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
|
||||
/* Main loop. */
|
||||
while (true) {
|
||||
pkt_data_fifo_t *myPktFIFO = pktReceiveDataBufferTimeout(packetHandler, TIME_MS2I(1000));
|
||||
if(myPktFIFO == NULL) {
|
||||
continue;
|
||||
}
|
||||
/* Packet buffer sent via FIFO. */
|
||||
ax25char_t *frame_buffer = myPktFIFO->buffer;
|
||||
uint16_t frame_size = myPktFIFO->packet_size;
|
||||
eventmask_t the_events;
|
||||
packetHandler->frame_count++;
|
||||
if(pktIsBufferValidAX25Frame(myPktFIFO) == MSG_OK) {
|
||||
the_events = EVT_DIAG_OUT_END | EVT_PKT_OUT_END;
|
||||
uint16_t actualCRC = frame_buffer[frame_size - 2] | (frame_buffer[frame_size - 1] << 8);
|
||||
uint16_t computeCRC = calc_crc16(frame_buffer, 0, frame_size - 2);
|
||||
uint16_t magicCRC = calc_crc16(frame_buffer, 0, frame_size);
|
||||
if(magicCRC == CRC_INCLUSIVE_CONSTANT)
|
||||
packetHandler->valid_count++;
|
||||
float32_t good = (float32_t)packetHandler->valid_count / (float32_t)packetHandler->packet_count;
|
||||
/* Write out the buffer data.
|
||||
* TODO: Have a define to put diagnostic data into AX25 buffer object.
|
||||
*/
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf),
|
||||
"AFSK capture: status %x"
|
||||
", packet count %u frame count %u valid frames %u (%.2f%%) bytes %u"
|
||||
", CRCr %04x, CRCc %04x, CRCm %04x\r\n",
|
||||
myPktFIFO->status,
|
||||
packetHandler->packet_count,
|
||||
packetHandler->frame_count,
|
||||
packetHandler->valid_count,
|
||||
(good * 100),
|
||||
frame_size,
|
||||
actualCRC,
|
||||
computeCRC,
|
||||
magicCRC
|
||||
);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
|
||||
#define LINE_LENGTH 60U
|
||||
uint16_t bufpos;
|
||||
uint16_t bufpos_a = 0;
|
||||
/* Write out a buffer line as hex first. */
|
||||
for(bufpos = 0; bufpos < frame_size; bufpos++) {
|
||||
if((bufpos + 1) % LINE_LENGTH == 0) {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "%02x\r\n", frame_buffer[bufpos]);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
/* Write out full line of converted ASCII under hex.*/
|
||||
bufpos_a = (bufpos + 1) - LINE_LENGTH;
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a) || (asciichar > 0x2f && asciichar < 0x3a) || (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
if((bufpos_a + 1) % LINE_LENGTH == 0) {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), " %c\r\n", asciichar);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), " %c ", asciichar);
|
||||
}
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
} while(bufpos_a++ < bufpos);
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "%02x ", frame_buffer[bufpos]);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
} /* End for(bufpos = 0; bufpos < frame_size; bufpos++). */
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
/* Write out remaining partial line of converted ASCII under hex. */
|
||||
do {
|
||||
char asciichar = frame_buffer[bufpos_a];
|
||||
if(asciichar == 0x7e) {
|
||||
asciichar = '^';
|
||||
} else {
|
||||
asciichar >>= 1;
|
||||
if(!((asciichar >= 0x70 && asciichar < 0x7a) || (asciichar > 0x2f && asciichar < 0x3a) || (asciichar > 0x40 && asciichar < 0x5b))) {
|
||||
asciichar = 0x20;
|
||||
} else if(asciichar >= 0x70 && asciichar < 0x7a) {
|
||||
asciichar &= 0x3f;
|
||||
}
|
||||
}
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), " %c ", asciichar);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
} while(++bufpos_a < bufpos);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
|
||||
if(actualCRC == computeCRC) {
|
||||
|
||||
packet_t pp = ax25_from_frame(frame_buffer, bufpos-2);
|
||||
if(pp != NULL) {
|
||||
|
||||
printAPRSpacket(pp);
|
||||
|
||||
// Packet decoding
|
||||
unsigned char *pinfo;
|
||||
ax25_get_info(pp, &pinfo);
|
||||
if(pinfo[0] == ':')
|
||||
{
|
||||
// Decode destination callsign
|
||||
char dest[10];
|
||||
uint8_t i=0;
|
||||
while(i < sizeof(dest)-1) {
|
||||
if(pinfo[i+1] == ':' || pinfo[i+1] == ' ') {
|
||||
dest[i++] = 0;
|
||||
break;
|
||||
}
|
||||
dest[i] = pinfo[i+1];
|
||||
i++;
|
||||
}
|
||||
|
||||
if(pinfo[10] == ':' && !strcmp(mycall, dest))
|
||||
{
|
||||
// Cut off control chars
|
||||
for(uint16_t i=11; pinfo[i] != 0 && i<0xFFFF; i++) {
|
||||
if(pinfo[i] == '{' || pinfo[i] == '\r' || pinfo[i] == '\n') {
|
||||
pinfo[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Do control
|
||||
TRACE_DEBUG("Received message: %s", &pinfo[11]);
|
||||
if(!strcmp((char*)&pinfo[11], "?GPIO PA8:1")) {
|
||||
// Switch on pin
|
||||
palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palSetPad(GPIOA, 8);
|
||||
} else if(!strcmp((char*)&pinfo[11], "?GPIO PA8:0")) {
|
||||
// Switch off pin
|
||||
palSetPadMode(GPIOA, 8, PAL_MODE_OUTPUT_PUSHPULL);
|
||||
palClearPad(GPIOA, 8);
|
||||
} else if(!strcmp((char*)&pinfo[11], "?APRSP")) {
|
||||
// Encode and transmit position packet
|
||||
trackPoint_t* trackPoint = getLastTrackPoint();
|
||||
packet_t packet = aprs_encode_position(&(config[0].aprs_conf), trackPoint); // Encode packet
|
||||
transmitOnRadio(packet, &(config[0].frequency), config[0].power, config[0].modulation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Try to digipeat
|
||||
packet_t result = digipeat_match (0, pp, mycall, mycall, alias_re, wide_re, 0, preempt, NULL);
|
||||
ax25_delete(pp);
|
||||
|
||||
if (result != NULL) {
|
||||
|
||||
TRACE_DEBUG("Digipeat\n");
|
||||
printAPRSpacket(result);
|
||||
|
||||
dedupe_remember(result, 0);
|
||||
transmitOnRadio(result, &(config[0].frequency), config[0].power, config[0].modulation);
|
||||
|
||||
ax25_delete(result);
|
||||
|
||||
} else {
|
||||
TRACE_DEBUG("No Digipeat\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Error in packet\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
|
||||
} else {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Bad CRC\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
|
||||
} else {/* End if valid frame. */
|
||||
the_events = EVT_DIAG_OUT_END;
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Invalid frame, status %x, bytes %u\r\n", myPktFIFO->status, myPktFIFO->packet_size);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
|
||||
#if SUSPEND_HANDLING == RELEASE_ON_OUTPUT
|
||||
/*
|
||||
* Wait for end of transmission on diagnostic channel.
|
||||
*/
|
||||
eventmask_t evt = chEvtWaitAllTimeout(the_events, TIME_S2I(10));
|
||||
if (!evt) {
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "FAIL: Timeout waiting for EOT from serial channels\r\n");
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
chEvtSignal(the_decoder, EVT_SUSPEND_EXIT);
|
||||
#else
|
||||
(void)the_events;
|
||||
#endif
|
||||
pktReleaseDataBuffer(packetHandler, myPktFIFO);
|
||||
if(packetHandler->packet_count % 100 == 0 && packetHandler->packet_count != 0) {
|
||||
/* Stop the decoder. */
|
||||
msg_t pmsg = pktStopDataReception(packetHandler);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Decoder STOP %i\r\n", pmsg);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
if(packetHandler->packet_count % 1000 == 0 && packetHandler->packet_count != 0) {
|
||||
chThdSleep(TIME_S2I(5));
|
||||
pmsg = pktCloseReceiveChannel(packetHandler);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Decoder CLOSE %i\r\n", pmsg);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
chThdSleep(TIME_S2I(5));
|
||||
packetHandler = pktOpenReceiveChannel(DECODE_AFSK, &afsk_radio);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Decoder OPEN %x\r\n", packetHandler);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
chThdSleep(TIME_S2I(5));
|
||||
pmsg = pktStartDataReception(packetHandler);
|
||||
serial_out = chsnprintf(serial_buf, sizeof(serial_buf), "Decoder START %i\r\n", pmsg);
|
||||
chnWrite((BaseSequentialStream*)&SDU1, (uint8_t *)serial_buf, serial_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startReceiver(void)
|
||||
{
|
||||
if(si446x_rx_thd == NULL)
|
||||
si446x_rx_thd = chThdCreateStatic(si446x_rx_wa, sizeof(si446x_rx_wa), HIGHPRIO, si_receiver, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "watchdog.h"
|
||||
#include "pi2c.h"
|
||||
#include "pac1720.h"
|
||||
#include "si446x.h"
|
||||
|
||||
sysinterval_t watchdog_tracking;
|
||||
|
||||
|
@ -13,7 +14,7 @@ void start_essential_threads(void) {
|
|||
pi2cInit(); // Initialize I2C
|
||||
pac1720_init(); // Initialize current measurement
|
||||
init_tracking_manager(false); // Initialize tracking manager (without GPS, GPS is initialized if needed by position thread)
|
||||
startReceiver(); // Start APRS receiver
|
||||
receiveAFSK(144800000, 0x3F); // Start APRS receiver TODO: Implement frequency changes
|
||||
chThdSleep(TIME_MS2I(300)); // Wait for tracking manager to initialize
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
#define __TYPES_H__
|
||||
|
||||
typedef struct {
|
||||
char callsign[16]; // APRS callsign
|
||||
uint8_t ssid; // APRS SSID
|
||||
char callsign[10]; // APRS callsign
|
||||
uint16_t symbol; // APRS symbol
|
||||
char path[16]; // APRS path
|
||||
uint16_t preamble; // Preamble in milliseconds
|
||||
|
@ -95,7 +94,7 @@ typedef struct {
|
|||
} trigger_conf_t;
|
||||
|
||||
typedef struct {
|
||||
char name[32];
|
||||
char name[10];
|
||||
|
||||
// Radio
|
||||
int8_t power;
|
||||
|
|
Ładowanie…
Reference in New Issue