Changed configuration concept

pull/1/head
Sven Steudte 2018-02-07 10:26:23 +01:00
rodzic 71b0b47f25
commit e7c3dfc807
40 zmienionych plików z 657 dodań i 868 usunięć

Wyświetl plik

@ -66,7 +66,7 @@ endif
# Stack size to the allocated to the Cortex-M main/exceptions stack. This
# stack is used for processing interrupts and exceptions.
ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
USE_EXCEPTIONS_STACKSIZE = 0x1400
USE_EXCEPTIONS_STACKSIZE = 0x1000
endif
# Enables the use of FPU (no, softfp, hard).

Wyświetl plik

@ -304,6 +304,13 @@
// Misc
#define LINE_TCXO_EN PAL_LINE(GPIOC, 13U)
// Hardware dependent settings
#define Si446x_MIN_FREQ 144000000 /* Minimum allowed frequency in Hz */
#define Si446x_MAX_FREQ 148000000 /* Maximum allowed frequency in Hz */
#define Si446x_CLK STM32_HSECLK /* Oscillator frequency in Hz */
#define Si446x_CLK_OFFSET 22 /* Oscillator frequency drift in ppm */
#define Si446x_CLK_TCXO_EN true /* Set this true, if a TCXO is used, false for XTAL */
/*

Wyświetl plik

@ -1,472 +1,133 @@
/*
* Position module configuration description
* =========================================
*
* This module activates the position transmission of the tracker. If one of the position modules activated, it will activate the tracking manager which
* samples GPS position and atmospheric data. If you want to save position data to the flash memory, you have to enable at lease one position module. You can
* transmit positions either using 2FSK (RTTY) with the UKHAS standard, CW (Morse) or APRS (AFSK or 2FSK).
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
* applies:
* 20 dBm => 127
* 15 dBm => 40
* 10 dBm => 20
* 5 dBm => 12
* 0 dBm => 8
*
* protocol prot_t Possible Options:
* (required) - PROT_APRS_AFSK FM AFSK 1200baud transmission using the APRS protocol
* this option requires aprs_conf to be set
* - PROT_APRS_2FSK 2FSK transmission using the APRS protocol
* this option requires aprs_conf and fsk_conf to be set
* - PROT_UKHAS_2FSK Comma separated 2FSK transmission (RTTY) using the UKHAS standard
* this option requires ukhas_conf and fsk_conf to be set
* - PROT_MORSE Morse transmission
* this option requires morse_conf and ook_conf to be set
*
* frequency.type freq_type_t Defines the frequency type. This option will be FREQ_STATIC if not set.
* (default FREQ_STATIC) Possible Options:
* - FREQ_STATIC Static frequency taken from frequency.hz
* - FREQ_APRS_REGION Using the APRS region frequency. The tracker will change its frequency specificly to the region
* where it is located. e.g. 144.8MHz in Europe or 144.39MHz in the US. If the tracker doesnt know its
* position it takes its frequency from frequency.hz as default. Note that the tracker knows its
* position from its position log too. So it might use the last frequency which has been used before
* resetting it.
*
* frequency.hz int Frequency that this Module will transmit on (in Hz). The tracker can transmit in the 2m band. This value will be used
* (required) as default when frequency.type == FREQ_APRS_REGION and it doesnt know its position
*
* init_delay int Initial delay (in ms) before the module starts. This might be useful if you dont want to transmit so many APRS packets
* (default 0ms) at the same time on the APRS network. This option is optional. It will be 0ms if not set.
*
* trigger.type trigger_type_t Event at which this module is triggered to transmit. This option will be TRIG_ONCE if not set.
* (default TRIG_ONCE) Possible options:
* - TRIG_ONCE Trigger once and never again (e.g. transmit specific position packet only at startup)
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* ============================== The following options are needed if protocol == PROT_APRS_AFSK or protocol == PROT_APRS_2FSK ================================
*
* aprs_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the APRS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* aprs_conf.ssid int(0-15) APRS SSID (no SSID = 0)
* (default 0)
*
* aprs_conf.symbol int APRS Symbol (according to this table http://www.aprs.org/symbols.html)
* (required) Possible Options: SYM_BALLOON, SYM_SMALLAIRCRAFT, SYM_SATELLITE
*
* aprs_conf.path string APRS digipeating path (default: no digipeating)
* (optional)
*
* aprs_conf.preamble int AFSK or 2FSK preamble length (in ms). This value is required while its default is 0ms (and this would simply not work ;-) )
* (required)
*
* aprs_conf.tel[0-4] telemetry_t There are numerous telemetry values which can be sent in the APRS position packet. One packet can contain 5 values.
* (required) There are possible options:
* - TEL_SATS GPS Satellites
* - TEL_TTFF Time to first fix (amount of seconds which it needed to aquire a GPS fix)
* - TEL_VBAT Battery voltage (in mV)
* - TEL_VSOL Solar voltage (in mV)
* - TEL_PBAT Battery power (positive charge, negative discharge)
* - TEL_RBAT Battery impedance (in mOhm)
* - TEL_PRESS Air pressure (in Pa)
* - TEL_TEMP Air temperature (in degC*100)
* - TEL_HUM Air humidity (in %*10)
*
* aprs_conf.tel_enc bool The telemetry in the position packets do only contain the raw values. Receivers (like aprs.fi) dont know what these
* (default false) values stands for. So we must tell them (e.g. that value 1 is air pressure measured in Pascal). If set to true, the
* tracker will transmit additional packets containing these informations. This option requires aprs_conf.tel_enc_cycle.
*
* aprs_conf.tel_enc_cycle int This values defines how often telemetry configuration packets are sent. Transmitting those packets every two
* (default 0sec) hours (7200sec) is a good value. This value is set in seconds.
*
* aprs_conf.tel_comment string There can be set a short comment which is sent with the APRS configuration packets. It has no affect if
* (optional) aprs_conf.tel_enc is set false. Its default is empty.
*
* ============================================= The following options are needed if protocol == PROT_APRS_2FSK ===============================================
*
* fsk_conf.speed int 2FSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*
* ============================================= The following options are needed if protocol == PROT_UKHAS_2FSK ==============================================
*
* fsk_conf.bits int(7-8) Bits
* (required)
*
* fsk_conf.stopbits int(1-2) Stopbits
* (required)
*
* fsk_conf.predelay int Predelay (in ms). The receiver needs to settle on the frequency for a while. Therefore is switched on some seconds
* (default 0ms) before. By default its 0ms but that wouldnt work.
*
* fsk_conf.baud int Baudrate. Following values have been tested successfully: 50, 300, 600.
* (required)
*
* fsk_conf.shift int Frequency shift of 2FSK
* (required)
*
* ukhas_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the UKHAS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* ukhas_conf.format string UKHAS format. This is the string which will be sent. This string contains variables (e.g. <VBAT>) which will be
* (required) replaced by the actual values.
* For example: "<CALL>,<ID>,<TIME>,<LAT>,<LON>,<ALT>" will become "MYCALL,324,14:23:35,52.4537,13.9362,8462"
* The prefix "$$$$$" and CRC will be added automatically. Following variables can be used:
* - <ID> Incremental ID
* - <DATE> Date
* - <TIME> Time
* - <LAT> Latitude
* - <LON> Longitude
* - <ALT> Altitude (in meter!)
* - <SATS> GPS Satellites
* - <TTFF> Time to first fix
* - <VBAT> Battery voltage
* - <VSOL> Solar voltage
* - <PBAT> Battery power consumption
* - <ISOL> Solar short current
* - <PRESS> Air pressure
* - <TEMP> Air temperature
* - <HUM> Air humidity
* - <LOC> Maidenhead locator
*
* ================================================ The following options are needed if protocol == PROT_MORSE ================================================
*
* ook_conf.speed int Morse Speed in Wpm
* (required)
*
* morse_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the morse "protocol".
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* morse_conf.format string Morse format. This is the string which will be sent. This string contains variables (e.g. <VBAT>) which will be
* (required) replaced by the actual values.
* For example: "BALLOON <CALL> <LOC> <ALT>M" will become "BALLOON MYCALL JO62CF 9362M"
* The following variables can be used:
* - <ID> Incremental ID
* - <DATE> Date
* - <TIME> Time
* - <LAT> Latitude
* - <LON> Longitude
* - <ALT> Altitude (in meter!)
* - <SATS> GPS Satellites
* - <TTFF> Time to first fix
* - <VBAT> Battery voltage
* - <VSOL> Solar voltage
* - <PBAT> Battery power consumption
* - <ISOL> Solar short current
* - <PRESS> Air pressure
* - <TEMP> Air temperature
* - <HUM> Air humidity
* - <LOC> Maidenhead locator
*/
/*
* Image module configuration description
* ======================================
*
* This module activates the transmission of pictures from the camera. You can use either SSDV transsmitted with 2FSK (RTTY) or APRS/SSDV. If you choose
* APRS/SSDV, generic APRS packets will be transmitted which can be received by any APRS receiver and decoded with a computer. In order to decode 2FSK/SSDV
* please use DL-FlDigi (https://ukhas.org.uk/projects:dl-fldigi). 2FSK/SSDV is very stable even with low receiption signals but it needs a SSB receiver. If
* you want to use APRS/SSDV you are going to need a packet receiver (like TH-D72, TH-D74 or FT2D or generic TNC). The packets can be decoded by a python
* script in the /decoder folder of this repository.
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
* applies:
* 20 dBm => 127
* 15 dBm => 40
* 10 dBm => 20
* 5 dBm => 12
* 0 dBm => 8
*
* protocol prot_t Possible Options:
* (required) - PROT_APRS_AFSK FM AFSK 1200baud transmission using the APRS/SSDV protocol
* this option requires aprs_conf to be set
* - PROT_APRS_2FSK 2FSK transmission using the APRS/SSDV protocol
* this option requires aprs_conf and fsk_conf to be set
* - PROT_SSDV_2FSK 2FSK transmission using the SSDV protocol
* this option requires fsk_conf to be set
*
* frequency.type freq_type_t Defines the frequency type. This option will be FREQ_STATIC if not set.
* (default FREQ_STATIC) Possible Options:
* - FREQ_STATIC Static frequency taken from frequency.hz
* - FREQ_APRS_REGION Using the APRS region frequency. The tracker will change its frequency specificly to the region
* where it is located. e.g. 144.8MHz in Europe or 144.39MHz in the US. If the tracker doesnt know its
* position it takes its frequency from frequency.hz as default. Note that the tracker knows its
* position from its position log too. So it might use the last frequency which has been used before
* resetting it.
*
* frequency.hz int Frequency that this Module will transmit on (in Hz). The tracker can transmit in the 2m band. This value will be used
* (required) as default when frequency.type == FREQ_APRS_REGION and it doesnt know its position
*
* init_delay int Initial delay (in ms) before the module starts. This might be useful if you dont want to transmit so many APRS packets
* (default 0ms) at the same time on the APRS network. This option is optional. It will be 0ms if not set.
*
* trigger.type trigger_type_t Event at which this module is triggered to transmit. This option will be TRIG_ONCE if not set.
* (default TRIG_ONCE) Possible options:
* - TRIG_ONCE Trigger once and never again (e.g. transmit specific position packet only at startup)
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* ssdv_conf.callsign string The SSDV callsign (or stream identifier). This value helps the SSDV algorithm to assign packets from different images
* (required) to the right data set. This is helpful if multiple modules transmit different images at the same time.
*
* ssdv_conf.ram_buffer data Array of bytes which is used by the module for buffering the image
* (required)
*
* ssdv_conf.ram_size Size of buffer => sizeof(ssdv_conf.ram_buffer)
* (required)
*
* ssdv_conf.res resolution_t Resolution of the image
* (default RES_QVGA) Possible options:
* - RES_QVGA QVGA Resolution (320x240px)
* - RES_VGA VGA Resolution (640x480px)
* - RES_XGA XGA Resolution (1204x768px)
* - RES_UXGA UXGA Resolution (1600x1200px)
* - RES_MAX The module samples the highest resolution which fits into ssdv_conf.ram_buffer.
*
* ssdv_conf.redundantTx bool Enables redudant packet transmission if set to true. This option will enable the packets to be transmitted twice.
*
* ssdv_conf.quality int(0-7) Quality (quantization) of the JPEG algorithm. It can be set from 0 (low quality) to 7 (high quality). (Recommended: 4)
*
* ============================== The following options are needed if protocol == PROT_APRS_AFSK or protocol == PROT_APRS_2FSK ================================
*
* aprs_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the APRS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* aprs_conf.ssid int(0-15) APRS SSID (no SSID = 0)
* (default 0)
*
* aprs_conf.path string APRS digipeating path (default: no digipeating)
* (optional)
*
* aprs_conf.preamble int AFSK or 2FSK preamble length (in ms). This value is required while its default is 0ms (and this would simply not work ;-) )
* (required)
*
* ============================================= The following options are needed if protocol == PROT_APRS_2FSK ===============================================
*
* fsk_conf.speed int 2FSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*
* ============================================== The following options are needed if protocol == PROT_SSDV_2FSK ==============================================
*
* fsk_conf.bits int(7-8) Bits
* (required)
*
* fsk_conf.stopbits int(1-2) Stopbits (Important: DL-FlDigi can decode SSDV only using 2 stopbits!)
* (required)
*
* fsk_conf.predelay int Predelay (in ms). The receiver needs to settle on the frequency for a while. Therefore is switched on some seconds
* (default 0ms) before. By default its 0ms but that wouldnt work.
*
* fsk_conf.baud int Baudrate. Following values have been tested successfully: 50, 300, 600. (Recommended: 300 or 600)
* (required)
*
* fsk_conf.shift int Frequency shift of 2FSK
* (required)
*/
/*
* Log module configuration description
* ====================================
*
* This module activates log (track point) transmission over APRS (AFSK or 2FSK). This module can be used to receive log points while the tracker has been out
* of range from the APRS network.
* Note: Track points are saved on the flash memory without activation of the Log module. They are just not sent out.
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
* applies:
* 20 dBm => 127
* 15 dBm => 40
* 10 dBm => 20
* 5 dBm => 12
* 0 dBm => 8
*
* protocol prot_t Possible Options:
* (required) - PROT_APRS_AFSK FM AFSK 1200baud transmission using the APRS protocol
* this option requires aprs_conf to be set
* - PROT_APRS_2FSK 2FSK transmission using the APRS protocol
* this option requires aprs_conf and fsk_conf to be set
*
* frequency.type freq_type_t Defines the frequency type. This option will be FREQ_STATIC if not set.
* (default FREQ_STATIC) Possible Options:
* - FREQ_STATIC Static frequency taken from frequency.hz
* - FREQ_APRS_REGION Using the APRS region frequency. The tracker will change its frequency specificly to the region
* where it is located. e.g. 144.8MHz in Europe or 144.39MHz in the US. If the tracker doesnt know its
* position it takes its frequency from frequency.hz as default. Note that the tracker knows its
* position from its position log too. So it might use the last frequency which has been used before
* resetting it.
*
* frequency.hz int Frequency that this Module will transmit on (in Hz). The tracker can transmit in the 2m band. This value will be used
* (required) as default when frequency.type == FREQ_APRS_REGION and it doesnt know its position
*
* init_delay int Initial delay (in ms) before the module starts. This might be useful if you dont want to transmit so many APRS packets
* (default 0ms) at the same time on the APRS network. This option is optional. It will be 0ms if not set.
*
* trigger.type trigger_type_t Event at which this module is triggered to transmit. This option will be TRIG_ONCE if not set.
* (default TRIG_ONCE) Possible options:
* - TRIG_ONCE Trigger once and never again (e.g. transmit specific position packet only at startup)
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* aprs_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the APRS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* aprs_conf.ssid int(0-15) APRS SSID (no SSID = 0)
* (default 0)
*
* aprs_conf.path string APRS digipeating path (default: no digipeating)
* (optional)
*
* aprs_conf.preamble int AFSK or 2FSK preamble length (in ms). This value is required while its default is 0ms (and this would simply not work ;-) )
* (required)
*
* ============================================= The following options are needed if protocol == PROT_APRS_2FSK ===============================================
*
* fsk_conf.speed int 2FSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*/
// Put your configuration settings here
#include "config.h"
#include "aprs.h"
#include "image.h"
#include "position.h"
#include "log.h"
#include "chprintf.h"
// Global variables
sysinterval_t track_cycle_time = TIME_S2I(600); // Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each 60 seconds
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 = 1000; // Battery voltage threshold at which GPS is switched on
uint16_t gps_off_vbat = 1000; // 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
// than gps_on_vbat and gps_off_vbat otherwise this value has no effect. Value 0 disables this feature
conf_t config = {
// Primary position transmission thread
.pos_pri = {
.thread_conf = {
.active = true,
.cycle = TIME_S2I(120)
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
.call = "DL7AD-12",
.path = "WIDE1-1",
.symbol = SYM_BALLOON,
module_conf_t config[7];
uint8_t ssdv_buffer[128*1024] __attribute__((aligned(32))); // Image buffer
.tel_enc_cycle = TIME_S2I(3600),
.tel_comment = "Hello World!"
},
void start_user_modules(void)
{
/*
* Use one of these example blocks by uncommenting the start_*_thread() line.
*/
// Secondary position transmission thread
.pos_sec = {
.thread_conf = {
.active = false,
.cycle = TIME_S2I(120)
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
/* -------------------------------------------------- POSITION TRANSMISSION -------------------------------------------------- */
.call = "DL7AD-12",
.path = "WIDE1-1",
.symbol = SYM_BALLOON,
// 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-13"); // 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-13"); // Telemetry comment
start_position_thread(&config[0]);
.tel_enc_cycle = TIME_S2I(3600),
.tel_comment = "Hello World!"
},
// 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-13"); // 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-13"); // Telemetry comment
//start_position_thread(&config[1]);
// Primary image transmission thread
.img_pri = {
.thread_conf = {
.active = false,
.cycle = CYCLE_CONTINUOUSLY
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
// 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-13"); // 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]);
.call = "DL7AD-12",
.path = "",
.res = RES_QVGA,
.quality = 4,
.buf_size = 32*1024
},
/* ---------------------------------------------------- IMAGE TRANSMISSION --------------------------------------------------- */
// Secondary image transmission thread
.img_sec = {
.thread_conf = {
.active = false,
.cycle = CYCLE_CONTINUOUSLY
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
// 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-13"); // 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]);
.call = "DL7AD-14",
.path = "",
// 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-13"); // 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]);
.res = RES_VGA,
.quality = 4,
.buf_size = 64*1024
},
// Log transmission thread
.log = {
.thread_conf = {
.active = false,
.cycle = TIME_S2I(120)
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
.call = "DL7AD-12",
.path = "WIDE1-1",
},
.rx = {
.thread_conf = {
.active = false
},
.radio_conf = {
.pwr = 0x7F,
.freq = FREQ_APRS_DYNAMIC,
.mod = MOD_AFSK,
.preamble = 200
},
/* ----------------------------------------------------- LOG TRANSMISSION ---------------------------------------------------- */
.call = "DL7AD-14",
.path = "WIDE1-1",
.symbol = SYM_BALLOON
},
// 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-13"); // APRS Callsign
config[6].aprs_conf.preamble = 200; // APRS Preamble (200ms)
//start_logging_thread(&config[6]);
}
.rssi = 0x4F,
.dig_active = false,
.keep_cam_switched_on = false,
.gps_on_vbat = 5000,
.gps_off_vbat = 5000,
.gps_onper_vbat = 5000
};

Wyświetl plik

@ -13,20 +13,9 @@
* too when operating at 3V. This option will also run the STM32 at 48MHz (AHB) permanently
* because USB needs that speed, otherwise it is running at 6MHz which saves a lot of power. */
#include "ch.h"
#include "types.h"
#include "radio.h"
void start_user_modules(void);
extern module_conf_t config[7];
extern sysinterval_t track_cycle_time;
extern sysinterval_t log_cycle_time;
extern bool keep_cam_switched_on;
extern uint16_t gps_on_vbat;
extern uint16_t gps_off_vbat;
extern uint16_t gps_onper_vbat;
extern conf_t config;
#endif

Wyświetl plik

@ -9,6 +9,7 @@
#include "ov5640.h"
#include "geofence.h"
#include "aprs.h"
#include "radio.h"
const SerialConfig uart_config =
{
@ -35,35 +36,30 @@ void debugOnUSB(BaseSequentialStream *chp, int argc, char *argv[])
}
static uint8_t usb_buffer[16*1024] __attribute__((aligned(32))); // USB image buffer
void printPicture(BaseSequentialStream *chp, int argc, char *argv[])
{
(void)argc;
(void)argv;
// Take picture
ssdv_conf_t conf = {
.res = RES_QVGA,
.quality = 4,
.ram_buffer = usb_buffer,
.ram_size = sizeof(usb_buffer),
};
bool camera_found = takePicture(&conf, false);
uint32_t size_sampled = takePicture(usb_buffer, sizeof(usb_buffer), RES_QVGA, false);
// Transmit image via USB
if(camera_found)
if(size_sampled)
{
bool start_detected = false;
for(uint32_t i=0; i<conf.size_sampled; i++)
for(uint32_t i=0; i<size_sampled; i++)
{
// Look for APP0 instead of SOI because SOI is lost sometimes, but we can add SOI easily later on
if(!start_detected && conf.ram_buffer[i] == 0xFF && conf.ram_buffer[i+1] == 0xE0) {
if(!start_detected && usb_buffer[i] == 0xFF && usb_buffer[i+1] == 0xE0) {
start_detected = true;
TRACE_USB("DATA > image/jpeg,%d", conf.size_sampled-i+2); // Flag the data on serial output
TRACE_USB("DATA > image/jpeg,%d", size_sampled-i+2); // Flag the data on serial output
streamPut(chp, 0xFF);
streamPut(chp, 0xD8);
}
if(start_detected)
streamPut(chp, conf.ram_buffer[i]);
streamPut(chp, usb_buffer[i]);
}
if(!start_detected)
{
@ -71,7 +67,7 @@ void printPicture(BaseSequentialStream *chp, int argc, char *argv[])
TRACE_USB("DATA > text/trace,no SOI flag found");
}
} else { // No camera found
} else { // Camera error
TRACE_USB("DATA > image,jpeg,0");
TRACE_USB("DATA > error,no camera found");
@ -110,7 +106,7 @@ void readLog(BaseSequentialStream *chp, int argc, char *argv[])
void printConfig(BaseSequentialStream *chp, int argc, char *argv[])
{
if(argc < 1)
/* if(argc < 1)
{
chprintf(chp, "Argument missing!\r\n");
chprintf(chp, "Argument 1: Id of config\r\n");
@ -145,7 +141,11 @@ void printConfig(BaseSequentialStream *chp, int argc, char *argv[])
chprintf(chp, "SSDV config: xx\r\n");
chprintf(chp, "Watchdog timeout: %d\r\n", config[id].wdg_timeout);
chprintf(chp, "Watchdog timeout: %d\r\n", config[id].wdg_timeout);*/
(void)argc;
(void)argv;
chprintf(chp, "TODO: Not implemented\r\n");
}
void send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[])
@ -160,18 +160,9 @@ 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(&(config[2].aprs_conf), argv[0], argv[1], false);
transmitOnRadio(packet, &(config[2].frequency), 127, MOD_AFSK);
packet_t packet = aprs_encode_message(config.rx.call, config.rx.path, argv[0], argv[1], false);
transmitOnRadio(packet, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
chprintf(chp, "Message sent!\r\n");
}
void test_rx(BaseSequentialStream *chp, int argc, char *argv[])
{
(void)chp;
(void)argc;
(void)argv;
receiveAFSK(config[2].frequency.hz, 0x4F);
}

Wyświetl plik

@ -86,7 +86,6 @@ void printPicture(BaseSequentialStream *chp, int argc, char *argv[]);
void readLog(BaseSequentialStream *chp, int argc, char *argv[]);
void command2Camera(BaseSequentialStream *chp, int argc, char *argv[]);
void send_aprs_message(BaseSequentialStream *chp, int argc, char *argv[]);
void test_rx(BaseSequentialStream *chp, int argc, char *argv[]);
#endif

Wyświetl plik

@ -58,7 +58,17 @@ int16_t pac1720_get_pbat(void) {
}
int16_t pac1720_get_psol(void) {
return 0;
int32_t fsp = FSV * FSC;
int16_t val;
uint8_t sign;
if(I2C_read16(PAC1720_ADDRESS, PAC1720_CH1_PWR_RAT_HIGH, (uint16_t*)&val)) {
I2C_read8(PAC1720_ADDRESS, PAC1720_CH1_VSENSE_HIGH, &sign);
return (sign >> 7 ? -1 : 1) * 10 * (val * fsp / 65535);
} else {
error |= 0x1;
return 0; // PAC1720 not available (maybe Vcc too low)
}
}
uint16_t pac1720_get_vbat(void) {
@ -154,6 +164,7 @@ THD_FUNCTION(pac1720_thd, arg)
pac1720_vsol += pac1720_get_vsol();
pac1720_pbat += pac1720_get_pbat();
pac1720_psol += pac1720_get_psol();
pac1720_counter++;
pac1720_unlock();

Wyświetl plik

@ -161,11 +161,11 @@ static void Si446x_init(void) {
chThdSleep(TIME_MS2I(10)); // Wait for transmitter to power up
// Power up (transmits oscillator type)
const uint8_t x3 = (Si446x_CLK >> 24) & 0x0FF;
const uint8_t x2 = (Si446x_CLK >> 16) & 0x0FF;
const uint8_t x1 = (Si446x_CLK >> 8) & 0x0FF;
const uint8_t x0 = (Si446x_CLK >> 0) & 0x0FF;
const uint8_t init_command[] = {0x02, 0x01, (Si446x_TCXO_EN & 0x1), x3, x2, x1, x0};
const uint8_t x3 = (Si446x_CCLK >> 24) & 0x0FF;
const uint8_t x2 = (Si446x_CCLK >> 16) & 0x0FF;
const uint8_t x1 = (Si446x_CCLK >> 8) & 0x0FF;
const uint8_t x0 = (Si446x_CCLK >> 0) & 0x0FF;
const uint8_t init_command[] = {0x02, 0x01, (Si446x_CLK_TCXO_EN & 0x1), x3, x2, x1, x0};
Si446x_write(init_command, 7);
chThdSleep(TIME_MS2I(25));
@ -183,7 +183,7 @@ static void Si446x_init(void) {
Si446x_write(gpio_pin_cfg_command, 8);
chThdSleep(TIME_MS2I(25));
#if !Si446x_TCXO_EN
#if !Si446x_CLK_TCXO_EN
Si446x_setProperty8(Si446x_GLOBAL_XO_TUNE, 0x00);
#endif
@ -437,7 +437,7 @@ static void Si446x_setFrequency(uint32_t freq)
Si446x_write(set_band_property_command, 5);
// Set the PLL parameters
uint32_t f_pfd = 2 * Si446x_CLK / outdiv;
uint32_t f_pfd = 2 * Si446x_CCLK / outdiv;
uint32_t n = ((uint32_t)(freq / f_pfd)) - 1;
float ratio = (float)freq / (float)f_pfd;
float rest = ratio - (float)n;
@ -447,14 +447,14 @@ static void Si446x_setFrequency(uint32_t freq)
uint32_t m1 = (m - m2 * 0x10000) >> 8;
uint32_t m0 = (m - m2 * 0x10000 - (m1 << 8));
uint32_t channel_increment = 524288 * outdiv * shift / (2 * Si446x_CLK);
uint32_t channel_increment = 524288 * outdiv * shift / (2 * Si446x_CCLK);
uint8_t c1 = channel_increment / 0x100;
uint8_t c0 = channel_increment - (0x100 * c1);
uint8_t set_frequency_property_command[] = {0x11, 0x40, 0x04, 0x00, n, m2, m1, m0, c1, c0};
Si446x_write(set_frequency_property_command, 10);
uint32_t x = ((((uint32_t)1 << 19) * outdiv * 1300.0)/(2*Si446x_CLK))*2;
uint32_t x = ((((uint32_t)1 << 19) * outdiv * 1300.0)/(2*Si446x_CCLK))*2;
uint8_t x2 = (x >> 16) & 0xFF;
uint8_t x1 = (x >> 8) & 0xFF;
uint8_t x0 = (x >> 0) & 0xFF;
@ -467,7 +467,7 @@ static void Si446x_setFrequency(uint32_t freq)
if(!shift)
return;
float units_per_hz = (( 0x40000 * outdiv ) / (float)Si446x_CLK);
float units_per_hz = (( 0x40000 * outdiv ) / (float)Si446x_CCLK);
// Set deviation for 2FSK
uint32_t modem_freq_dev = (uint32_t)(units_per_hz * shift / 2.0 );
@ -493,7 +493,7 @@ static void Si446x_setPowerLevel(int8_t level)
static void Si446x_setModemAFSK_TX(void)
{
// Setup the NCO modulo and oversampling mode
uint32_t s = Si446x_CLK / 10;
uint32_t s = Si446x_CCLK / 10;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
@ -518,7 +518,7 @@ static void Si446x_setModemAFSK_TX(void)
static void Si446x_setModemAFSK_RX(void)
{
// Setup the NCO modulo and oversampling mode
uint32_t s = Si446x_CLK;
uint32_t s = Si446x_CCLK;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
@ -573,7 +573,7 @@ static void Si446x_setModemAFSK_RX(void)
static void Si446x_setModem2FSK(uint32_t speed)
{
// Setup the NCO modulo and oversampling mode
uint32_t s = Si446x_CLK / 10;
uint32_t s = Si446x_CCLK / 10;
uint8_t f3 = (s >> 24) & 0xFF;
uint8_t f2 = (s >> 16) & 0xFF;
uint8_t f1 = (s >> 8) & 0xFF;
@ -757,12 +757,8 @@ static bool Si4464_restoreRX(void)
void Si446x_receive_stop(void)
{
rx_mod = MOD_NOT_SET;
rx_frequency = 0;
/*
* Shutdown radio if it isnt transmitting. If it is, the transmission subthread
* isnt going to restore the transmission state because rx_mod is set MOD_NOT_SET.
*/
if(Si446x_getState() == Si446x_STATE_RX)
Si446x_shutdown();
}
@ -958,7 +954,7 @@ THD_FUNCTION(si_fifo_feeder_afsk, arg)
Si446x_writeFIFO(localBuffer, c);
// Start transmission
Si446x_transmit(radio_freq, radio_pwr, all, 0x3F, TIME_S2I(10));
Si446x_transmit(radio_freq, radio_pwr, all, 0x4F, TIME_S2I(10));
while(c < all) { // Do while bytes not written into FIFO completely
// Determine free memory in Si446x-FIFO
@ -976,8 +972,11 @@ THD_FUNCTION(si_fifo_feeder_afsk, arg)
chThdSleep(TIME_MS2I(15));
}
// Shutdown radio (and wait for Si446x to finish transmission)
if(rx_mod == MOD_NOT_SET) {
/*
* Shutdown radio if receiption has been interrupted. If receiption was interrupted rx_frequency is set.
* If receiption has not been interrupted rx_frequency is set 0.
*/
if(!rx_frequency) {
Si446x_shutdown();
} else {
Si4464_restoreRX();
@ -1038,7 +1037,7 @@ THD_FUNCTION(si_receiver, arg)
chRegSetThreadName("radio_receiver");
/* Buffer and size params for serial terminal output. */
char serial_buf[1024];
char serial_buf[512];
int serial_out;
/*
@ -1216,7 +1215,7 @@ THD_FUNCTION(si_receiver, arg)
}
}
void receiveAFSK(uint32_t freq, uint8_t rssi) {
void start_rx_thread(uint32_t freq, uint8_t rssi) {
lockRadio();
// Initialize radio
@ -1268,8 +1267,11 @@ THD_FUNCTION(si_fifo_feeder_fsk, arg)
chThdSleep(TIME_MS2I(15)); // That value is ok up to 96k
}*/
// Shutdown radio (and wait for Si446x to finish transmission)
if(rx_mod == MOD_NOT_SET) {
/*
* Shutdown radio if receiption has been interrupted. If receiption was interrupted rx_frequency is set.
* If receiption has not been interrupted rx_frequency is set 0.
*/
if(!rx_frequency) {
Si446x_shutdown();
} else {
Si4464_restoreRX();

Wyświetl plik

@ -5,31 +5,34 @@
#include "hal.h"
#include "types.h"
// Hardware dependent settings
#ifndef Si446x_CLK
#error Si446x_CLK is not defined which is needed for Si446x.
#endif
#define Si446x_MIN_FREQ 144000000 /* Minimum allowed frequency in Hz */
#define Si446x_MAX_FREQ 148000000 /* Maximum allowed frequency in Hz */
#define Si446x_CLK STM32_HSECLK /* Oscillator frequency in Hz */
#define Si446x_TCXO_EN true /* Set this true, if a TCXO is used, false for XTAL */
#ifndef Si446x_CLK_OFFSET
#define Si446x_CLK_OFFSET 0
#endif
// Varios macros
#define Si446x_getGPIO0() palReadLine(LINE_RADIO_GPIO0)
#define Si446x_getGPIO1() palReadLine(LINE_RADIO_GPIO1)
#define Si446x_getCCA() palReadLine(LINE_RADIO_IRQ)
#define Si446x_inRadioBand(freq) (Si446x_MIN_FREQ <= (freq) && (freq) <= Si446x_MAX_FREQ)
#define Si446x_getGPIO0() palReadLine(LINE_RADIO_GPIO0)
#define Si446x_getGPIO1() palReadLine(LINE_RADIO_GPIO1)
#define Si446x_getCCA() palReadLine(LINE_RADIO_IRQ)
#define Si446x_inRadioBand(freq) (Si446x_MIN_FREQ <= (freq) && (freq) <= Si446x_MAX_FREQ)
#define Si446x_CCLK ((Si446x_CLK) + (Si446x_CLK_OFFSET) * (Si446x_CLK) / 1000000) /* Frequency offset corrected oscillator frequency */
// Si4464 States
#define Si446x_STATE_NOCHANGE 0
#define Si446x_STATE_SLEEP 1
#define Si446x_STATE_SPI_ACTIVE 2
#define Si446x_STATE_READY 3
#define Si446x_STATE_READY2 4
#define Si446x_STATE_TX_TUNE 5
#define Si446x_STATE_RX_TUNE 6
#define Si446x_STATE_TX 7
#define Si446x_STATE_RX 8
#define Si446x_STATE_NOCHANGE 0
#define Si446x_STATE_SLEEP 1
#define Si446x_STATE_SPI_ACTIVE 2
#define Si446x_STATE_READY 3
#define Si446x_STATE_READY2 4
#define Si446x_STATE_TX_TUNE 5
#define Si446x_STATE_RX_TUNE 6
#define Si446x_STATE_TX 7
#define Si446x_STATE_RX 8
// Si4464 Registers
@ -159,7 +162,7 @@ int16_t Si446x_getLastTemperature(void);
void sendAFSK(packet_t packet, uint32_t freq, uint8_t pwr);
void send2FSK(packet_t packet, uint32_t freq, uint8_t pwr);
void receiveAFSK(uint32_t freq, uint8_t rssi);
void start_rx_thread(uint32_t freq, uint8_t rssi);
void unlockRadio(void);
void lockRadioByCamera(void);

Wyświetl plik

@ -316,7 +316,7 @@ bool pktProcessAFSK(AFSKDemodDriver *myDriver, min_pwmcnt_t current_tone[]) {
if(pktCheckAFSKSymbolTime(myDriver)) {
/* A symbol is ready to decode. */
if(!pktDecodeAFSKSymbol(myDriver))
/* Error in decoding. */
/* Unable to store character - buffer full. */
return false;
}
pktUpdateAFSKSymbolPLL(myDriver);
@ -439,8 +439,9 @@ bool pktDecodeAFSKSymbol(AFSKDemodDriver *myDriver) {
} /* End function. */
/**
* @brief Reset decoder.
* @brief Reset the AFSK decoder and filter.
* @notes Called at completion of packet reception.
* @post Selected tone decoder and common AFSK data is initialized.
*
* @param[in] myDriver pointer to an @p AFSKDemodDriver structure.
*
@ -451,6 +452,16 @@ void pktResetAFSKDecoder(AFSKDemodDriver *myDriver) {
* Called when a decode stream has completed.
* Called from normal thread level.
*/
/* Reset the decoder data.*/
myDriver->frame_state = FRAME_SEARCH;
myDriver->prior_freq = TONE_NONE;
myDriver->bit_index = 0;
myDriver->decimation_accumulator = 0;
/* Set the hdlc bits to all ones. */
myDriver->hdlc_bits = (int32_t)-1;
switch(AFSK_DECODE_TYPE) {
case AFSK_DSP_QCORR_DECODE: {
@ -752,6 +763,11 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
myDriver->decoder_state = DECODER_ACTIVE;
#if AFSK_DEBUG_TYPE == AFSK_PWM_DATA_CAPTURE_DEBUG
char buf[80];
int out = chsnprintf(buf, sizeof(buf),"\r\n======= START ===========\r\n");
chnWrite(pkt_out, (uint8_t *)buf, out);
#endif
/* Increase thread priority. */
(void)chThdSetPriority(DECODER_RUN_PRIORITY);
/* Turn on the decoder LED. */
@ -778,6 +794,14 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
if(n == sizeof(packed_pwm_counts_t)) {
array_min_pwm_counts_t radio;
pktUnpackPWMData(data, &radio);
#if AFSK_DEBUG_TYPE == AFSK_PWM_DATA_CAPTURE_DEBUG
char buf[80];
int out = chsnprintf(buf, sizeof(buf), "%i, %i\r\n",
radio.pwm.impulse, radio.pwm.valley);
chnWrite(pkt_out, (uint8_t *)buf, out);
#endif
/* look for "in band" signal in radio data. */
if(radio.pwm.impulse == 0) {
switch(radio.pwm.valley) {
@ -785,59 +809,55 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
/* End of data flag from PWM. */
myDriver->decoder_state = DECODER_CLOSE;
continue; /* From this case. */
}
} /* End case 0. */
case 1: {
/* Buffer overrun flag from PWM. */
/* PWM side has already set an event for this. */
/* Buffer overrun flag from PWM.
* PWM side has already set an event for this.
*/
myDriver->decoder_state = DECODER_ERROR;
continue; /* From this case. */
}
} /* End case 1. */
case 2:
default: {
/* Unknown flag from PWM.
* If a chained buffer system is implemented it will be case 2.
*/
/* Unknown flag from PWM. */
pktAddEventFlags(myHandler, EVT_PWM_UNKNOWN_INBAND);
myDriver->active_demod_object->status |= EVT_PWM_UNKNOWN_INBAND;
myDriver->decoder_state = DECODER_ERROR;
continue; /* From this case. */
}
} /* End case default. */
} /* End switch. */
}
} /* End if in-band. */
/*
* If error in data (HDLC_RESET) stop decoding.
* Process the AFSK into HDLC bit and AX25 data.
*/
if(!pktProcessAFSK(myDriver, radio.array)) {
/* AX25 character decoded but buffer is full.
* Status set and event sent by HDLC processor.
*/
myDriver->decoder_state = DECODER_ERROR;
break; /* From this case. */
}
if(myDriver->frame_state == FRAME_CLOSE) {
#if AFSK_COLLISION_RESTART == TRUE
uint16_t theCRC =
calc_crc16(myHandler->active_packet_object->buffer, 0,
myHandler->active_packet_object->packet_size);
if((myHandler->active_packet_object->packet_size < AX25_MIN_FRAME)
|| (theCRC != CRC_INCLUSIVE_CONSTANT)) {
pktRestartAFSKDecoder(myDriver);
continue;
}
#endif
/* Check for change of frame state. */
switch(myDriver->frame_state) {
case FRAME_SEARCH:
pktWriteDecoderLED(PAL_TOGGLE);
continue;
case FRAME_OPEN:
case FRAME_DATA:
pktWriteDecoderLED(PAL_HIGH);
continue;
case FRAME_RESET:
continue;
case FRAME_CLOSE: {
myDriver->decoder_state = DECODER_CLOSE;
break; /* From this case. */
}
/* Queued data processed OK but not at frame end.
* Get next data in queue.
* Toggle decoder LED.
*/
pktWriteDecoderLED(PAL_TOGGLE);
continue;
}
/* Data not received in time.
* Stop any further ICU writes to queue.
*/
continue; /* From this case. */
}
} /* End switch. */
} /* End data == sizeof PWM. */
/* PWM data timeout. */
pktAddEventFlags(myHandler, EVT_PWM_STREAM_TIMEOUT);
myDriver->active_demod_object->status |= EVT_PWM_STREAM_TIMEOUT;
myDriver->decoder_state = DECODER_TIMEOUT;
@ -876,14 +896,6 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
chSysUnlock();
}
/* Reset the decoder data.*/
myDriver->frame_state = FRAME_SEARCH;
myDriver->prior_freq = TONE_NONE;
myDriver->bit_index = 0;
myDriver->decimation_accumulator = 0;
/* Set the hdlc bits to all ones. */
myDriver->hdlc_bits = -1;
/*
* Reset the correlation decoder and its filters.
*/
@ -911,8 +923,6 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
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
qcorr_stats_t *statistics = get_qcorr_statistics(myDriver);
myHandler->active_packet_object->correction =
@ -923,13 +933,25 @@ THD_FUNCTION(pktAFSKDecoder, arg) {
uint16_t magicCRC =
calc_crc16(myHandler->active_packet_object->buffer, 0,
myHandler->active_packet_object->packet_size);
#if AFSK_DEBUG_TYPE == AFSK_PWM_DATA_CAPTURE_DEBUG
char buf[80];
int out = chsnprintf(buf, sizeof(buf),
"\r\n======= END %s =========\r\n",
(magicCRC == CRC_INCLUSIVE_CONSTANT) ? "(good)" : "(bad)");
chnWrite(pkt_out, (uint8_t *)buf, out);
#endif
/* 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;
/* Copy status into packet buffer object. */
myHandler->active_packet_object->status =
myDriver->active_demod_object->status;
/* Send the packet buffer to the consumer. */
chFifoSendObject(myHandler->packet_fifo_pool,
myHandler->active_packet_object);
myHandler->active_packet_object = NULL;

Wyświetl plik

@ -50,6 +50,8 @@
#define AFSK_QCORR_DEC_MS_DEBUG 4
#define AFSK_QCORR_DEC_CS_DEBUG 5
#define AFSK_QCORR_DEC_MFIL_DEBUG 6
#define AFSK_PWM_DATA_CAPTURE_DEBUG 7
#define AFSK_PWM_DATA_REPLAY_DEBUG 8
#define AFSK_DEBUG_TYPE AFSK_NO_DEBUG

Wyświetl plik

@ -36,6 +36,7 @@ typedef enum packetHandlerStates {
typedef enum HDLCFrameStates{
FRAME_SEARCH,
FRAME_OPEN,
FRAME_DATA,
FRAME_CLOSE,
FRAME_RESET
} frame_state_t;
@ -103,7 +104,7 @@ typedef struct packetHandlerData {
/**
* @brief Event flags.
*/
eventflags_t status;
//eventflags_t status;
/**
* @brief Packet count.

Wyświetl plik

@ -46,10 +46,20 @@
const ICUConfig pwm_icucfg = {
ICU_INPUT_ACTIVE_HIGH,
ICU_COUNT_FREQUENCY, /* ICU clock frequency. */
#if defined(LINE_PWM_MIRROR)
pktRadioICUWidth, /* ICU width callback. */
#else
NULL, /* ICU width callback. */
#endif
pktRadioICUPeriod, /* ICU period callback. */
PktRadioICUOverflow, /* ICU overflow callback. */
ICU_CHANNEL_1, /* Timer channel (0 or 1). */
#if PWM_TIMER_CHANNEL == 0
ICU_CHANNEL_1, /* Timer channel 0. */
#elif PWM_TIMER_CHANNEL == 1
ICU_CHANNEL_2, /* Timer channel 1. */
#elif
#error PWM_CHANNEL undefined or incorrectly defined
#endif
0
};
/*===========================================================================*/
@ -99,6 +109,9 @@ ICUDriver *pktAttachICU(radio_unit_t radio_id) {
*/
pktSetLineModeICU();
/* If using PWM mirror to output to a diagnostic port. */
pktSetLineModePWMMirror();
/* Initialise the timers. */
chVTObjectInit(&myICU->cca_timer);
chVTObjectInit(&myICU->icu_timer);
@ -141,6 +154,9 @@ void pktDetachICU(ICUDriver *myICU) {
/* Disable overflow LED. */
pktUnsetLineModeOverflowLED();
/* If using PWM mirror disable diagnostic port. */
pktUnsetLineModePWMMirror();
}
/**
@ -452,9 +468,9 @@ void pktRadioCCAInput(ICUDriver *myICU) {
/* 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.
* De-glitch for 8 AFSK bit times.
*/
chVTSetI(&myICU->cca_timer, TIME_MS2I(66),
chVTSetI(&myICU->cca_timer, TIME_MS2I(7),
(vtfunc_t)pktRadioCCATrailTimer, myICU);
}
/* Idle state. */
@ -468,8 +484,9 @@ void pktRadioCCAInput(ICUDriver *myICU) {
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),
/* De-glitch for 16 AFSK bit times. */
chVTSetI(&myICU->cca_timer,
TIME_MS2I(14),
(vtfunc_t)pktRadioCCALeadTimer, myICU);
break;
}
@ -478,6 +495,21 @@ void pktRadioCCAInput(ICUDriver *myICU) {
return;
}
/**
* @brief Width callback from ICU driver.
* @notes Called at ISR level.
*
* @param[in] myICU pointer to a @p ICUDriver structure
*
* @api
*/
void pktRadioICUWidth(ICUDriver *myICU) {
(void)myICU;
#if defined(LINE_PWM_MIRROR)
pktWritePWMMirror(PAL_LOW);
#endif
}
/**
* @brief Period callback from ICU driver.
* @notes Called at ISR level.
@ -493,6 +525,9 @@ void pktRadioICUPeriod(ICUDriver *myICU) {
*
* See halconf.h for the definition.
*/
#if defined(LINE_PWM_MIRROR)
pktWritePWMMirror(PAL_HIGH);
#endif
AFSKDemodDriver *myDemod = myICU->link;
chSysLockFromISR();

Wyświetl plik

@ -32,8 +32,8 @@
* TODO: This should be calculated using SYSTEM CLOCK.
* ICU has to run at an integer divide from SYSTEM CLOCK.
*/
//#define ICU_COUNT_FREQUENCY 2880000U
#define ICU_COUNT_FREQUENCY 4000000U
#define ICU_COUNT_FREQUENCY 2880000U
//#define ICU_COUNT_FREQUENCY 2000000U
/* Limit of ICU and PWM count for packed format. */
#define ICU_MAX_COUNT 0xFFFFFF
@ -114,16 +114,16 @@ typedef struct {
static inline void pktConvertICUtoPWM(ICUDriver *icup, byte_packed_pwm_t *dest) {
icucnt_t impulse = icuGetWidthX(icup);
icucnt_t valley = icuGetPeriodX(icup) - impulse;
dest->pwm.impulse = impulse & 0xFFU;
dest->pwm.valley = valley & 0xFFU;
dest->pwm.impulse = (packed_pwmcnt_t)impulse & 0xFFU;
dest->pwm.valley = (packed_pwmcnt_t)valley & 0xFFU;
/*
* Pack extension bits 8-11 of impulse and valley into a byte.
* Impulse goes into low nibble and valley into high nibble.
*/
valley >>= 4;
impulse >>= 8;
dest->pwm.xtn = ((icucnt_t)(impulse) & 0x0FU);
dest->pwm.xtn += ((icucnt_t)(valley) & 0xF0U);
dest->pwm.xtn = ((packed_pwmxtn_t)(impulse) & 0x000FU);
dest->pwm.xtn |= ((packed_pwmxtn_t)(valley) & 0x00F0U);
}
/**
@ -137,10 +137,10 @@ static inline void pktConvertICUtoPWM(ICUDriver *icup, byte_packed_pwm_t *dest)
*/
static inline void pktUnpackPWMData(byte_packed_pwm_t src, array_min_pwm_counts_t *dest) {
min_icucnt_t duration = src.pwm.impulse;
duration += ((min_icucnt_t)(src.pwm.xtn & 0x0FU) << 8);
duration |= ((min_icucnt_t)(src.pwm.xtn & 0x0FU) << 8);
dest->pwm.impulse = duration;
duration = src.pwm.valley;
duration += ((min_icucnt_t)(src.pwm.xtn & 0xF0U) << 4);
duration |= ((min_icucnt_t)(src.pwm.xtn & 0xF0U) << 4);
dest->pwm.valley = duration;
}
@ -194,6 +194,7 @@ extern "C" {
ICUDriver *pktAttachICU(radio_unit_t radio_id);
void pktDetachICU(ICUDriver *myICU);
void pktICUStart(ICUDriver *myICU);
void pktRadioICUWidth(ICUDriver *myICU);
void pktRadioICUPeriod(ICUDriver *myICU);
void PktRadioICUOverflow(ICUDriver *myICU);
void pktRadioCCAInput(ICUDriver *myICU);

Wyświetl plik

@ -8,11 +8,16 @@
#include "pktconf.h"
/*
* Here we handle the generation of HDLC bits and data bits.
* HDLC flag is detected at the raw link level.
* RLL encoding (bit stuffing) is also handled and removed from the data stream.
/**
* @brief Extract HDLC from AFSK.
* @post The HDLC state will be updated.
* @param[in] myDriver pointer to an @p AFSKDemodDriver structure.
*
* @return status of operation
* @retval true character processed and HDLC state updated on flags.
* @retval false frame buffer full.
*
* @api
*/
bool pktExtractHDLCfromAFSK(AFSKDemodDriver *myDriver) {
@ -32,7 +37,8 @@ bool pktExtractHDLCfromAFSK(AFSKDemodDriver *myDriver) {
* Check if we are in AX25 data capture mode.
* If so check and act on HDLC codes otherwise just store data.
*/
if(myDriver->frame_state == FRAME_OPEN) {
switch(myDriver->frame_state) {
case FRAME_OPEN: {
switch(myDriver->hdlc_bits & HDLC_CODE_MASK) {
case HDLC_FLAG: {
/*
@ -74,7 +80,8 @@ bool pktExtractHDLCfromAFSK(AFSKDemodDriver *myDriver) {
myDriver->frame_state = FRAME_SEARCH;
break;
}
return false;
myDriver->frame_state = FRAME_RESET;
return true;
} /* End case. */
default: {
@ -108,11 +115,12 @@ bool pktExtractHDLCfromAFSK(AFSKDemodDriver *myDriver) {
}
/* Else shift the prior bit to make space for next bit. */
myDriver->current_byte >>= 1;
//myDriver->current_byte &= 0x7F;
return true;
} /* End case default. */
} /* End switch. */
} /* Else not frame_open... */
case FRAME_SEARCH: {
/*
* Frame start not yet detected.
* Check for opening HDLC flag sequence.
@ -122,18 +130,24 @@ bool pktExtractHDLCfromAFSK(AFSKDemodDriver *myDriver) {
||
((myDriver->hdlc_bits & HDLC_FRAME_MASK_B) == HDLC_FRAME_OPEN_B)
) {
myDriver->frame_state = FRAME_OPEN;
/* Reset AX25 data indexes. */
myHandler->active_packet_object->packet_size = 0;
myDriver->bit_index = 0;
myDriver->frame_state = FRAME_OPEN;
/*
* AX25 data buffering is now enabled.
* Data bytes will be written to the AX25 buffer.
*/
}
return true;
/* Reset AX25 data indexes. */
myHandler->active_packet_object->packet_size = 0;
myDriver->bit_index = 0;
/*
* AX25 data buffering is now enabled.
* Data bytes will be written to the AX25 buffer.
*/
}
return true;
}
default:
return true;
} /* End switch on frame state. */
} /* End function. */
/** @} */

Wyświetl plik

@ -15,7 +15,6 @@ static const ShellCommand commands[] = {
{"config", printConfig},
{"command", command2Camera},
{"aprs_message", send_aprs_message},
{"test_rx", test_rx},
{NULL, NULL}
};
@ -49,7 +48,7 @@ int main(void) {
// Startup threads
start_essential_threads(); // Startup required modules (tracking managemer, watchdog)
start_user_modules(); // Startup optional modules (eg. POSITION, LOG, ...)
start_user_threads(); // Startup optional modules (eg. POSITION, LOG, ...)
while(true) {
#if ACTIVATE_USB

Wyświetl plik

@ -657,7 +657,7 @@ uint32_t getAPRSRegionFrequency(void) {
// Position unknown
if(point == NULL || (point->gps_lat == 0 && point->gps_lon == 0))
return 0;
return 144800000;
// America 144.390 MHz
if(isPointInAmerica(point->gps_lat, point->gps_lon))
@ -699,20 +699,3 @@ uint32_t getAPRSRegionFrequency(void) {
return 144800000;
}
uint32_t getFrequency(freq_conf_t *config)
{
switch(config->type) {
case FREQ_APRS_REGION:; // Dynamic frequency (determined by GPS position)
uint32_t freq = getAPRSRegionFrequency();
if(!freq) // Use default frequency (if freq is 0 = position unknown)
return config->hz;
return freq;
case FREQ_STATIC: // Static frequency
return config->hz;
default:
return 0;
}
}

Wyświetl plik

@ -23,7 +23,6 @@ typedef struct {
} coord_t;
uint32_t getAPRSRegionFrequency(void);
uint32_t getFrequency(freq_conf_t *config);
#endif

Wyświetl plik

@ -39,26 +39,26 @@
* HAL driver system settings.
*/
#define STM32_NO_INIT FALSE
#define STM32_HSI_ENABLED FALSE
#define STM32_HSI_ENABLED TRUE
#define STM32_LSI_ENABLED TRUE
#define STM32_HSE_ENABLED TRUE
#define STM32_HSE_ENABLED FALSE
#define STM32_LSE_ENABLED FALSE
#define STM32_CLOCK48_REQUIRED TRUE
#define STM32_SW STM32_SW_PLL
#define STM32_PLLSRC STM32_PLLSRC_HSE
#define STM32_PLLM_VALUE 26
#define STM32_PLLSRC STM32_PLLSRC_HSI
#define STM32_PLLM_VALUE 16
#define STM32_PLLN_VALUE 384
#define STM32_PLLP_VALUE 4
#define STM32_PLLP_VALUE 8
#define STM32_PLLQ_VALUE 8
#define STM32_HPRE STM32_HPRE_DIV1
#define STM32_PPRE1 STM32_PPRE1_DIV2
#define STM32_PPRE1 STM32_PPRE1_DIV1
#define STM32_PPRE2 STM32_PPRE2_DIV1
#define STM32_RTCSEL STM32_RTCSEL_LSI
#define STM32_RTCPRE_VALUE 8
#define STM32_MCO1SEL STM32_MCO1SEL_PLL
#define STM32_MCO1PRE STM32_MCO1PRE_DIV1
#define STM32_MCO2SEL STM32_MCO2SEL_PLL
#define STM32_MCO2PRE STM32_MCO2PRE_DIV4 /* Camera XCLK 24MHz */
#define STM32_MCO2PRE STM32_MCO2PRE_DIV2 /* Camera XCLK 24MHz */
#define STM32_I2SSRC STM32_I2SSRC_CKIN
#define STM32_PLLI2SN_VALUE 192
#define STM32_PLLI2SR_VALUE 5

Wyświetl plik

@ -234,11 +234,37 @@ static inline void pktWriteOverflowLED(uint8_t state) {
}
static inline void pktUnsetLineModeOverflowLED(void) {
#if defined( LINE_OVERFLOW_LED)
#if defined(LINE_OVERFLOW_LED)
palSetLineMode(LINE_OVERFLOW_LED, PAL_MODE_UNCONNECTED);
#endif
}
static inline void pktSetLineModePWMMirror(void) {
#if defined(LINE_PWM_MIRROR)
palSetLineMode(LINE_PWM_MIRROR, PAL_MODE_OUTPUT_PUSHPULL);
#endif
}
static inline void pktUnsetLineModePWMMirror(void) {
#if defined(LINE_PWM_MIRROR)
palSetLineMode(LINE_PWM_MIRROR, PAL_MODE_UNCONNECTED);
#endif
}
static inline void pktWritePWMMirror(uint8_t state) {
#if defined(LINE_PWM_MIRROR)
if(state != PAL_TOGGLE)
palWriteLine(LINE_PWM_MIRROR, state);
else
palToggleLine(LINE_PWM_MIRROR);
#else
(void)state;
#endif
}
#endif /* _PKTCONF_H_ */
/** @} */

Wyświetl plik

@ -23,6 +23,8 @@
#define LINE_UART4_TX PAL_LINE(GPIOA, 12U)
#define LINE_UART4_RX PAL_LINE(GPIOA, 11U)
//#define LINE_PWM_MIRROR PAL_LINE(GPIOA, 8U)
#define PWM_ICU ICUD4
/* Definitions for ICU FIFO implemented using chfactory. */

Wyświetl plik

@ -23,6 +23,7 @@
#include "base91.h"
#include "digipeater.h"
#include "dedupe.h"
#include "radio.h"
#define METER_TO_FEET(m) (((m)*26876) / 8192)
@ -37,6 +38,7 @@ char alias_re[] = "WIDE[4-7]-[1-7]|CITYD";
char wide_re[] = "WIDE[1-7]-[1-7]";
enum preempt_e preempt = PREEMPT_OFF;
static heard_t heard_list[20];
static bool dedupe_initialized;
void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len)
{
@ -67,7 +69,7 @@ void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len)
* - Number of satellites being used
* - Number of cycles where GPS has been lost (if applicable in cycle)
*/
packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoint)
packet_t aprs_encode_position(const char *callsign, const char *path, uint8_t symbol, trackPoint_t *trackPoint)
{
// Latitude
uint32_t y = 380926 * (90 - trackPoint->gps_lat/10000000.0);
@ -97,9 +99,9 @@ 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>%s,%s:!", config->callsign, APRS_DEST_CALLSIGN, config->path);
uint32_t len = chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:!", callsign, APRS_DEST_CALLSIGN, path);
xmit[len+0] = (config->symbol >> 8) & 0xFF;
xmit[len+0] = (symbol >> 8) & 0xFF;
xmit[len+1] = y3+33;
xmit[len+2] = y2+33;
xmit[len+3] = y1+33;
@ -108,7 +110,7 @@ packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoin
xmit[len+6] = x2+33;
xmit[len+7] = x1+33;
xmit[len+8] = x1r+33;
xmit[len+9] = config->symbol & 0xFF;
xmit[len+9] = symbol & 0xFF;
xmit[len+10] = a1+33;
xmit[len+11] = a1r+33;
xmit[len+12] = ((gpsFix << 5) | (src << 3) | origin) + 33;
@ -143,10 +145,10 @@ packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoin
return ax25_from_text(xmit, 1);
}
packet_t aprs_encode_data_packet(char packetType, const aprs_conf_t *config, uint8_t *data)
packet_t aprs_encode_data_packet(const char *callsign, const char *path, char packetType, uint8_t *data)
{
char xmit[256];
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:{{%c%s", config->callsign, APRS_DEST_CALLSIGN, config->path, packetType, data);
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s:{{%c%s", callsign, APRS_DEST_CALLSIGN, path, packetType, data);
return ax25_from_text(xmit, 1);
}
@ -154,18 +156,18 @@ packet_t aprs_encode_data_packet(char packetType, const aprs_conf_t *config, uin
/**
* Transmit message packet
*/
packet_t aprs_encode_message(const aprs_conf_t *config, const char *receiver, const char *text, const bool noCounter)
packet_t aprs_encode_message(const char *callsign, const char *path, const char *receiver, const char *text, const bool noCounter)
{
char xmit[256];
if(noCounter)
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", config->callsign, APRS_DEST_CALLSIGN, config->path, receiver, text);
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s", callsign, APRS_DEST_CALLSIGN, path, receiver, text);
else
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", config->callsign, APRS_DEST_CALLSIGN, config->path, receiver, text, ++msg_id);
chsnprintf(xmit, sizeof(xmit), "%s>%s,%s::%-9s:%s{%d", callsign, APRS_DEST_CALLSIGN, path, receiver, text, ++msg_id);
return ax25_from_text(xmit, 1);
}
packet_t aprs_encode_query_answer_aprsd(const aprs_conf_t *config, const char *receiver)
packet_t aprs_encode_query_answer_aprsd(const char *callsign, const char *path, const char *receiver)
{
char buf[256] = "Directs=";
uint32_t out = 8;
@ -175,7 +177,7 @@ packet_t aprs_encode_query_answer_aprsd(const aprs_conf_t *config, const char *r
}
buf[out-1] = 0; // Remove last spacer
return aprs_encode_message(config, receiver, buf, true);
return aprs_encode_message(callsign, path, receiver, buf, true);
}
static bool aprs_decode_message(packet_t pp)
@ -208,7 +210,7 @@ static bool aprs_decode_message(packet_t pp)
}
// Try to find out if this message is meant for us
if(pinfo[10] == ':' && !strcmp(config[2].aprs_conf.callsign, dest))
if(pinfo[10] == ':' && !strcmp(config.rx.call, dest))
{
char msg_id_rx[8];
memset(msg_id_rx, 0, sizeof(msg_id_rx));
@ -256,23 +258,23 @@ static bool aprs_decode_message(packet_t pp)
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);
packet_t pp = aprs_encode_position(config.rx.call, config.rx.path, config.rx.symbol, trackPoint);
transmitOnRadio(pp, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
} else if(!strcmp(command, "?APRSD")) { // Transmit position
TRACE_INFO("Message: Directs query");
packet_t pp = aprs_encode_query_answer_aprsd(&(config->aprs_conf), src);
transmitOnRadio(pp, &(config[2].frequency), config[2].power, config[2].modulation);
packet_t pp = aprs_encode_query_answer_aprsd(config.rx.call, config.rx.path, src);
transmitOnRadio(pp, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
} else if(!strcmp(command, "?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
packet_t pp = aprs_encode_message(config.rx.call, config.rx.path, src, buf, true);
transmitOnRadio(pp, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
chThdSleep(TIME_S2I(5)); // Give some time to send the message
NVIC_SystemReset();
@ -283,8 +285,8 @@ static bool aprs_decode_message(packet_t pp)
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);
packet_t pp = aprs_encode_message(config.rx.call, config.rx.path, src, buf, true);
transmitOnRadio(pp, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
}
return false; // Mark that message dont has to be digipeated
@ -295,24 +297,31 @@ static bool aprs_decode_message(packet_t pp)
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) {
dedupe_remember(result, 0);
transmitOnRadio(result, &(config[2].frequency), config[2].power, config[2].modulation);
if(!dedupe_initialized) {
dedupe_init(TIME_S2I(10));
dedupe_initialized = true;
}
if(!dedupe_check(pp, 0)) { // Last identical packet older than 10 seconds
packet_t result = digipeat_match(0, pp, config.rx.call, config.rx.call, alias_re, wide_re, 0, preempt, NULL);
if(result != NULL) { // Should be digipeated
dedupe_remember(result, 0);
transmitOnRadio(result, config.rx.radio_conf.freq, config.rx.radio_conf.pwr, config.rx.radio_conf.mod);
}
}
}
/**
* Transmit APRS telemetry configuration
*/
packet_t aprs_encode_telemetry_configuration(const aprs_conf_t *config, uint8_t type)
packet_t aprs_encode_telemetry_configuration(const char *callsign, const char *path, uint8_t type)
{
switch(type)
{
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);
case 0: return aprs_encode_message(callsign, path, callsign, "PARM.Vbat,Vsol,Pbat,Temperature,Airpressure", true);
case 1: return aprs_encode_message(callsign, path, callsign, "UNIT.V,V,W,degC,Pa", true);
case 2: return aprs_encode_message(callsign, path, 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(callsign, path, callsign, "BITS.11111111,", true);
default: return NULL;
}
}
@ -358,6 +367,6 @@ void aprs_decode_packet(packet_t pp)
if(pinfo[0] == ':') digipeat = aprs_decode_message(pp); // ax25_get_dti(pp)
// Digipeat packet
//if(digipeat) aprs_digipeat(pp);
if(config.dig_active) aprs_digipeat(pp);
}

Wyświetl plik

@ -52,11 +52,11 @@
void aprs_debug_getPacket(packet_t pp, char* buf, uint32_t len);
packet_t aprs_encode_position(const aprs_conf_t *config, trackPoint_t *trackPoint);
packet_t aprs_encode_telemetry_configuration(const aprs_conf_t *config, uint8_t type);
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);
packet_t aprs_encode_query_answer_aprsd(const aprs_conf_t *config, const char *receiver);
packet_t aprs_encode_position(const char *callsign, const char *path, uint8_t symbol, trackPoint_t *trackPoint);
packet_t aprs_encode_telemetry_configuration(const char *callsign, const char *path, uint8_t type);
packet_t aprs_encode_message(const char *callsign, const char *path, const char *receiver, const char *text, const bool noCounter);
packet_t aprs_encode_data_packet(const char *callsign, const char *path, char packetType, uint8_t *data);
packet_t aprs_encode_query_answer_aprsd(const char *callsign, const char *path, const char *receiver);
void aprs_decode_packet(packet_t pp);

Wyświetl plik

@ -227,7 +227,7 @@ void dedupe_remember (packet_t pp, int chan)
int dedupe_check (packet_t pp, int chan)
{
unsigned short crc = ax25_dedupe_crc(pp);
time_t now = chVTGetSystemTime();
sysinterval_t now = chVTGetSystemTime();
int j;
for (j=0; j<HISTORY_MAX; j++) {

Wyświetl plik

@ -37,9 +37,9 @@ bool p_sleep(const sleep_conf_t *config)
return false;
}
sysinterval_t waitForTrigger(sysinterval_t prev, trigger_conf_t *config)
sysinterval_t waitForTrigger(sysinterval_t prev, sysinterval_t timeout)
{
switch(config->type)
/*switch(config->type)
{
case TRIG_NEW_POINT: // Wait for new tracking point
waitForNewTrackPoint();
@ -55,7 +55,9 @@ sysinterval_t waitForTrigger(sysinterval_t prev, trigger_conf_t *config)
chThdSleep(TIME_S2I(10));
}
return chVTGetSystemTimeX();
return chVTGetSystemTimeX();*/
return chThdSleepUntilWindowed(prev, prev + TIME_S2I(timeout));
}
void trigger_new_tracking_point(void)

Wyświetl plik

@ -9,8 +9,9 @@
#define TX_CONTINUOSLY trigger_immediately
bool p_sleep(const sleep_conf_t *config);
sysinterval_t waitForTrigger(sysinterval_t prev, trigger_conf_t *config);
sysinterval_t waitForTrigger(sysinterval_t prev, sysinterval_t timeout);
void trigger_new_tracking_point(void);
void trigger_immediately(void);
#endif

Wyświetl plik

@ -280,7 +280,7 @@ uint32_t gimage_id; // Global image ID (for all image threads)
mutex_t camera_mtx;
bool camera_mtx_init = false;
void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx)
static void encode_ssdv(const uint8_t *image, uint32_t image_len, thd_img_conf_t* conf, uint8_t image_id, bool redudantTx)
{
(void)redudantTx;
@ -294,13 +294,11 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
// Init SSDV (FEC at 2FSK, non FEC at APRS)
bi = 0;
ssdv_enc_init(&ssdv, SSDV_TYPE_PADDING, conf->ssdv_conf.callsign, image_id, conf->ssdv_conf.quality);
ssdv_enc_init(&ssdv, SSDV_TYPE_PADDING, "N0CALL", image_id, conf->quality);
ssdv_enc_set_buffer(&ssdv, pkt);
while(true)
{
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(600); // TODO: Implement more sophisticated method
while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
{
b = &image[bi];
@ -330,14 +328,14 @@ void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf,
// Sync byte, CRC and FEC of SSDV not transmitted (because its not neccessary inside an APRS packet)
base91_encode(&pkt[6], pkt_base91, 174);
packet_t packet = aprs_encode_data_packet('I', &conf->aprs_conf, pkt_base91);
transmitOnRadio(packet, &conf->frequency, conf->power, conf->modulation);
packet_t packet = aprs_encode_data_packet(conf->call, conf->path, 'I', pkt_base91);
transmitOnRadio(packet, conf->radio_conf.freq, conf->radio_conf.pwr, conf->radio_conf.mod);
chThdSleep(TIME_MS2I(100)); // Leave other threads some time
// Packet spacing (delay)
if(conf->packet_spacing)
chThdSleep(TIME_MS2I(conf->packet_spacing));
if(conf->thread_conf.packet_spacing)
chThdSleep(TIME_MS2I(conf->thread_conf.packet_spacing));
i++;
}
@ -399,9 +397,9 @@ static bool analyze_image(uint8_t *image, uint32_t image_len)
static bool camInitialized = false;
bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
uint32_t takePicture(uint8_t* buffer, uint32_t size, resolution_t res, bool enableJpegValidation)
{
bool camera_found = false;
uint32_t size_sampled = 0;
// Initialize mutex
if(!camera_mtx_init)
@ -416,7 +414,6 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
if(camInitialized || OV5640_isAvailable()) { // OV5640 available
TRACE_INFO("IMG > OV5640 found");
camera_found = true;
// Lock Radio (The radio uses the same DMA for SPI as the camera)
lockRadioByCamera(); // Lock radio
@ -431,10 +428,10 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
}
// Sample data from pseudo DCMI through DMA into RAM
conf->size_sampled = OV5640_Snapshot2RAM(conf->ram_buffer, conf->ram_size, conf->res);
size_sampled = OV5640_Snapshot2RAM(buffer, size, res);
// Switch off camera
if(!keep_cam_switched_on) {
if(!config.keep_cam_switched_on) {
OV5640_deinit();
camInitialized = false;
}
@ -443,7 +440,7 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
if(enableJpegValidation)
{
TRACE_INFO("CAM > Validate integrity of JPEG");
jpegValid = analyze_image(conf->ram_buffer, conf->ram_size);
jpegValid = analyze_image(buffer, size);
TRACE_INFO("CAM > JPEG image %s", jpegValid ? "valid" : "invalid");
} else {
jpegValid = true;
@ -464,63 +461,61 @@ bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation)
TRACE_INFO("IMG > Unlock camera");
chMtxUnlock(&camera_mtx);
return camera_found;
return size_sampled;
}
THD_FUNCTION(imgThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
thd_img_conf_t* conf = (thd_img_conf_t*)arg;
if(conf->init_delay) chThdSleep(TIME_MS2I(conf->init_delay));
if(conf->thread_conf.init_delay) chThdSleep(conf->thread_conf.init_delay);
TRACE_INFO("IMG > Startup image thread");
sysinterval_t time = chVTGetSystemTimeX();
// Create buffer
uint8_t buffer[conf->buf_size] __attribute__((aligned(32)));
sysinterval_t time = chVTGetSystemTime();
while(true)
{
TRACE_INFO("IMG > Do module IMAGE cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(600); // TODO: Implement more sophisticated method
if(!p_sleep(&conf->sleep_conf))
if(!p_sleep(&conf->thread_conf.sleep_conf))
{
// Take picture
bool camera_found = takePicture(&conf->ssdv_conf, true);
uint32_t size_sampled = takePicture(buffer, conf->buf_size, conf->res, true);
gimage_id++; // Increase SSDV image counter
// Radio transmission
if(camera_found) {
if(size_sampled) {
// Write picture to SD card
if(initSD())
/*if(initSD())
{
char filename[64];
chsnprintf(filename, sizeof(filename), "IMG%d_%d.jpg", getLastTrackPoint()->reset, gimage_id-1);
writeBufferToFile(filename, conf->ssdv_conf.ram_buffer, conf->ssdv_conf.size_sampled);
}
writeBufferToFile(filename, conf->ssdv_conf.ram_buffer, size_sampled);
}*/
// Encode and transmit picture
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
encode_ssdv(conf->ssdv_conf.ram_buffer, conf->ssdv_conf.size_sampled, conf, (uint8_t)(gimage_id-1), conf->redundantTx);
encode_ssdv(buffer, size_sampled, conf, (uint8_t)(gimage_id-1), conf->radio_conf.redundantTx);
} else { // No camera found
TRACE_INFO("IMG > Encode/Transmit SSDV (no cam found) ID=%d", gimage_id-1);
encode_ssdv(noCameraFound, sizeof(noCameraFound), conf, (uint8_t)(gimage_id-1), conf->redundantTx);
TRACE_INFO("IMG > Encode/Transmit SSDV (camera error) ID=%d", gimage_id-1);
encode_ssdv(noCameraFound, sizeof(noCameraFound), conf, (uint8_t)(gimage_id-1), conf->radio_conf.redundantTx);
}
}
time = waitForTrigger(time, &conf->trigger);
time = waitForTrigger(time, conf->thread_conf.cycle);
}
}
void start_image_thread(module_conf_t *conf)
void start_image_thread(thd_img_conf_t *conf)
{
chsnprintf(conf->name, sizeof(conf->name), "IMG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(conf->packet_spacing ? 6*1024 : 12*1024), "IMG", NORMALPRIO, imgThread, conf);
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE((conf->thread_conf.packet_spacing ? 6:12) * 1024 + conf->buf_size), "IMG", NORMALPRIO, imgThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("IMG > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(conf);
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(1) + TIME_MS2I(conf->init_delay);
}
}

Wyświetl plik

@ -3,9 +3,10 @@
#include "ch.h"
#include "hal.h"
#include "types.h"
void start_image_thread(module_conf_t *conf);
bool takePicture(ssdv_conf_t *conf, bool enableJpegValidation);
void start_image_thread(thd_img_conf_t *conf);
uint32_t takePicture(uint8_t* buffer, uint32_t size, resolution_t resolution, bool enableJpegValidation);
extern mutex_t camera_mtx;
extern uint8_t gimage_id;

Wyświetl plik

@ -13,6 +13,7 @@
#include "flash.h"
#include "watchdog.h"
#include "sleep.h"
#include "radio.h"
/*
* Sequence determines in which order log packets are sent out
@ -133,18 +134,17 @@ void getNextLogTrackPoint(trackPoint_t* log)
THD_FUNCTION(logThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
thd_log_conf_t* conf = (thd_log_conf_t*)arg;
if(conf->init_delay) chThdSleep(TIME_MS2I(conf->init_delay));
if(conf->thread_conf.init_delay) chThdSleep(conf->thread_conf.init_delay);
TRACE_INFO("LOG > Startup logging thread");
sysinterval_t time = chVTGetSystemTimeX();
sysinterval_t time = chVTGetSystemTime();
while(true)
{
TRACE_INFO("LOG > Do module LOG cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(600); // TODO: Implement more sophisticated method
if(!p_sleep(&conf->sleep_conf))
if(!p_sleep(&conf->thread_conf.sleep_conf))
{
// Get log from memory
trackPoint_t log;
@ -155,26 +155,22 @@ THD_FUNCTION(logThread, arg)
base91_encode((uint8_t*)&log, pkt_base91, sizeof(log));
// Encode and transmit log packet
packet_t packet = aprs_encode_data_packet('L', &conf->aprs_conf, pkt_base91); // Encode packet
packet_t packet = aprs_encode_data_packet(conf->call, conf->path, 'L', pkt_base91); // Encode packet
// Transmit packet
transmitOnRadio(packet, &conf->frequency, conf->power, conf->modulation);
transmitOnRadio(packet, conf->radio_conf.freq, conf->radio_conf.pwr, conf->radio_conf.mod);
}
time = waitForTrigger(time, &conf->trigger);
time = waitForTrigger(time, conf->thread_conf.cycle);
}
}
void start_logging_thread(module_conf_t *conf)
void start_logging_thread(thd_log_conf_t *conf)
{
chsnprintf(conf->name, sizeof(conf->name), "LOG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(2*1024), "LOG", NORMALPRIO, logThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("LOG > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(conf);
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(1);
}
}

Wyświetl plik

@ -1,7 +1,9 @@
#ifndef __LOG_H__
#define __LOG_H__
void start_logging_thread(module_conf_t *conf);
#include "types.h"
void start_logging_thread(thd_log_conf_t *conf);
#endif

Wyświetl plik

@ -14,10 +14,10 @@
THD_FUNCTION(posThread, arg)
{
module_conf_t* conf = (module_conf_t*)arg;
thd_pos_conf_t* conf = (thd_pos_conf_t*)arg;
// Wait
if(conf->init_delay) chThdSleep(TIME_MS2I(conf->init_delay));
if(conf->thread_conf.init_delay) chThdSleep(conf->thread_conf.init_delay);
// Start tracking manager (if not running yet)
init_tracking_manager(true);
@ -26,32 +26,31 @@ THD_FUNCTION(posThread, arg)
TRACE_INFO("POS > Startup position thread");
// Set telemetry configuration transmission variables
sysinterval_t last_conf_transmission = chVTGetSystemTimeX() - TIME_S2I(conf->aprs_conf.tel_enc_cycle);
sysinterval_t time = chVTGetSystemTimeX();
sysinterval_t last_conf_transmission = chVTGetSystemTime() - TIME_S2I(conf->tel_enc_cycle);
sysinterval_t time = chVTGetSystemTime();
while(true)
{
TRACE_INFO("POS > Do module POSITION cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(600); // TODO: Implement more sophisticated method
TRACE_INFO("POS > Get last track point");
trackPoint_t* trackPoint = getLastTrackPoint();
if(!p_sleep(&conf->sleep_conf))
if(!p_sleep(&conf->thread_conf.sleep_conf))
{
TRACE_INFO("POS > Transmit position");
// Encode/Transmit position packet
packet_t packet = aprs_encode_position(&(conf->aprs_conf), trackPoint); // Encode packet
transmitOnRadio(packet, &conf->frequency, conf->power, conf->modulation);
packet_t packet = aprs_encode_position(conf->call, conf->path, conf->symbol, trackPoint);
transmitOnRadio(packet, conf->radio_conf.freq, conf->radio_conf.pwr, conf->radio_conf.mod);
chThdSleep(TIME_S2I(5));
// Encode/Transmit APRSD packet
packet_t pp = aprs_encode_query_answer_aprsd(&(config->aprs_conf), config->aprs_conf.callsign);
transmitOnRadio(pp, &conf->frequency, conf->power, conf->modulation);
packet_t pp = aprs_encode_query_answer_aprsd(conf->call, conf->path, conf->call);
transmitOnRadio(pp, conf->radio_conf.freq, conf->radio_conf.pwr, conf->radio_conf.mod);
// Telemetry encoding parameter transmission
if(conf->aprs_conf.tel_enc_cycle != 0 && last_conf_transmission + TIME_S2I(conf->aprs_conf.tel_enc_cycle) < chVTGetSystemTimeX())
if(conf->tel_enc_cycle != 0 && last_conf_transmission + TIME_S2I(conf->tel_enc_cycle) < chVTGetSystemTime())
{
chThdSleep(TIME_S2I(5)); // Take a litte break between the packet transmissions
@ -60,29 +59,25 @@ THD_FUNCTION(posThread, arg)
// Encode and transmit telemetry config packet
for(uint8_t type=0; type<4; type++)
{
packet = aprs_encode_telemetry_configuration(&conf->aprs_conf, type);
transmitOnRadio(packet, &conf->frequency, conf->power, conf->modulation);
packet = aprs_encode_telemetry_configuration(conf->call, conf->path, type);
transmitOnRadio(packet, conf->radio_conf.freq, conf->radio_conf.pwr, conf->radio_conf.mod);
chThdSleep(TIME_S2I(5));
}
last_conf_transmission += TIME_S2I(conf->aprs_conf.tel_enc_cycle);
last_conf_transmission += TIME_S2I(conf->tel_enc_cycle);
}
}
time = waitForTrigger(time, &conf->trigger);
time = waitForTrigger(time, conf->thread_conf.cycle);
}
}
void start_position_thread(module_conf_t *conf)
void start_position_thread(thd_pos_conf_t *conf)
{
chsnprintf(conf->name, sizeof(conf->name), "POS");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(10*1024), "POS", NORMALPRIO, posThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("POS > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(conf);
conf->wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(1);
}
}

Wyświetl plik

@ -1,9 +1,8 @@
#ifndef __POS_H__
#define __POS_H__
#include "ch.h"
#include "hal.h"
#include "types.h"
void start_position_thread(module_conf_t *conf);
void start_position_thread(thd_pos_conf_t *conf);
#endif

Wyświetl plik

@ -6,13 +6,15 @@
#include "geofence.h"
static const char *getModulation(uint8_t key) {
const char *val[] = {"unknown", "2FSK", "AFSK"};
const char *val[] = {"AFSK", "2FSK"};
return val[key];
};
bool transmitOnRadio(packet_t packet, freq_conf_t *freq_conf, uint8_t pwr, mod_t mod)
bool transmitOnRadio(packet_t packet, uint32_t freq, uint8_t pwr, mod_t mod)
{
uint32_t freq = getFrequency(freq_conf); // Get transmission frequency
if(freq == FREQ_APRS_DYNAMIC)
freq = getAPRSRegionFrequency(); // Get transmission frequency by geofencing
uint8_t *c;
uint32_t len = ax25_get_info(packet, &c);
@ -31,9 +33,6 @@ bool transmitOnRadio(packet_t packet, freq_conf_t *freq_conf, uint8_t pwr, mod_t
case MOD_AFSK:
sendAFSK(packet, freq, pwr);
break;
case MOD_NOT_SET:
TRACE_ERROR("RAD > Modulation not set");
break;
}
} else {

Wyświetl plik

@ -18,7 +18,7 @@
#define APRS_FREQ_ARGENTINA 144930000
#define APRS_FREQ_BRAZIL 145575000
bool transmitOnRadio(packet_t packet, freq_conf_t *freq_conf, uint8_t pwr, mod_t mod);
bool transmitOnRadio(packet_t packet, uint32_t freq, uint8_t pwr, mod_t mod);
void startReceiver(void);
THD_FUNCTION(moduleRADIO, arg);

Wyświetl plik

@ -9,12 +9,25 @@
sysinterval_t watchdog_tracking;
void start_essential_threads(void) {
void start_essential_threads(void)
{
init_watchdog(); // Init watchdog
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)
receiveAFSK(144800000, 0x47); // Start APRS receiver TODO: Implement frequency changes
chThdSleep(TIME_MS2I(300)); // Wait for tracking manager to initialize
}
void start_user_threads(void)
{
if(config.pos_pri.thread_conf.active) start_position_thread(&config.pos_pri);
if(config.pos_sec.thread_conf.active) start_position_thread(&config.pos_sec);
if(config.img_pri.thread_conf.active) start_image_thread(&config.img_pri);
if(config.img_sec.thread_conf.active) start_image_thread(&config.img_sec);
if(config.log.thread_conf.active) start_logging_thread(&config.log);
if(config.rx.thread_conf.active) start_rx_thread(config.rx.radio_conf.freq, config.rssi);
}

Wyświetl plik

@ -4,6 +4,7 @@
#include "ch.h"
void start_essential_threads(void);
void start_user_threads(void);
extern sysinterval_t watchdog_tracking; // Last update time for module TRACKING

Wyświetl plik

@ -17,7 +17,6 @@
static trackPoint_t trackPoints[2];
static trackPoint_t* lastTrackPoint;
static module_conf_t trac_conf = {.name = "TRAC"}; // Fake config needed for watchdog tracking
static bool threadStarted = false;
static bool tracking_useGPS = false;
@ -132,7 +131,7 @@ void waitForNewTrackPoint(void)
static void aquirePosition(trackPoint_t* tp, trackPoint_t* ltp, sysinterval_t timeout)
{
sysinterval_t start = chVTGetSystemTimeX();
sysinterval_t start = chVTGetSystemTime();
gpsFix_t gpsFix;
memset(&gpsFix, 0, sizeof(gpsFix_t));
@ -141,7 +140,7 @@ static void aquirePosition(trackPoint_t* tp, trackPoint_t* ltp, sysinterval_t ti
uint16_t batt = stm32_get_vbat();
if(!tracking_useGPS) { // No position thread running
tp->gps_lock = GPS_OFF;
} else if(batt < gps_on_vbat) {
} else if(batt < config.gps_on_vbat) {
tp->gps_lock = GPS_LOWBATT1;
} else {
@ -149,14 +148,13 @@ static void aquirePosition(trackPoint_t* tp, trackPoint_t* ltp, sysinterval_t ti
bool status = GPS_Init();
if(status) {
// Search for lock as long enough power is available
do {
batt = stm32_get_vbat();
gps_get_fix(&gpsFix);
} while(!isGPSLocked(&gpsFix) && batt >= gps_off_vbat && chVTGetSystemTimeX() <= start + timeout); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-3sec in order to keep synchronization)
} while(!isGPSLocked(&gpsFix) && batt >= config.gps_off_vbat && chVTGetSystemTime() <= start + timeout); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-3sec in order to keep synchronization)
if(batt < gps_off_vbat) { // GPS was switched on but prematurely switched off because the battery is low on power, switch off GPS
if(batt < config.gps_off_vbat) { // GPS was switched on but prematurely switched off because the battery is low on power, switch off GPS
GPS_Deinit();
TRACE_WARN("TRAC > GPS sampling finished GPS LOW BATT");
@ -170,11 +168,11 @@ static void aquirePosition(trackPoint_t* tp, trackPoint_t* ltp, sysinterval_t ti
} else { // GPS locked successfully, switch off GPS (unless cycle is less than 60 seconds)
// Switch off GPS (if cycle time is more than 60 seconds)
if(track_cycle_time < TIME_S2I(60)) {
if(timeout < TIME_S2I(60)) {
TRACE_INFO("TRAC > Keep GPS switched on because cycle < 60sec");
tp->gps_lock = GPS_LOCKED2;
} else if(gps_onper_vbat != 0 && batt >= gps_onper_vbat) {
TRACE_INFO("TRAC > Keep GPS switched on because VBAT >= %dmV", gps_onper_vbat);
} else if(config.gps_onper_vbat != 0 && batt >= config.gps_onper_vbat) {
TRACE_INFO("TRAC > Keep GPS switched on because VBAT >= %dmV", config.gps_onper_vbat);
tp->gps_lock = GPS_LOCKED2;
} else {
TRACE_INFO("TRAC > Switch off GPS");
@ -208,7 +206,7 @@ static void aquirePosition(trackPoint_t* tp, trackPoint_t* ltp, sysinterval_t ti
}
}
tp->gps_ttff = TIME_I2S(chVTGetSystemTimeX() - start); // Time to first fix
tp->gps_ttff = TIME_I2S(chVTGetSystemTime() - start); // Time to first fix
if(tp->gps_lock != GPS_LOCKED1 && tp->gps_lock != GPS_LOCKED2) { // We have no valid GPS fix
// Take time from internal RTC
@ -301,7 +299,7 @@ static void setSystemStatus(trackPoint_t* tp) {
tp->sys_error |= (bme280_error & 0x7) << 8;
// Set system time
tp->sys_time = TIME_I2S(chVTGetSystemTimeX());
tp->sys_time = TIME_I2S(chVTGetSystemTime());
}
/**
@ -359,15 +357,26 @@ THD_FUNCTION(trackingThread, arg) {
// Wait for position threads to start
chThdSleep(TIME_MS2I(500));
sysinterval_t cycle_time = chVTGetSystemTimeX();
sysinterval_t cycle_time = chVTGetSystemTime();
while(true)
{
TRACE_INFO("TRAC > Do module TRACKING MANAGER cycle");
trac_conf.wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(600); // TODO: Implement more sophisticated method
trackPoint_t* tp = &trackPoints[(id+1) % 2]; // Current track point (the one which is processed now)
trackPoint_t* ltp = &trackPoints[ id % 2]; // Last track point
// Determine cycle time
sysinterval_t track_cycle_time = TIME_S2I(600);
if(config.pos_pri.thread_conf.active && config.pos_sec.thread_conf.active) { // Both position threads are active
track_cycle_time = config.pos_pri.thread_conf.cycle < config.pos_sec.thread_conf.cycle ? config.pos_pri.thread_conf.cycle : config.pos_sec.thread_conf.cycle; // Choose the smallest cycle
} else if(config.pos_pri.thread_conf.active) { // Only primary position thread is active
track_cycle_time = config.pos_pri.thread_conf.cycle;
} else if(config.pos_sec.thread_conf.active) { // Only secondary position thread is active
track_cycle_time = config.pos_pri.thread_conf.cycle;
} else { // There must be an error
TRACE_ERROR("TRAC > Tracking manager started but no position thread is active");
}
// Get GPS position
aquirePosition(tp, ltp, track_cycle_time - TIME_S2I(3));
@ -420,8 +429,6 @@ void init_tracking_manager(bool useGPS)
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("TRAC > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(&trac_conf);
trac_conf.wdg_timeout = chVTGetSystemTimeX() + TIME_S2I(1);
chThdSleep(TIME_MS2I(300)); // Wait a little bit until tracking manager has initialized first dataset
}
}

Wyświetl plik

@ -2,20 +2,12 @@
#include "hal.h"
#include "debug.h"
static module_conf_t *registered_threads[10];
static uint8_t threads_cnt = 0;
// Hardware Watchdog configuration
static const WDGConfig wdgcfg = {
.pr = STM32_IWDG_PR_256,
.rlr = STM32_IWDG_RL(10000)
};
void register_thread_at_wdg(module_conf_t *thread_config)
{
registered_threads[threads_cnt++] = thread_config;
}
static void flash_led(void) {
palSetLine(LINE_IO_GREEN);
chThdSleep(TIME_MS2I(50));
@ -34,13 +26,15 @@ THD_FUNCTION(wdgThread, arg) {
chThdSleep(TIME_MS2I(500));
bool healthy = true;
for(uint8_t i=0; i<threads_cnt; i++) {
if(registered_threads[i]->wdg_timeout < chVTGetSystemTimeX())
// FIXME: Watchdog without functionality at the moment
/*for(uint8_t i=0; i<threads_cnt; i++) {
if(registered_threads[i]->wdg_timeout < chVTGetSystemTime())
{
TRACE_ERROR("WDG > Thread %s not healty", registered_threads[i]->name);
healthy = false; // Threads reached timeout
}
}
}*/
if(healthy)
wdgReset(&WDGD1); // Reset hardware watchdog at no error

Wyświetl plik

@ -1,9 +1,6 @@
#ifndef __WATCHDOG_H__
#define __WATCHDOG_H__
#include "types.h"
void register_thread_at_wdg(module_conf_t *thread_config);
void init_watchdog(void);
#endif

Wyświetl plik

@ -1,13 +1,19 @@
#ifndef __TYPES_H__
#define __TYPES_H__
#include "ch.h"
#include "ax25_pad.h"
#define FREQ_APRS_DYNAMIC 0
#define CYCLE_CONTINUOUSLY 0
typedef struct {
char callsign[10]; // APRS callsign
uint16_t symbol; // APRS symbol
char path[16]; // APRS path
uint16_t preamble; // Preamble in milliseconds
uint16_t tel_enc_cycle; // Telemetry encoding cycle in seconds
char tel_comment[64]; // Telemetry comment
char callsign[AX25_MAX_ADDR_LEN]; // APRS callsign
uint16_t symbol; // APRS symbol
char path[16]; // APRS path
uint16_t preamble; // Preamble in milliseconds
uint16_t tel_enc_cycle; // Telemetry encoding cycle in seconds
char tel_comment[64]; // Telemetry comment
} aprs_conf_t;
typedef enum {
@ -26,28 +32,9 @@ typedef struct {
uint16_t vsol_thres;
} sleep_conf_t;
typedef struct {
uint8_t dummy; // Not used yet
} afsk_conf_t;
typedef struct {
uint32_t speed;
} fsk_conf_t;
typedef enum {
FREQ_STATIC, // Fixed frequency
FREQ_APRS_REGION // APRS region dependent (it changes it frequency automatically depending on which APRS frequency is used in this region)
} freq_type_t;
typedef struct {
freq_type_t type;
uint32_t hz;
} freq_conf_t;
typedef enum { // Modulation type
MOD_NOT_SET,
MOD_2FSK,
MOD_AFSK
MOD_AFSK,
MOD_2FSK
} mod_t;
typedef struct { // Radio message type
@ -55,11 +42,8 @@ typedef struct { // Radio message type
uint32_t bin_len; // Binary length (it bits)
uint8_t power; // Power in dBm
mod_t mod; // Modulation
freq_conf_t* freq; // Frequency
afsk_conf_t* afsk_conf; // AFSK config
fsk_conf_t* fsk_conf; // 2FSK config
uint32_t speed; // Speed
uint32_t freq; // Frequency
} radioMSG_t;
typedef enum {
@ -72,57 +56,104 @@ typedef enum {
RES_MAX
} resolution_t;
typedef struct {
char callsign[7]; // Callsign (or stream identifier)
resolution_t res; // Camera resolution
uint8_t quality; // JPEG quality
uint8_t *ram_buffer; // Camera Buffer
uint32_t ram_size; // Size of buffer
uint32_t size_sampled; // Actual image data size (do not set in config)
} ssdv_conf_t;
typedef enum {
TRIG_ONCE, // Trigger once and never again (e.g. transmit specific position packet only at startup)
TRIG_NEW_POINT, // Triggered when new track point available
TRIG_TIMEOUT, // Triggered by timeout (e.g. trasmit position every 120sec)
TRIG_CONTINUOUSLY // Continue continuously (e.g. send new image once old image sent completely)
} trigger_type_t;
/* ------------------------------------------- NEW ------------------------------------------- */
typedef struct {
trigger_type_t type; // Trigger type
uint32_t timeout; // Timeout in seconds
} trigger_conf_t;
int8_t pwr;
uint32_t freq; // 0: APRS region frequency (determined by geofencing), f>0 static frequency
mod_t mod;
uint16_t preamble;
uint32_t speed;
bool redundantTx;
} radio_conf_t; // Radio / Modulation
typedef struct {
char name[10];
// Radio
int8_t power;
freq_conf_t frequency;
mod_t modulation;
// Timing
uint32_t init_delay;
uint32_t packet_spacing;
bool active;
sysinterval_t init_delay;
sysinterval_t packet_spacing;
sleep_conf_t sleep_conf;
trigger_conf_t trigger;
sysinterval_t cycle; // Cycle time (0: continously)
} thread_conf_t; // Thread
// Modulation
union {
afsk_conf_t afsk_conf;
fsk_conf_t fsk_conf;
};
typedef struct {
thread_conf_t thread_conf;
radio_conf_t radio_conf;
// Protocol
aprs_conf_t aprs_conf;
ssdv_conf_t ssdv_conf;
char call[AX25_MAX_ADDR_LEN];
char path[16];
uint16_t symbol;
// Transmission
bool redundantTx; // Redundand packet transmission (APRS only)
sysinterval_t tel_enc_cycle;
char tel_comment[32];
} thd_pos_conf_t;
// Watchdog
sysinterval_t wdg_timeout; // Time at which watchdog will reset the STM32, 0 inactive
} module_conf_t;
typedef struct {
thread_conf_t thread_conf;
radio_conf_t radio_conf;
// Protocol
char call[AX25_MAX_ADDR_LEN];
char path[16];
resolution_t res;
uint8_t quality;
uint32_t buf_size;
} thd_img_conf_t;
typedef struct {
thread_conf_t thread_conf;
radio_conf_t radio_conf;
// Protocol
char call[AX25_MAX_ADDR_LEN];
char path[16];
} thd_log_conf_t;
typedef struct {
radio_conf_t radio_conf;
thread_conf_t thread_conf;
// Protocol
char call[AX25_MAX_ADDR_LEN];
char path[16];
uint16_t symbol;
} thd_rx_conf_t;
typedef struct {
thd_pos_conf_t pos_pri; // Primary position thread configuration
thd_pos_conf_t pos_sec; // Secondary position thread configuration
thd_img_conf_t img_pri; // Primary image thread configuration
thd_img_conf_t img_sec; // Secondary image thread configuration
thd_log_conf_t log; // Log transmission configuration
thd_rx_conf_t rx; // Receiver configuration
uint8_t rssi; // Squelch for receiption
bool dig_active; // Digipeater active flag
bool keep_cam_switched_on; // 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; // Battery voltage threshold at which GPS is switched on
uint16_t gps_off_vbat; // Battery voltage threshold at which GPS is switched off
uint16_t gps_onper_vbat; // 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
} conf_t;
#endif