kopia lustrzana https://github.com/DL7AD/pecanpico10
Changed configuration concept
rodzic
71b0b47f25
commit
e7c3dfc807
|
@ -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).
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ typedef struct {
|
|||
} coord_t;
|
||||
|
||||
uint32_t getAPRSRegionFrequency(void);
|
||||
uint32_t getFrequency(freq_conf_t *config);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue