Added ?APRSP, ?GPIO and ?RESET queries

Reorganized APRS messaging
Updated APRS demodulation library
pull/1/head
Sven Steudte 2018-01-30 10:24:51 +01:00
rodzic 192a930992
commit 974f3a74d7
24 zmienionych plików z 1003 dodań i 619 usunięć

Wyświetl plik

@ -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]);
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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;

Wyświetl plik

@ -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. */
/*===========================================================================*/

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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. */

Wyświetl plik

@ -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 */
/** @} */

Wyświetl plik

@ -21,7 +21,8 @@
/* External declarations. */
/*===========================================================================*/
extern BaseSequentialStream* diag_out;
extern BaseSequentialStream* pkt_out;
extern const SerialConfig debug_config;

Wyświetl plik

@ -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,

Wyświetl plik

@ -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_ */

Wyświetl plik

@ -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. */
}
/** @} */

Wyświetl plik

@ -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_ */
/** @} */

Wyświetl plik

@ -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

Wyświetl plik

@ -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. */

Wyświetl plik

@ -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. */
/*===========================================================================*/

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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;