Many new features: rx on both frequencies. APRS-IS igate.

New features:
- rx on main qrg, secondary qrg or both. (If we are not configured as WIDE1 or WIDE2 digi). QSY time depends on amount of other users heard, in  10min qindow.
- tx_qrg for beacon (if we ae configured as WIDE1 or WIDE2 digi)
- tx_power configurable on second frequency
- long waited aprs-is igtate feature. We provide a bid-direcional gateway.
- snr-in-path-encoding compatible for aprs-is (optional)

- added new parameter lora_SPEED to function loraSend()

Signed-off-by: Thomas Osterried <dl9sau@darc.de>
pull/1/head refs/heads/master-2022-01-19T094625
Thomas Osterried 2022-01-19 10:37:07 +01:00
rodzic b74442758a
commit a2db80ab04
4 zmienionych plików z 693 dodań i 129 usunięć

Wyświetl plik

@ -39,15 +39,15 @@
</div>
<div class="grid-container full">
<input class="button-primary" type="submit" value="Save">
</div>
</div>
</form>
</article>
</section>
<section>
<div class="grid-container full">
<h2 class="u-full-width">APRS Settings</h2>
</div>
<article>
<div class="grid-container full">
<h2 class="u-full-width">APRS Settings</h2>
</div>
<form action="/save_aprs_cfg" method="post">
<div class="grid-container full">
<h5 class="u-full-width">Transmission Settings</h5>
@ -73,17 +73,31 @@
<input name="lora_tx_en" id="lora_tx_en" type="checkbox" value="1" title="Allow TX on LoRa. Disable this if you like to prevent TX under all circumstances (i.e. if your tracker is behind an rx-amplifier)">
</div>
<div>
<label for="txPower">dBm</label>
<label for="txPower">TX power [dBm]</label>
<input name="txPower" id="txPower" type="number" min="0" max="23" title="LoRa TX Power. Range 0 to 23dBm">
</div>
<div>
<label for="lora_cradapt">Automatic CodeRate adaption</label>
<input name="lora_cradapt" id="lora_cradapt" type="checkbox" value="1" title="Enable automatic CR adaption. Still testing, if it behaves good to our network. Currently works only for SF12 modes, because for SF 10 and lower the code for programming CR+SF+BW-combination has not been written yet">
<label for="lora_cradapt">Automatic CodeRate adaption on TX</label>
<input name="lora_cradapt" id="lora_cradapt" type="checkbox" value="1" title="Enable automatic CR adaption. Use this only if you are not a WIDE1 or WIDE2 digi. Still testing, if it behaves good to our network. Currently works only for SF12 modes, because for SF 10 and lower the code for programming CR+SF+BW-combination has not been written yet">
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_rssi2p">Add SNR and RSSI to path</label>
<input name="lora_rssi2p" id="lora_rssi2p" type="checkbox" value="1" title="If enabled, add SNR and RSSI in path on receiption, prefixed with 'Q'. Example: SNR 23 and RSSI -73 will become Q2373*. SNR -03 RSSI -104: QA3K4*. SNR -13 RSSI -114: QB3L4*. .. Still testing, if it behaves good to our network.">
<select id="lora_rssi2p" name="lora_rssi2p">
<option value="0">off</option>
<option value="1">To RF during digipeating</option>
<option value="2">To KISS</option>
<option value="4">To APRSIS</option>
<option value="3">To RF and KISS</option>
<option value="5">To RF and APRSIS</option>
<option value="6">To KISS and APRSIS</option>
<option value="7">To RF and KISS and APRSIS</option>
</select>
</div>
<div>
<label for="snraprsis">SNR/RSSI-encoding on kiss: compatible to APRS-IS?</label>
<input name="snraprsis" id="snraprsis" type="checkbox" value=1 title="Add snr+rssi at last digipeater, without digipeated flag. Use this for APRSIS connections. If you have connected a digipeater software to kiss, this option has to be switched off">
</div>
</div>
<div class="grid-container full">
@ -92,7 +106,7 @@
<div class="grid-container quarters">
<div>
<label for="aprs_callsign">Callsign and SSID</label>
<input class="u-full-width" type="text" minlength="3" name="aprs_callsign" placeholder="NOCALL-1" id="aprs_callsign" title="your callsign with SSID">
<input class="u-full-width" type="text" minlength="3" maxlength="9" name="aprs_callsign" placeholder="NOCALL-1" id="aprs_callsign" title="your callsign with SSID">
</div>
<div>
<label for="aprs_relay_path">Relay Path</label>
@ -184,6 +198,8 @@
<label for="sb_max_interv">Max interval [s]</label>
<input name="sb_max_interv" id="sb_max_interv" type="number" min="120" title="Maximal time for Smart Beaconing">
</div>
</div>
<div class="grid-container quarters">
<div>
<label for="sb_min_speed">Min speed [km/h]</label>
<input name="sb_min_speed" id="sb_min_speed" type="number" min="0" title="Minimal speed for Smart Beaconing">
@ -192,6 +208,8 @@
<label for="sb_max_speed">Max speed [km/h]</label>
<input name="sb_max_speed" id="sb_max_speed" type="number" min="1" title="Maximal speed for Smart Beaconing">
</div>
</div>
<div class="grid-container quarters">
<div>
<label for="sb_angle">Course change [degrees]</label>
<input name="sb_angle" id="sb_angle" type="number" min="5" max="360" title="Angle of course change to speed up beacon transmission. Recommended value: 28">
@ -220,37 +238,24 @@
</div>
</div>
<div class="grid-container full">
<h6 class="u-full-width">Only for mode repater: additional settings <br/>EXPERIMANTAL - USE WITH CARE!</h6>
<h6 class="u-full-width">Additional settings for secondary frequency:<br/>EXPERIMANTAL - USE WITH CARE!</h6>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_mode">LoRa Repater Mode</label>
<select id="lora_dig_mode" name="lora_dig_mode">
<option value="0">off (default). This device does not do any repeating decision.</option>
<option value="1">Repeat if own call is addressed (recommended for normal users)</option>
<option value="2">Act as WIDE1 (fill-in) digi</option>
<option value="3">Act as a simple WIDE2 digi (consider to instead attach a real aprs-digipeater software via KISS)</option>
</select>
</div>
<br/>
<div>
<label for="lora_dig_x_m">If repeater mode enbabled: repeat on which frequency</label>
<select id="lora_dig_x_m" name="lora_dig_x_m">
<option value="0">Repeat only to main frequency (default)</option>
<option value="1">Repeat to both frequencies</option>
<option value="2">Repeat only to cross-digi frequency</option>
</select>
</div>
<br/>
<div>
<label for="lora_freq_x">Frequency for cross-repeating [MHz]</label>
<label for="lora_freq_x">Secondary Frequency [MHz]</label>
(for cross-repeating or listening transmissions from igates)</label>
<input name="lora_freq_x" id="lora_freq_x" type="number" min="0.000" max="928.000" step="0.001" title="LoRa center frequency - must be different from main frequency and between 433.001 and 928.000 or 0 if not needed. I.e. 433.900">
</div>
<br/>
<div>
<label for="lora_speed_x">Speed on cross digi frequency</label>
<label for="txPower_x">TX power on secondary frequency [dBm]</label>
<input name="txPower_x" id="txPower_x" type="number" min="0" max="23" title="LoRa TX Power on secondary frequency. Range 0 to 23dBm">
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_speed_x">Speed on secondary frequency</label>
<select id="lora_speed_x" name="lora_speed_x">
<option value="1200">BW 125khz CR 4:7 SF 9 (Fast Standard. default; use high speeds on the cross digi frequency)</option>
<option value="1200">BW 125khz CR 4:7 SF 9 (Fast Standard. default; use high speeds on the secondary frequency)</option>
<option value="610">BW 125khz CR 4:8 SF 10 (610bps)</option>
<option value="300">BW 125khz CR 4:5 SF 12 (300bps)</option>
<option value="240">BW 125khz CR 4:6 SF 12 (244bps)</option>
@ -259,6 +264,92 @@
</select>
</div>
</div>
<div class="grid-container halves">
<div>
<label for="rx_qrg">RX on frequencies</label>
(Only honored, if we are NOT configured as WIDE1 or WIDE2 digi. If set to both frequencies: Ratio depends on how many packets are received on each qrg in a 10min window; we stay 20s on one qrg.)
<select id="rx_qrg" name="rx_qrg">
<option value="1">RX on main frequency</option>
<option value="2">RX on secondary frequency</option>
<option value="3">RX on both frequencies</option>
</select>
</div>
<div>
<label for="lora_freq_rx_curr">Current RX Frequency:</label>
<input name="lora_freq_rx_curr" id="lora_freq_rx_curr" type="number" min="0.000" title="Nothing to enter here"</input>
</div>
</div>
<div class="grid-container full">
<h6 class="u-full-width">Additional settings for mode repater:<br/>EXPERIMANTAL - USE WITH CARE!</h6>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_mode">LoRa Repater Mode</label>
<select id="lora_dig_mode" name="lora_dig_mode">
<option value="0">off. This device does not do any repeating decision.</option>
<option value="1">Repeat if own call is addressed (recommended for normal users). default.</option>
<option value="2">Act as WIDE1 (fill-in) digi</option>
<option value="3">Act as a simple WIDE2 digi (consider to instead attach a real aprs-digipeater software via KISS)</option>
</select>
</div>
</div>
<div class="grid-container halves">
<div>
<label for="lora_dig_x_m">Digipeat heard stations on which frequencies</label>
(If LoRa Repeater Mode has not been set to off)
<select id="lora_dig_x_m" name="lora_dig_x_m">
<option value="0">Repeat only to main frequency (default)</option>
<option value="1">Repeat to both frequencies</option>
<option value="2">Repeat only to cross-digi frequency</option>
</select>
</div>
<div>
<label for="tx_qrg_bc">TX beacon or from-kiss on frequencies</label>
(Only honored, if we are configured as WIDE1 or WIDE2 digi)
<select id="tx_qrg_bc" name="tx_qrg_bc">
<option value="1">TX on main frequency</option>
<option value="2">TX on secondary frequency</option>
<option value="3">TX on both frequencies</option>
</select>
</div>
</div>
<div class="grid-container full">
<h5 class="u-full-width">APRS-IS settings</h5>
</div>
<div class="grid-container halves">
<div>
<label for="aprsis_en">Enable APRS-IS connection</label>
<input name="aprsis_en" id="aprsis_en" type="checkbox" value="1" title="If we are configured as WIFI client, connet to the APRS-IS net">
</div>
<div>
<label for="aprsis_srv_h">Server Name</label>
<input type="text" name="aprsis_srv_h" id="aprsis_srv_h" title="Server name or IP Address. I.e. euro.aprs2.net">
</div>
<div>
<label for="aprsis_srv_p">TCP Port</label>
<input type="number" name="aprsis_srv_p" id="aprsis_srv_p" min="1" max="65535" title="TCP Port, i.e. 14580">
</div>
<div>
<label for="aprsis_fltr">Filter (optional)</label>
<input type="text" name="aprsis_fltr" id="aprsis_fltr" title="Request server-site filter (may be left empty). See http://www.aprs-is.net/javAPRSFilter.aspx" placeholder="may be left blank">
</div>
<div>
<label for="aprsis_call">Callsign (optional)</label>
<input type="text" name="aprsis_call" id="aprsis_call" minlength="3" maxlength="9" title="Use this callsign for the APRS-IS connection. If not configured, your default callsign is used." placeholder="may be left blank">
</div>
<div>
<label for="aprsis_pw">Password (required)</label>
<input type="password" name="aprsis_pw" id="aprsis_pw" title="Your password for the APRS-IS connection.">
</div>
<div>
<label for="aprsis_2rf">Allow traffic from inet to rf</label>
<input name="aprsis_2rf" id="aprsis_2rf" type="checkbox" value="1" title="Gate APRS-IS traffic to rf (will be encoded in 3rd-party format)">
</div>
<div>
<label for"aprsis_status">Connection status</label>
<input type="text" name="aprsis_status" id="aprsis_status" title="Connection status. Nothing to enter here">
</div>
</div>
<div class="grid-container full">
<div>
<input class="button-primary u-full-width" type="submit" value="Save" title="save settings, remember reboot tracker after save">

Wyświetl plik

@ -10,6 +10,7 @@ extern Preferences preferences;
static const char *const PREF_WIFI_SSID = "wifi_ssid";
static const char *const PREF_WIFI_PASSWORD = "wifi_password";
static const char *const PREF_AP_PASSWORD = "ap_password";
// LoRa settings
static const char *const PREF_LORA_FREQ_PRESET_INIT = "lora_freq_i";
static const char *const PREF_LORA_FREQ_PRESET = "lora_freq";
@ -21,10 +22,18 @@ static const char *const PREF_LORA_TX_POWER_INIT = "txPower_i";
static const char *const PREF_LORA_TX_POWER = "txPower";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET_INIT = "lora_rssi2p_i";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET = "lora_rssi2p";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET_INIT = "snraprsis_i";
static const char *const PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET = "snraprsis";
static const char *const PREF_LORA_FREQ_CROSSDIGI_PRESET_INIT = "lora_freq_x_i";
static const char *const PREF_LORA_FREQ_CROSSDIGI_PRESET = "lora_freq_x";
static const char *const PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET_INIT = "tx_qrg_bc_i";
static const char *const PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET = "tx_qrg_bc";
static const char *const PREF_LORA_SPEED_CROSSDIGI_PRESET_INIT = "lora_speed_x_i";
static const char *const PREF_LORA_SPEED_CROSSDIGI_PRESET = "lora_speed_x";
static const char *const PREF_LORA_TX_POWER_CROSSDIGI_PRESET_INIT = "txPower_x_i";
static const char *const PREF_LORA_TX_POWER_CROSSDIGI_PRESET = "txPower_x";
static const char *const PREF_LORA_RX_ON_FREQUENCIES_PRESET_INIT = "rx_qrg_i";
static const char *const PREF_LORA_RX_ON_FREQUENCIES_PRESET = "rx_qrg";
static const char *const PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET_INIT = "lora_cradapt_i";
static const char *const PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET = "lora_cradapt";
static const char *const PREF_APRS_DIGIPEATING_MODE_PRESET_INIT = "lora_dig_mode_i";
@ -103,4 +112,20 @@ static const char *const PREF_DEV_AUTO_SHUT_PRESET_INIT = "shutdown_dtini";
static const char *const PREF_DEV_SHOW_OLED_TIME = "sh_oledtime"; // set OLED timeout
static const char *const PREF_DEV_SHOW_OLED_TIME_INIT = "sh_oledtime_i";
// APRSIS settings
static const char *const PREF_APRSIS_EN_INIT = "aprsis_en_i";
static const char *const PREF_APRSIS_EN = "aprsis_en";
static const char *const PREF_APRSIS_SERVER_NAME_INIT = "aprsis_srv_h_i";
static const char *const PREF_APRSIS_SERVER_NAME = "aprsis_srv_h";
static const char *const PREF_APRSIS_SERVER_PORT_INIT = "aprsis_srv_p_i";
static const char *const PREF_APRSIS_SERVER_PORT = "aprsis_srv_p";
static const char *const PREF_APRSIS_FILTER_INIT = "aprsis_fltr_i";
static const char *const PREF_APRSIS_FILTER = "aprsis_fltr";
static const char *const PREF_APRSIS_CALLSIGN_INIT = "aprsis_call_i";
static const char *const PREF_APRSIS_CALLSIGN = "aprsis_call";
static const char *const PREF_APRSIS_PASSWORD_INIT = "aprsis_pw_i";
static const char *const PREF_APRSIS_PASSWORD = "aprsis_pw";
static const char *const PREF_APRSIS_ALLOW_INET_TO_RF_INIT = "aprsis_2rf_i";
static const char *const PREF_APRSIS_ALLOW_INET_TO_RF = "aprsis_2rf";
#endif

Wyświetl plik

@ -110,6 +110,20 @@
ulong lora_speed_cross_digi = 1200;
double lora_freq_cross_digi = 433.900;
double lora_freq_rx_curr = lora_freq;
ulong lora_speed_rx_curr = lora_speed;
// Variables for WIFI APRS-IS connection. Requires ENABLE_WIFI
#ifdef ENABLE_WIFI
boolean aprsis_enabled = false;
String aprsis_host = "euro.aprs2.net";
uint16_t aprsis_port = 14580;
String aprsis_filter = "";
String aprsis_callsign = "";
String aprsis_password = "-1";
boolean aprsis_data_allow_inet_to_rf = true;
#endif
// Variables for APRS packaging
String Tcall; //your Call Sign for normal position reports
String aprsSymbolTable = APRS_SYMBOL_TABLE;
@ -261,30 +275,39 @@ float avg_c_y, avg_c_x;
#ifdef TXDISABLE // define TXDISABLE if you like to ensure that we never TX (i.e. if we are behind an rx-amplifier)
boolean lora_tx_enabled = false;
uint8_t txPower = 0;
uint8_t txPower_cross_digi = 0;
#else
boolean lora_tx_enabled = true;
#ifdef TXdbmW
uint8_t txPower = TXdbmW;
uint8_t txPower_cross_digi = TXdbmW;
#else
uint8_t txPower = 23;
uint8_t txPower_cross_digi = 23;
#endif
#endif
// may be configured
boolean rate_limit_message_text = true; // ratelimit adding messate text (-> saves airtime)
boolean lora_automatic_cr_adaption = false; // automatic CR adaption (should be configurable in Web-Interface).
boolean lora_automatic_cr_adaption = false; // automatic CR adaption
// We'll count users (except own digipeated packets), and if we're alone, CR4/8 doesn't disturb anyone.
// If we have a high load on the channel, we'll decrease up to CR4/5.
// You may set this to off if you are a fixed station / repeater / gateway
// This may become set to true by default, after it proves it behaves good to our network
uint8_t lora_digipeating_mode = 1; // Digipeating: 0: disabled (recommended if the device should not do repeating decisions, and even more, if you have attached a normal aprs digipeating software via kiss). 1: if own call addressed (recommended for users) 2: act as WIDE1 fill-in digi (recommended for standalone fill-in-digi). 3: act as a simple stupid WIDE2 digi
uint8_t lora_cross_digipeating_mode = 0; // 0: disable cross freq digipeating. 1: send on both frequencies. 2: send only on cross frequency
boolean lora_add_snr_rssi_to_path = true; // Add snr+rssi to path. May become default, after it proves it behaves good to our network
#define FLAG_ADD_SNR_RSSI_FOR_RF 1
#define FLAG_ADD_SNR_RSSI_FOR_KISS 2
#define FLAG_ADD_SNR_RSSI_FOR_APRSIS 4
uint8_t lora_add_snr_rssi_to_path = (FLAG_ADD_SNR_RSSI_FOR_RF | FLAG_ADD_SNR_RSSI_FOR_KISS | FLAG_ADD_SNR_RSSI_FOR_APRSIS); // Add snr+rssi to path. May become default, after it proves it behaves good to our network
boolean kiss_add_snr_rssi_to_path_at_position_without_digippeated_flag = 1; // Add snr+rssi at last digipeater, without digipeated flag, at last position in path. Set to 1, if you pass data to aprs-is. Set to 0 if you pass data to your favourite digipeater software. We need this hack because our rssi-encoded data should not be interpreted as "(last ==) direct heard station" in the aprs-is net.
int tx_beacon_and_fromKiss_on_frequencies = 1; // TX beacon or from-kiss on following frequencies. Only if lora_digipeating_mode > 1 (we are a WIDE1 or WIDE2 digi). 1: main freq. 2: cross_digi_freq. 3: both frequencies
int rx_on_frequencies = 1; // RX freq. Only if lora_digipeating_mode < 2 (we are a user) 1: main freq. 2: cross_digi_freq. 3: both frequencies
#ifdef KISS_PROTOCOL
bool acceptOwnPositionReportsViaKiss = true; // may be a web-configurable variable true: Switches off local beacons as long as a kiss device is sending positions with our local callsign. false: filters out position packets with own callsign coming from kiss (-> do not send to LoRa).
boolean gps_allow_sleep_while_kiss = true; // may be a web-configurable variable. If user has a kiss device attached via kiss which sends positions with own call, we don't need our gps to be turned on -> We pause sending positions by ourself (neither fixed nor smart beaconing). Except: user has a display attached to this tracker, he'll will be able to see his position because our gps does not go to sleep (-> set this to false). Why sleep? Energy saving
bool acceptOwnPositionReportsViaKiss = true; // true: Switches off local beacons as long as a kiss device is sending positions with our local callsign. false: filters out position packets with own callsign coming from kiss (-> do not send to LoRa).
boolean gps_allow_sleep_while_kiss = true; // user has a kiss device attached via kiss which sends positions with own call, we don't need our gps to be turned on -> We pause sending positions by ourself (neither fixed nor smart beaconing). Except: user has a display attached to this tracker, he'll will be able to see his position because our gps does not go to sleep (-> set this to false). Why sleep? Energy saving
// do not configure
uint32_t time_last_own_position_via_kiss_received = 0L; // kiss client sends position report with our call+ssid. Remember when.
@ -295,15 +318,18 @@ boolean kiss_client_came_via_bluetooth = false;
// do not configure
boolean dont_send_own_position_packets = false; // dynamicaly set if kiss device sends position. Maybe there are other usecases (-> kiss-independent)
boolean gps_state_before_autochange = false; // remember gps state before autochange
uint32_t time_last_lora_frame_received = 0L;
uint32_t time_last_lora_frame_received_on_main_freq = 0L;
uint32_t time_last_own_text_message_via_kiss_received = 0L;
uint32_t time_lora_automaic_cr_adoption_rx_measurement_window = 0L;
uint16_t lora_automaic_cr_adoption_rf_transmissions_heard_in_timeslot = 0;
uint16_t lora_packets_received_in_timeslot_on_main_freq = 0;
uint16_t lora_packets_received_in_timeslot_on_secondary_freq = 0;
char lora_TXBUFF_for_digipeating[BG_RF95_MAX_MESSAGE_LEN+1] = ""; // buffer for digipeating
time_t time_lora_TXBUFF_for_digipeating_was_filled = 0L;
#ifdef ENABLE_WIFI
tWebServerCfg webServerCfg;
String to_aprsis_data = "";
#endif
static const adc_atten_t atten = ADC_ATTEN_DB_6;
@ -453,33 +479,8 @@ void buzzer(int* melody, int array_size){
}
#endif
void sendpacket(){
#ifdef BUZZER
int melody[] = {1000, 50, 800, 100};
buzzer(melody, sizeof(melody)/sizeof(int));
#endif
batt_read();
prepareAPRSFrame();
loraSend(txPower, lora_freq, outString); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
}
/**
* Send message as APRS LoRa packet
* @param lora_LTXPower
* @param lora_FREQ
* @param message
*/
// Feauture request: add param lora_speed. Currently, we need a variable for storing the old speed, and affer loraSend(), we have to revert :(
void loraSend(byte lora_LTXPower, float lora_FREQ, const String &message) {
if (!lora_tx_enabled)
return;
#ifdef ENABLE_LED_SIGNALING
digitalWrite(TXLED, LOW);
#endif
lastTX = millis();
int messageSize = min(message.length(), sizeof(lora_TXBUFF) - 1);
message.toCharArray((char*)lora_TXBUFF, messageSize + 1, 0);
void lora_set_speed(ulong lora_speed) {
if(lora_speed==1200){
rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512);
}
@ -498,6 +499,52 @@ void loraSend(byte lora_LTXPower, float lora_FREQ, const String &message) {
else {
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096);
}
}
#if defined(ENABLE_WIFI)
void send_to_aprsis(String s)
{
to_aprsis_data = s;
return;
}
#endif
void sendpacket(){
#ifdef BUZZER
int melody[] = {1000, 50, 800, 100};
buzzer(melody, sizeof(melody)/sizeof(int));
#endif
batt_read();
prepareAPRSFrame();
if (lora_tx_enabled) {
if (tx_beacon_and_fromKiss_on_frequencies % 2)
loraSend(txPower, lora_freq, lora_speed, outString); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
if (tx_beacon_and_fromKiss_on_frequencies > 1 && lora_digipeating_mode > 1)
loraSend(txPower_cross_digi, lora_freq_cross_digi, lora_speed_cross_digi, outString); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
}
#if defined(ENABLE_WIFI)
send_to_aprsis(outString);
#endif
}
/**
* Send message as APRS LoRa packet
* @param lora_LTXPower
* @param lora_FREQ
* @param lora_SPEED
* @param message
*/
void loraSend(byte lora_LTXPower, float lora_FREQ, ulong lora_SPEED, const String &message) {
if (!lora_tx_enabled)
return;
#ifdef ENABLE_LED_SIGNALING
digitalWrite(TXLED, LOW);
#endif
lastTX = millis();
int messageSize = min(message.length(), sizeof(lora_TXBUFF) - 1);
message.toCharArray((char*)lora_TXBUFF, messageSize + 1, 0);
lora_set_speed(lora_SPEED);
rf95.setFrequency(lora_FREQ);
rf95.setTxPower(lora_LTXPower);
rf95.sendAPRS(lora_TXBUFF, messageSize);
@ -506,8 +553,10 @@ void loraSend(byte lora_LTXPower, float lora_FREQ, const String &message) {
digitalWrite(TXLED, HIGH);
#endif
// cross-digipeating may have altered our RX-frequency. Revert frequency change needed for this transmission.
if (lora_FREQ != lora_freq)
rf95.setFrequency(lora_freq);
if (lora_FREQ != lora_freq_rx_curr)
rf95.setFrequency(lora_freq_rx_curr);
if (lora_SPEED != lora_speed_rx_curr)
lora_set_speed(lora_speed_rx_curr);
}
void batt_read(){
@ -717,6 +766,7 @@ String prepareCallsign(const String& callsign){
}
#endif
// + SETUP --------------------------------------------------------------+//
void setup(){
#ifdef T_BEAM_V0_7 /*
@ -780,7 +830,7 @@ void setup(){
}
lora_tx_enabled = preferences.getBool(PREF_LORA_TX_ENABLE);
if (!preferences.getInt(PREF_LORA_TX_POWER_INIT)){
if (!preferences.getBool(PREF_LORA_TX_POWER_INIT)){
preferences.putBool(PREF_LORA_TX_POWER_INIT, true);
preferences.putInt(PREF_LORA_TX_POWER, txPower);
}
@ -794,9 +844,15 @@ void setup(){
if (!preferences.getBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET_INIT)){
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET_INIT, true);
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET, lora_add_snr_rssi_to_path);
preferences.putInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET, lora_add_snr_rssi_to_path);
}
lora_add_snr_rssi_to_path = preferences.getBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
lora_add_snr_rssi_to_path = preferences.getInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
if (!preferences.getBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET_INIT)){
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET_INIT, true);
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET, kiss_add_snr_rssi_to_path_at_position_without_digippeated_flag);
}
kiss_add_snr_rssi_to_path_at_position_without_digippeated_flag = preferences.getBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET);
if (!preferences.getBool(PREF_APRS_DIGIPEATING_MODE_PRESET_INIT)){
preferences.putBool(PREF_APRS_DIGIPEATING_MODE_PRESET_INIT, true);
@ -810,19 +866,36 @@ void setup(){
}
lora_cross_digipeating_mode = preferences.getInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET);
if (!preferences.getBool(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET_INIT)){
preferences.putBool(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET_INIT, true);
preferences.putInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET, tx_beacon_and_fromKiss_on_frequencies);
}
tx_beacon_and_fromKiss_on_frequencies = preferences.getInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET);
if (!preferences.getBool(PREF_LORA_FREQ_CROSSDIGI_PRESET_INIT)){
preferences.putBool(PREF_LORA_FREQ_CROSSDIGI_PRESET_INIT, true);
preferences.putDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET, lora_freq_cross_digi);
}
lora_freq_cross_digi = preferences.getDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET);
if (!preferences.getBool(PREF_LORA_SPEED_CROSSDIGI_PRESET_INIT)){
preferences.putBool(PREF_LORA_SPEED_CROSSDIGI_PRESET_INIT, true);
preferences.putInt(PREF_LORA_SPEED_CROSSDIGI_PRESET, lora_speed_cross_digi);
}
lora_speed_cross_digi = preferences.getInt(PREF_LORA_SPEED_CROSSDIGI_PRESET);
if (!preferences.getBool(PREF_LORA_TX_POWER_CROSSDIGI_PRESET_INIT)){
preferences.putBool(PREF_LORA_TX_POWER_CROSSDIGI_PRESET_INIT, true);
preferences.putInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET, txPower_cross_digi);
}
txPower_cross_digi = lora_tx_enabled ? preferences.getInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET) : 0;
if (!preferences.getBool(PREF_LORA_RX_ON_FREQUENCIES_PRESET_INIT)){
preferences.putBool(PREF_LORA_RX_ON_FREQUENCIES_PRESET_INIT, true);
preferences.putInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET, rx_on_frequencies);
}
rx_on_frequencies = preferences.getInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET);
// APRS station settings
aprsSymbolTable = preferences.getString(PREF_APRS_SYMBOL_TABLE);
@ -1009,13 +1082,6 @@ void setup(){
}
shutdown_active = preferences.getBool(PREF_DEV_AUTO_SHUT);
if (clear_preferences){
delay(1000);
if(digitalRead(BUTTON)==LOW){
clear_preferences = 2;
}
}
if (!preferences.getBool(PREF_APRS_SHOW_CMT_INIT)){
preferences.putBool(PREF_APRS_SHOW_CMT_INIT, true);
preferences.putBool(PREF_APRS_SHOW_CMT, show_cmt);
@ -1040,17 +1106,71 @@ void setup(){
preferences.putBool(PREF_DEV_OL_EN,enabled_oled);
}
enabled_oled = preferences.getBool(PREF_DEV_OL_EN);
// APRSIS settings
#ifdef ENABLE_WIFI
if (!preferences.getBool(PREF_APRSIS_EN_INIT)){
preferences.putBool(PREF_APRSIS_EN_INIT, true);
preferences.putBool(PREF_APRSIS_EN, aprsis_enabled);
}
aprsis_enabled = preferences.getBool(PREF_APRSIS_EN);
if (!preferences.getBool(PREF_APRSIS_SERVER_NAME_INIT)){
preferences.putBool(PREF_APRSIS_SERVER_NAME_INIT, true);
preferences.putString(PREF_APRSIS_SERVER_NAME, aprsis_host);
}
aprsis_host = preferences.getString(PREF_APRSIS_SERVER_NAME);
if (!preferences.getBool(PREF_APRSIS_SERVER_PORT_INIT)){
preferences.putBool(PREF_APRSIS_SERVER_PORT_INIT, true);
preferences.putInt(PREF_APRSIS_SERVER_PORT, aprsis_port);
}
aprsis_port = preferences.getInt(PREF_APRSIS_SERVER_PORT);
if (!preferences.getBool(PREF_APRSIS_FILTER_INIT)){
preferences.putBool(PREF_APRSIS_FILTER_INIT, true);
preferences.putString(PREF_APRSIS_FILTER, aprsis_filter);
}
aprsis_filter = preferences.getString(PREF_APRSIS_FILTER);
if (!preferences.getBool(PREF_APRSIS_CALLSIGN_INIT)){
preferences.putBool(PREF_APRSIS_CALLSIGN_INIT, true);
preferences.putString(PREF_APRSIS_CALLSIGN, aprsis_callsign);
}
aprsis_callsign = preferences.getString(PREF_APRSIS_CALLSIGN);
if (!preferences.getBool(PREF_APRSIS_PASSWORD_INIT)){
preferences.putBool(PREF_APRSIS_PASSWORD_INIT, true);
preferences.putString(PREF_APRSIS_PASSWORD, aprsis_password);
}
aprsis_password = preferences.getString(PREF_APRSIS_PASSWORD);
if (!preferences.getBool(PREF_APRSIS_ALLOW_INET_TO_RF_INIT)){
preferences.putBool(PREF_APRSIS_ALLOW_INET_TO_RF_INIT, true);
preferences.putBool(PREF_APRSIS_ALLOW_INET_TO_RF, aprsis_data_allow_inet_to_rf);
}
aprsis_data_allow_inet_to_rf = preferences.getBool(PREF_APRSIS_ALLOW_INET_TO_RF);
#endif
if (clear_preferences){
delay(1000);
if(digitalRead(BUTTON)==LOW){
clear_preferences = 2;
}
}
#endif
// enforce valid transmissions even on wrong configurations
if (aprsSymbolTable.length() != 1)
aprsSymbolTable = String("/");
if (aprsSymbol.length() != 1)
aprsSymbol = String("[");
if (aprsLatPreset.length() != 8 || !(aprsLatPreset.endsWith("N") || aprsLatPreset.endsWith("S")) || aprsLatPreset.c_str()[4] != '.')
aprsLatPreset = String("0000.00N");
if (aprsLonPreset.length() != 9 || !(aprsLonPreset.endsWith("E") || aprsLonPreset.endsWith("W")) || aprsLonPreset.c_str()[5] != '.')
aprsLonPreset = String("00000.00E");
// enforce valid transmissions even on wrong configurations
if (aprsSymbolTable.length() != 1)
aprsSymbolTable = String("/");
if (aprsSymbol.length() != 1)
aprsSymbol = String("[");
if (aprsLatPreset.length() != 8 || !(aprsLatPreset.endsWith("N") || aprsLatPreset.endsWith("S")) || aprsLatPreset.c_str()[4] != '.')
aprsLatPreset = String("0000.00N");
if (aprsLonPreset.length() != 9 || !(aprsLonPreset.endsWith("E") || aprsLonPreset.endsWith("W")) || aprsLonPreset.c_str()[5] != '.')
aprsLonPreset = String("00000.00E");
for (int i=0;i<ANGLE_AVGS;i++) { // set average_course to "0"
average_course[i]=0;
@ -1141,30 +1261,14 @@ if (aprsLonPreset.length() != 9 || !(aprsLonPreset.endsWith("E") || aprsLonPrese
batt_read();
writedisplaytext("LoRa-APRS","","Init:","ADC OK!","BAT: "+String(BattVolts,2),"");
if(lora_speed==1200){
rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512);
}
else if(lora_speed==610){
rf95.setModemConfig(BG_RF95::Bw125Cr48Sf1024);
}
else if(lora_speed==180){
rf95.setModemConfig(BG_RF95::Bw125Cr48Sf4096);
}
else if(lora_speed==210){
rf95.setModemConfig(BG_RF95::Bw125Cr47Sf4096);
}
else if(lora_speed==240){
rf95.setModemConfig(BG_RF95::Bw125Cr46Sf4096);
}
else {
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096);
}
lora_speed_rx_curr = ((rx_on_frequencies != 2 || lora_digipeating_mode < 2) ? lora_speed : lora_speed_cross_digi);
lora_set_speed(lora_speed_rx_curr);
Serial.printf("LoRa Speed:\t%lu\n", lora_speed);
Serial.printf("LoRa Speed:\t%lu\n", lora_speed_rx_curr);
rf95.setFrequency(lora_freq);
rf95.setFrequency((rx_on_frequencies != 2 || lora_digipeating_mode < 2) ? lora_freq : lora_freq_cross_digi);
Serial.printf("LoRa FREQ:\t%f\n", lora_freq);
rf95.setTxPower(txPower);
rf95.setTxPower((rx_on_frequencies != 2 || lora_digipeating_mode < 2) ? txPower : txPower_cross_digi);
delay(250);
#ifdef KISS_PROTOCOL
xTaskCreatePinnedToCore(taskTNC, "taskTNC", 10000, nullptr, 1, nullptr, xPortGetCoreID());
@ -1279,7 +1383,7 @@ char *encode_snr_rssi_in_path()
char *add_element_to_path(const char *data, const char *element)
{
static char buf[BG_RF95_MAX_MESSAGE_LEN+1];
if (strlen(data) + 1 /* ',' */ + strlen(element) > sizeof(buf)-1)
if (strlen(data) + 1 /* ',' */ + strlen(element) + 1 /* '*' */ > sizeof(buf)-1)
return 0;
char *p = strchr(data, '>');
char *header_end = strchr(data, ':');
@ -1324,6 +1428,26 @@ char *add_element_to_path(const char *data, const char *element)
return buf;
}
// append element to path, regardless if it will exceep max digipeaters. It's for snr encoding for aprs-is. We don't us
// add_element_to_path, because we will append at the last position, and do not change digipeated bit.
char *append_element_to_path(const char *data, const char *element) {
static char buf[BG_RF95_MAX_MESSAGE_LEN+10+1];
if (strlen(data) + 1 /* ',' */ + strlen(element) > sizeof(buf)-1)
return 0;
char *p = strchr(data, '>');
char *header_end = strchr(data, ':');
if (header_end <= p)
return 0;
char *q = strchr(data, ',');
if (q > header_end)
q = 0;
if (q && q < p)
return 0;
snprintf(buf, header_end-data +1, "%s", data);
sprintf(buf + strlen(buf), ",%s%s", element, header_end);
return buf;
}
#define AX_ADDR_LEN 9 // room for "DL9SAU-15" == 9
#define AX_DIGIS_MAX 8
@ -1435,6 +1559,11 @@ void handle_lora_frame_for_lora_digipeating(const char *received_frame, char *sn
if (frame->n_digis > AX_DIGIS_MAX)
return;
// aprs-message / query addressed to us? Format: ":DL9SAU-15:..."
if (frame->data[0] == ':' && strlen(frame->data) > 10 && frame->data[10] == ':' &&
!strncmp((frame->data)+1, Tcall.c_str(), Tcall.length()) && (Tcall.length() == 9 || frame->data[9] == ' '))
return;
// '>' and ':' found. Always in header. Sanity check: if ',' present, it must be > p. If r exists, must be > q. And header_end must be > than the others and always a message-body *(header_end + 1) != 0.
// wide1-digi case
@ -1764,6 +1893,18 @@ void loop() {
if (xQueueReceive(tncToSendQueue, &TNC2DataFrame, (1 / portTICK_PERIOD_MS)) == pdPASS) {
time_last_frame_via_kiss_received = millis();
const char *data = TNC2DataFrame->c_str();
#if defined(ENABLE_WIFI)
// No word "NOGATE" or "RFONLY" in header? -> may be sent to aprs-is
char *q = strstr(data, ",NOGATE");
if (!q || q > strchr(data, ':')) {
q = strstr(data, ",RFONLY");
if (!q || q > strchr(data, ':')) {
send_to_aprsis(*TNC2DataFrame);
}
}
#endif
// Frame comes from same call as ours and is a position report?
if (!strncmp(data, Tcall.c_str(), Tcall.length()) && data[Tcall.length()] == '>') {
char *p = strchr(data, ':');
@ -1845,11 +1986,15 @@ void loop() {
}
}
if (lora_tx_enabled) {
loraSend(txPower, lora_freq, String(data));
if (tx_beacon_and_fromKiss_on_frequencies % 2)
loraSend(txPower, lora_freq, lora_speed, String(data)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
if (tx_beacon_and_fromKiss_on_frequencies > 1 && lora_digipeating_mode > 1)
loraSend(txPower_cross_digi, lora_freq_cross_digi, lora_speed_cross_digi, String(data)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
enableOled(); // enable OLED
writedisplaytext("((KISSTX))","","","","","");
time_to_refresh = millis() + showRXTime;
}
out:
delete TNC2DataFrame;
}
@ -1877,6 +2022,7 @@ out:
}
const char *received_frame = loraReceivedFrameString.c_str();
// CR adaption: because only for SF12 different CR levels have been defined, we unfortunately cannot deal with SF < 12.
// In most cases, only useful for normal users, not for WIDE1 or WIDE2 digis. But there may exist good reasons; thus we don't enforce.
if (lora_automatic_cr_adaption && lora_speed <= 300L) {
// not our own digipeated call?
if (! (strncmp(received_frame, Tcall.c_str(), Tcall.length()) == 0 && received_frame[Tcall.length()] == '>')) {
@ -1889,7 +2035,27 @@ out:
lora_automaic_cr_adoption_rf_transmissions_heard_in_timeslot++;
}
}
time_last_lora_frame_received = millis();
if (lora_freq_rx_curr == lora_freq) {
time_last_lora_frame_received_on_main_freq = millis();
lora_packets_received_in_timeslot_on_main_freq++;
} else {
lora_packets_received_in_timeslot_on_secondary_freq++;
}
#if defined(ENABLE_WIFI)
// No word "NOGATE" or "RFONLY" in header? -> may be sent to aprs-is
char *q = strstr(received_frame, ",NOGATE");
if (!q || q > strchr(received_frame, ':')) {
q = strstr(received_frame, ",RFONLY");
if (!q || q > strchr(received_frame, ':')) {
char *s = 0;
if (lora_add_snr_rssi_to_path & FLAG_ADD_SNR_RSSI_FOR_APRSIS)
s = append_element_to_path(received_frame, encode_snr_rssi_in_path());
send_to_aprsis(s ? String(s) : loraReceivedFrameString);
}
}
#endif
#ifdef SHOW_RX_PACKET // only show RX packets when activitated in config
writedisplaytext(" ((RX))", "", loraReceivedFrameString, "", "", "");
#ifdef ENABLE_WIFI
@ -1899,24 +2065,24 @@ out:
#endif
#ifdef KISS_PROTOCOL
char *s = 0;
if (lora_add_snr_rssi_to_path)
s = add_element_to_path(received_frame, encode_snr_rssi_in_path());
if (lora_add_snr_rssi_to_path & FLAG_ADD_SNR_RSSI_FOR_KISS) {
s = kiss_add_snr_rssi_to_path_at_position_without_digippeated_flag ? append_element_to_path(received_frame, encode_snr_rssi_in_path()) : add_element_to_path(received_frame, encode_snr_rssi_in_path());
}
sendToTNC(s ? String(s) : loraReceivedFrameString);
#endif
// Are we configured as lora digi?
if (lora_digipeating_mode > 0 && lora_tx_enabled) {
uint32_t time_lora_TXBUFF_for_digipeating_was_filled_prev = time_lora_TXBUFF_for_digipeating_was_filled;
handle_lora_frame_for_lora_digipeating(received_frame, encode_snr_rssi_in_path());
char *snrrssi = encode_snr_rssi_in_path();
if (!(lora_add_snr_rssi_to_path & FLAG_ADD_SNR_RSSI_FOR_RF)) *snrrssi = 0;
handle_lora_frame_for_lora_digipeating(received_frame, snrrssi);
// new frame in digipeating queue? cross-digi freq enabled and freq set? Send without delay.
if (*lora_TXBUFF_for_digipeating && lora_cross_digipeating_mode > 0 && lora_freq_cross_digi > 1.0 && lora_freq_cross_digi != lora_freq && time_lora_TXBUFF_for_digipeating_was_filled > time_lora_TXBUFF_for_digipeating_was_filled_prev) {
// word NOGATE part of the header? Don't gate it
// word 'NOGATE' part of the header? Don't gate it
char *q = strstr(lora_TXBUFF_for_digipeating, ",NOGATE");
if (!q || q > strchr(lora_TXBUFF_for_digipeating, ':')) {
ulong lora_speed_prev = lora_speed;
lora_speed = lora_speed_cross_digi;
loraSend(txPower, lora_freq_cross_digi, String(lora_TXBUFF_for_digipeating)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
lora_speed = lora_speed_prev; // we really need an argument for speed in loraSend()
loraSend(txPower_cross_digi, lora_freq_cross_digi, lora_speed_cross_digi, String(lora_TXBUFF_for_digipeating)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
writedisplaytext(" ((TX cross-digi))", "", String(lora_TXBUFF_for_digipeating), "", "", "");
#ifdef KISS_PROTOCOL
char *s = add_element_to_path(lora_TXBUFF_for_digipeating, "GATE");
@ -1932,6 +2098,59 @@ out:
#endif
#endif
}
if (rx_on_frequencies == 3 && lora_digipeating_mode < 2) {
static uint8_t slot_table[9][10] = {
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // 1:9
{ 0, 1, 1, 1, 1, 0, 1, 1, 1, 1 }, // 2:8
{ 0, 1, 1, 0, 1, 1, 1, 0, 1, 1 }, // 3:7
{ 0, 1, 0, 1, 1, 0, 1, 0, 1, 1 }, // 4:6
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, // 5:5
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1 }, // 6:4. From here it's inverse to 4:6, but time-slots left-shifted by one, so first position is alwas the main frequency
{ 0, 0, 1, 0, 0, 0, 1, 0, 0, 1 }, // 7:3
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, // 8:2
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } // 9:1
};
static uint8_t *curr_slot_table = slot_table[4];
static uint8_t *p_curr_slot_table = curr_slot_table;
static uint32_t next_slot = millis() + 20*1000L;
static uint32_t recompute_rx_freq_window = millis() + 10*60*1000L;
if (millis() > recompute_rx_freq_window) {
// recompute_rx_freq_window and align to n.000 seconds
recompute_rx_freq_window = (millis() / 1000 + 10*60L) * 1000L;
// reduce high rx counts because if ratio is < 1:10 or > 10:1 we still like to listen on at least one timeslot for packets
if (lora_packets_received_in_timeslot_on_main_freq > 100) lora_packets_received_in_timeslot_on_main_freq = 100;
if (lora_packets_received_in_timeslot_on_secondary_freq > 100) lora_packets_received_in_timeslot_on_secondary_freq = 100;
// avoid division by zero
uint8_t lora_packets_received_in_timeslot_on_both_freq = lora_packets_received_in_timeslot_on_main_freq + lora_packets_received_in_timeslot_on_secondary_freq;
uint8_t ratio = (lora_packets_received_in_timeslot_on_both_freq ? (lora_packets_received_in_timeslot_on_main_freq * 10 / lora_packets_received_in_timeslot_on_both_freq) : 5);
if (ratio > 9) ratio = 9;
if (ratio < 1) ratio = 1;
// choose slot table
curr_slot_table = slot_table[ratio-1];
p_curr_slot_table = curr_slot_table;
next_slot = 0;
lora_packets_received_in_timeslot_on_main_freq = lora_packets_received_in_timeslot_on_main_freq / 5;
lora_packets_received_in_timeslot_on_secondary_freq = lora_packets_received_in_timeslot_on_secondary_freq / 5;
}
if (millis() > next_slot) {
// next timeslot is in 20s (aligned to n.000s). If we'd do qsy more often (esp. in a 5:5 situation), chances increase that we loose too much packets due to qsy during receiption. 30 may be too long. 15 too short. 20s also alings good to our 10min window with 10 slots (20/60.0 * 10 * 3 aligns exactly to the 10min window)
next_slot = (millis() / 1000 + 20L) * 1000L;
// avoid calling rf95.setFrequency() and lora_set_speed() if previos *p_curr_slot_table was the same freq/speed
if (*p_curr_slot_table != ((p_curr_slot_table > curr_slot_table) ? p_curr_slot_table[-1] : curr_slot_table[9])) {
lora_freq_rx_curr = (*p_curr_slot_table) ? lora_freq_cross_digi : lora_freq;
rf95.setFrequency(lora_freq_rx_curr);
lora_speed_rx_curr = (*p_curr_slot_table) ? lora_speed_cross_digi : lora_speed;
lora_set_speed(lora_speed_rx_curr);
}
// restart from beginning of current row?
if ((p_curr_slot_table - curr_slot_table) >= 9)
p_curr_slot_table = curr_slot_table;
else
p_curr_slot_table++;
}
}
if (lora_automatic_cr_adaption && lora_speed <= 300L && millis() > (5*60*1000L) && (time_lora_automaic_cr_adoption_rx_measurement_window + 5*60*1000L) < millis()) {
// recalculate automatic adapted CR
@ -1939,7 +2158,7 @@ out:
// Approx seconds between frames: if transmission takes with CR4/5 3s and sleeps 3x so long, we see every 25s a transmission in a 5s window.
// With CR4/6 we have 300 / (3*4) * 300.0/240.0 = 31s; wih CR4/7 we have 35s, and with 41s.
// Slowly, step by step, incrase CR after frequency becomes quet
uint32_t t_diff = millis() - time_last_lora_frame_received;
uint32_t t_diff = millis() - time_last_lora_frame_received_on_main_freq;
if (t_diff > (300*1000L / (3*4) * 300.0/180.0) && lora_speed <= 210)
lora_speed = 180;
else if (t_diff > (300*1000L / (3*4) * 300.0/210.0) && lora_speed <= 240)
@ -2103,12 +2322,8 @@ behind_position_tx:
// 5s grace time (plus up to 250ms random) for digipeating. 10s if we are a fill-in digi
if ((time_lora_TXBUFF_for_digipeating_was_filled + 5*lora_digipeating_mode*1000L + (millis() % 250)) < millis()) {
if (lora_cross_digipeating_mode < 2 && (time_lora_TXBUFF_for_digipeating_was_filled + 2* 5*lora_digipeating_mode*1000L) > millis()) {
ulong lora_speed_prev = lora_speed;
// if SF12: we degipeat in fastest mode CR4/5.
if (lora_speed < 300)
lora_speed = 300;
loraSend(txPower, lora_freq, String(lora_TXBUFF_for_digipeating)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
lora_speed = lora_speed_prev;
// if SF12: we degipeat in fastest mode CR4/5. -> if lora_speed < 300 tx in lora_speed_300.
loraSend(txPower, lora_freq, (lora_speed < 300) ? 300 : lora_speed, String(lora_TXBUFF_for_digipeating)); //send the packet, data is in TXbuff from lora_TXStart to lora_TXEnd
writedisplaytext(" ((TX digi))", "", String(lora_TXBUFF_for_digipeating), "", "", "");
#ifdef KISS_PROTOCOL
sendToTNC(String(lora_TXBUFF_for_digipeating));

Wyświetl plik

@ -26,6 +26,20 @@ extern String infoApName;
extern String infoApPass;
extern String infoApAddr;
// For APRS-IS connection
extern String to_aprsis_data;
extern boolean aprsis_enabled;
extern String aprsis_host;
extern uint16_t aprsis_port;
extern String aprsis_filter;
extern String aprsis_callsign;
extern String aprsis_password;
extern boolean aprsis_data_allow_inet_to_rf;
extern double lora_freq_rx_curr;
extern boolean lora_tx_enabled;
QueueHandle_t webListReceivedQueue = nullptr;
std::list <tReceivedPacketData*> receivedPackets;
const int MAX_RECEIVED_LIST_SIZE = 50;
@ -34,6 +48,19 @@ String apSSID = "";
String apPassword;
String defApPassword = "xxxxxxxxxx";
// needed for aprsis igate functionality
String aprsis_status = "Disconnected";
// aprsis 3rd party traffic encoding
String generate_third_party_packet(String, String);
#ifdef KISS_PROTOCOL
extern void sendToTNC(const String &);
#endif
extern uint8_t txPower;
extern double lora_freq;
extern ulong lora_speed;
extern void loraSend(byte, float, ulong, const String &);
WebServer server(80);
#ifdef KISS_PROTOCOL
WiFiServer tncServer(NETWORK_TNC_PORT);
@ -152,7 +179,7 @@ void handle_SaveWifiCfg() {
Serial.println("Updated AP PASS: " + server.arg(PREF_AP_PASSWORD));
}
}
server.sendHeader("Location", "/");
server.send(302,"text/html", "");
}
@ -198,11 +225,15 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceBool(PREF_LORA_TX_ENABLE);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_POWER);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_DIGIPEATING_MODE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET);
jsonData += jsonLineFromPreferenceDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_SPEED_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET);
jsonData += jsonLineFromPreferenceString(PREF_APRS_CALLSIGN);
jsonData += jsonLineFromPreferenceString(PREF_APRS_RELAY_PATH);
jsonData += jsonLineFromPreferenceString(PREF_APRS_SYMBOL_TABLE);
@ -236,6 +267,16 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceBool(PREF_DEV_AUTO_SHUT);
jsonData += jsonLineFromPreferenceInt(PREF_DEV_AUTO_SHUT_PRESET);
jsonData += jsonLineFromPreferenceInt(PREF_DEV_SHOW_OLED_TIME);
jsonData += jsonLineFromPreferenceBool(PREF_APRSIS_EN);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_SERVER_NAME);
jsonData += jsonLineFromPreferenceInt(PREF_APRSIS_SERVER_PORT);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_FILTER);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_CALLSIGN);
jsonData += jsonLineFromPreferenceString(PREF_APRSIS_PASSWORD);
jsonData += jsonLineFromPreferenceBool(PREF_APRSIS_ALLOW_INET_TO_RF);
jsonData += jsonLineFromInt("lora_freq_rx_curr", (unsigned long ) (lora_freq_rx_curr*1000L));
//jsonData += jsonLineFromPreferenceDouble("lora_freq_rx_curr", lora_freq_rx_curr);
jsonData += jsonLineFromString("aprsis_status", aprsis_status.c_str());
jsonData += jsonLineFromInt("FreeHeap", ESP.getFreeHeap());
jsonData += jsonLineFromInt("HeapSize", ESP.getHeapSize());
jsonData += jsonLineFromInt("FreeSketchSpace", ESP.getFreeSketchSpace());
@ -278,13 +319,19 @@ void handle_SaveAPRSCfg() {
preferences.putInt(PREF_LORA_TX_POWER, server.arg(PREF_LORA_TX_POWER).toInt());
}
preferences.putBool(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET, server.hasArg(PREF_LORA_AUTOMATIC_CR_ADAPTION_PRESET));
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET, server.hasArg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET));
if (server.hasArg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET)){
preferences.putInt(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET, server.arg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_PRESET).toInt());
}
preferences.putBool(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET, server.hasArg(PREF_LORA_ADD_SNR_RSSI_TO_PATH_END_AT_KISS_PRESET));
if (server.hasArg(PREF_APRS_DIGIPEATING_MODE_PRESET)){
preferences.putInt(PREF_APRS_DIGIPEATING_MODE_PRESET, server.arg(PREF_APRS_DIGIPEATING_MODE_PRESET).toInt());
}
if (server.hasArg(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET)){
preferences.putInt(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET, server.arg(PREF_APRS_CROSS_DIGIPEATING_MODE_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET)) {
preferences.putInt(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET, server.arg(PREF_LORA_TX_BEACON_AND_KISS_ON_FREQUENCIES_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_FREQ_CROSSDIGI_PRESET)){
preferences.putDouble(PREF_LORA_FREQ_CROSSDIGI_PRESET, server.arg(PREF_LORA_FREQ_CROSSDIGI_PRESET).toDouble());
Serial.printf("FREQ crossdigi saved:\t%f\n", server.arg(PREF_LORA_FREQ_CROSSDIGI_PRESET).toDouble());
@ -292,6 +339,12 @@ void handle_SaveAPRSCfg() {
if (server.hasArg(PREF_LORA_SPEED_CROSSDIGI_PRESET)){
preferences.putInt(PREF_LORA_SPEED_CROSSDIGI_PRESET, server.arg(PREF_LORA_SPEED_CROSSDIGI_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_TX_POWER_CROSSDIGI_PRESET)) {
preferences.putInt(PREF_LORA_TX_POWER_CROSSDIGI_PRESET, server.arg(PREF_LORA_TX_POWER_CROSSDIGI_PRESET).toInt());
}
if (server.hasArg(PREF_LORA_RX_ON_FREQUENCIES_PRESET)) {
preferences.putInt(PREF_LORA_RX_ON_FREQUENCIES_PRESET, server.arg(PREF_LORA_RX_ON_FREQUENCIES_PRESET).toInt());
}
// APRS station settings
if (server.hasArg(PREF_APRS_CALLSIGN) && !server.arg(PREF_APRS_CALLSIGN).isEmpty()){
preferences.putString(PREF_APRS_CALLSIGN, server.arg(PREF_APRS_CALLSIGN));
@ -355,6 +408,25 @@ void handle_SaveAPRSCfg() {
if (server.hasArg(PREF_APRS_SB_TURN_TIME_PRESET)){
preferences.putInt(PREF_APRS_SB_TURN_TIME_PRESET, server.arg(PREF_APRS_SB_TURN_TIME_PRESET).toInt());
}
preferences.putBool(PREF_APRSIS_EN, server.hasArg(PREF_APRSIS_EN));
if (server.hasArg(PREF_APRSIS_SERVER_NAME)){
preferences.putString(PREF_APRSIS_SERVER_NAME, server.arg(PREF_APRSIS_SERVER_NAME));
}
if (server.hasArg(PREF_APRSIS_SERVER_PORT)){
preferences.putInt(PREF_APRSIS_SERVER_PORT, server.arg(PREF_APRSIS_SERVER_PORT).toInt());
}
if (server.hasArg(PREF_APRSIS_FILTER)){
preferences.putString(PREF_APRSIS_FILTER, server.arg(PREF_APRSIS_FILTER));
}
if (server.hasArg(PREF_APRSIS_CALLSIGN)){
preferences.putString(PREF_APRSIS_CALLSIGN, server.arg(PREF_APRSIS_CALLSIGN));
}
if (server.hasArg(PREF_APRSIS_PASSWORD)){
preferences.putString(PREF_APRSIS_PASSWORD, server.arg(PREF_APRSIS_PASSWORD));
}
preferences.putBool(PREF_APRSIS_ALLOW_INET_TO_RF, server.hasArg(PREF_APRSIS_ALLOW_INET_TO_RF));
preferences.putBool(PREF_APRS_SHOW_BATTERY, server.hasArg(PREF_APRS_SHOW_BATTERY));
preferences.putBool(PREF_ENABLE_TNC_SELF_TELEMETRY, server.hasArg(PREF_ENABLE_TNC_SELF_TELEMETRY));
@ -537,6 +609,36 @@ void handle_saveDeviceCfg(){
tReceivedPacketData *receivedPacketData = nullptr;
WiFiClient aprs_is_client;
uint32_t t_connect_apprsis_again = 0L;
String aprs_callsign = webServerCfg->callsign;
aprsis_host.trim();
aprsis_filter.trim();
aprsis_password.trim();
if (aprsis_callsign.length() < 3 || aprsis_callsign.length() > 9)
aprsis_callsign = "";
if (aprsis_callsign.isEmpty()) aprsis_callsign = aprs_callsign;
if (aprsis_callsign.length() < 3 || aprsis_callsign.length() > 9)
aprsis_callsign = "";
aprsis_callsign.toUpperCase(); aprsis_callsign.trim();
// sanity check
if (aprsis_enabled) {
if (aprsis_callsign.isEmpty() || aprsis_host.isEmpty() || aprsis_port == 0) {
aprsis_enabled = false;
} else {
const char *p = aprsis_callsign.c_str();
for (; *p && aprsis_enabled; p++) {
if (*p == '-') {
int len = strlen(p+1);
if (len < 1 || len > 2)
aprsis_enabled = false;
} else if ( !((*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9')) )
aprsis_enabled = false;
}
}
}
while (true){
server.handleClient();
if (xQueueReceive(webListReceivedQueue, &receivedPacketData, (1 / portTICK_PERIOD_MS)) == pdPASS) {
@ -557,6 +659,137 @@ void handle_saveDeviceCfg(){
delete receivedPacketData;
}
if (aprsis_enabled) {
boolean err = true;
if (WiFi.getMode() == 1) {
if (WiFi.status() != WL_CONNECTED) { aprsis_status = "Error: no internet"; goto on_err; } else { if (aprsis_status == "Error: no internet") aprsis_status = "Internet available"; }
if (!aprs_is_client.connected() && t_connect_apprsis_again < millis()) {
aprsis_status = "Connecting";
aprs_is_client.connect(aprsis_host.c_str(), aprsis_port);
if (!aprs_is_client.connected()) { aprsis_status = "Error: connect failed"; goto on_err; }
aprsis_status = "Connected. Waiting for greeting.";
uint32_t t_start = millis();
while (!aprs_is_client.available() && (millis()-t_start) < 25000) delay(100);
if (aprs_is_client.available()) {
// check
String s = aprs_is_client.readStringUntil('\n');
if (s.isEmpty() || !s.startsWith("#")) { aprsis_status = "Error: unexpected greeting"; goto on_err; }
} else { aprsis_status = "Error: No response"; goto on_err; }
aprsis_status = "Login";
char buffer[1024];
sprintf(buffer, "user %s pass %s TTGO-T-Beam-LoRa-APRS 0.1%s%s\r\n", aprsis_callsign.c_str(), aprsis_password.c_str(), aprsis_filter.isEmpty() ? " filter " : "", aprsis_filter.isEmpty() ? aprsis_filter.c_str() : "");
aprs_is_client.print(String(buffer));
t_start = millis();
while (!aprs_is_client.available() && (millis()-t_start) < 25000) delay(100);
aprsis_status = "Logged in";
if (aprs_is_client.available()) {
// check
String s = aprs_is_client.readStringUntil('\n');
if (s.isEmpty() || !s.startsWith("#")) { aprsis_status = "Error: unexpected reponse on login"; goto on_err; }
if (s.indexOf(" verified") == -1) { aprsis_status = "Error: Login denied: " + s; aprsis_status.trim(); goto on_err; }
} else { aprsis_status = "Error: No response"; goto on_err; }
}
if (!aprs_is_client.connected())
goto on_err;
//aprsis_status = "OK";
if (aprs_is_client.available()) {
//aprsis_status = "OK, reading";
String s = aprs_is_client.readStringUntil('\n');
if (!s) goto on_err;
//aprsis_status = "OK";
s.trim();
if (s.isEmpty()) goto on_err;
if (*(s.c_str()) != '#') {
// generate third party packet. Use aprs_callsign (deriving from webServerCfg->callsign), because aprsis_callsign may have a non-aprs (but only aprsis-compatible) ssid like '-L4'
String third_party_packet = generate_third_party_packet(aprs_callsign, s);
if (!third_party_packet.isEmpty()) {
#ifdef KISS_PROTOCOL
sendToTNC(third_party_packet);
#endif
if (lora_tx_enabled && aprsis_data_allow_inet_to_rf) {
// if not our own frame coming back.
// or: not query or aprs-message addressed to our call (check both, aprs_callsign and aprsis_callsign).
// ^This is quite complex. Format: "..::DL9SAU-15:..."
boolean do_not_send = false;
if (s.startsWith(aprs_callsign + '>') || s.startsWith(aprsis_callsign + '>')) {
do_not_send = true;
} else {
char *q = strchr(s.c_str(), ':');
if (q) {
q++;
if (*q == ':' && strlen(q) > 10 && q[10] == ':' &&
((!strncmp(q+1, aprs_callsign.c_str(), aprs_callsign.length()) && (aprs_callsign.length() == 9 || q[9] == ' ')) ||
(!strncmp(q+1, aprsis_callsign.c_str(), aprsis_callsign.length()) && (aprsis_callsign.length() == 9 || q[9] == ' ')) ))
do_not_send = true;
}
}
if (!do_not_send)
loraSend(txPower, lora_freq, lora_speed, third_party_packet);
}
}
}
}
if (to_aprsis_data) {
// copy(). We are threaded..
String data = String(to_aprsis_data);
// clear queue
to_aprsis_data = "";
data.trim();
char *p = strchr(data.c_str(), '>');
char *q;
// some plausibility checks.
if (p && p > data.c_str() && (q = strchr(p+1, ':'))) {
// Due to http://www.aprs-is.net/IGateDetails.aspx , never gate third-party traffic contining TCPIP or TCPXX
// IGATECALL>APRS,GATEPATH:}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data
char *r; char *s;
if (!(q[1] == '}' && (r = strchr(q+2, '>')) && ((s = strstr(r+1, ",TCPIP,")) || (s = strstr(r+1, ",TCPXX,"))) && strstr(s+6, "*:"))) {
char buf[256];
int len = (q-data.c_str());
if (len > 0 && len < sizeof(buf)) {
strncpy(buf, data.c_str(), len);
buf[len] = 0;
String s_data = String(buf) + (lora_tx_enabled ? ",qAR," : ",qAO,") + aprsis_callsign + q + "\r\n";
aprsis_status = "OK, sending: " + s_data; aprsis_status.trim();
aprs_is_client.print(s_data);
}
}
}
//aprsis_status = "OK";
}
err = false;
if (err) {
on_err:
aprs_is_client.stop();
if (!aprsis_status.startsWith("Error: "))
aprsis_status = "Disconnected";
if (t_connect_apprsis_again <= millis())
t_connect_apprsis_again = millis() + 60000;
to_aprsis_data = "";
}
}
}
vTaskDelay(5/portTICK_PERIOD_MS);
}
}
String generate_third_party_packet(String callsign, String packet_in)
{
String packet_out = "";
const char *s = packet_in.c_str();
char *p = strchr(s, '>');
char *q = strchr(s, ',');
char *r = strchr(s, ':');
char fromtodest[20]; // room for max (due to spec) 'DL9SAU-15>APRSXX-NN' + \0
if (p > s && p < q && q < r && (q-s) < sizeof(fromtodest)) {
r++;
strncpy(fromtodest, s, q-s);
fromtodest[(q-s)] = 0;
packet_out = callsign + ">APRS:}" + fromtodest + ",TCPIP," + callsign + "*:" + r;
// ^ 3rd party traffic should be addressed directly (-> not to WIDE2-1 or so)
}
return packet_out;
}