mirror of
https://github.com/richonguzman/LoRa_APRS_iGate.git
synced 2026-11-03 12:12:20 +01:00
202 lines
7.2 KiB
C++
202 lines
7.2 KiB
C++
/* Copyright (C) 2026 Ricardo Guzman - CA2RXU
|
|
*
|
|
* This file is part of LoRa APRS iGate.
|
|
*
|
|
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <APRSPacketLib.h>
|
|
#include <TinyGPS++.h>
|
|
#include <SPIFFS.h>
|
|
#include <WiFi.h>
|
|
#include "configuration.h"
|
|
#include "board_pinout.h"
|
|
#include "gps_utils.h"
|
|
#include "display.h"
|
|
#include "utils.h"
|
|
|
|
#ifdef GPS_BAUDRATE
|
|
#define GPS_BAUD GPS_BAUDRATE
|
|
#else
|
|
#define GPS_BAUD 9600
|
|
#endif
|
|
|
|
#define GPS_BAUD_FILE "/gps_baud.dat"
|
|
#define GPS_BAUD_PROBE_TIME_MS 1500
|
|
const uint32_t gpsBaudRates[] = {9600, 115200, 38400, 4800, 57600}; // most common first
|
|
|
|
extern Configuration Config;
|
|
extern HardwareSerial gpsSerial;
|
|
extern TinyGPSPlus gps;
|
|
extern bool stationCallsignIsValid;
|
|
String distance, iGateBeaconPacket, iGateLoRaBeaconPacket;
|
|
|
|
|
|
namespace GPS_Utils {
|
|
|
|
#ifdef HAS_GPS
|
|
bool isKnownBaudRate(uint32_t baud) {
|
|
if (baud == GPS_BAUD) return true;
|
|
for (uint32_t knownBaud : gpsBaudRates) {
|
|
if (knownBaud == baud) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t readSavedBaudRate() {
|
|
File file = SPIFFS.open(GPS_BAUD_FILE, "r");
|
|
if (!file) return 0;
|
|
uint32_t baud = file.readString().toInt();
|
|
file.close();
|
|
return isKnownBaudRate(baud) ? baud : 0;
|
|
}
|
|
|
|
void saveBaudRate(uint32_t baud) {
|
|
File file = SPIFFS.open(GPS_BAUD_FILE, "w");
|
|
if (!file) {
|
|
Utils::println("GPS: could not save baud rate");
|
|
return;
|
|
}
|
|
file.print(baud);
|
|
file.close();
|
|
}
|
|
|
|
bool probeBaudRate(uint32_t baud) {
|
|
gpsSerial.end();
|
|
delay(50);
|
|
gpsSerial.begin(baud, SERIAL_8N1, GPS_TX, GPS_RX);
|
|
uint32_t validSentences = gps.passedChecksum();
|
|
uint32_t probeStart = millis();
|
|
while (millis() - probeStart < GPS_BAUD_PROBE_TIME_MS) {
|
|
while (gpsSerial.available() > 0) gps.encode(gpsSerial.read());
|
|
if (gps.passedChecksum() > validSentences) return true; // NMEA with valid checksum
|
|
delay(10);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t detectBaudRate() {
|
|
uint32_t savedBaud = readSavedBaudRate();
|
|
|
|
uint32_t candidates[2 + sizeof(gpsBaudRates) / sizeof(gpsBaudRates[0])];
|
|
size_t count = 0;
|
|
auto addCandidate = [&](uint32_t baud) {
|
|
if (baud == 0) return;
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (candidates[i] == baud) return;
|
|
}
|
|
candidates[count++] = baud;
|
|
};
|
|
addCandidate(savedBaud); // saved first, then board default, then common ones
|
|
addCandidate(GPS_BAUD);
|
|
for (uint32_t baud : gpsBaudRates) addCandidate(baud);
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (i == 1) displayShow("GPS", "Searching", "baud rate...", "", 0);
|
|
bool found = probeBaudRate(candidates[i]);
|
|
if (!found && i == 0) found = probeBaudRate(candidates[i]); // retry first one: GPS may be slow to start
|
|
if (found) {
|
|
if (candidates[i] != savedBaud) saveBaudRate(candidates[i]);
|
|
return candidates[i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
String getiGateLoRaBeaconPacket() {
|
|
return iGateLoRaBeaconPacket;
|
|
}
|
|
|
|
void generateBeacons() {
|
|
String beaconPacket = APRSPacketLib::generateBasePacket(Config.callsign, "APLRG1", Config.beacon.path);
|
|
String encodedGPS = APRSPacketLib::encodeGPSIntoBase91(Config.beacon.latitude, Config.beacon.longitude, 0, 0, Config.beacon.symbol, false, 0, true, Config.beacon.ambiguityLevel);
|
|
|
|
if (Config.callsign.indexOf("NOCALL-10") != 0) {
|
|
if (!stationCallsignIsValid) {
|
|
displayShow("***** ERROR ******", "CALLSIGN = NOT VALID!", "", "Only Rx Mode Active", 3000);
|
|
Config.loramodule.txActive = false;
|
|
Config.aprs_is.messagesToRF = false;
|
|
Config.aprs_is.objectsToRF = false;
|
|
Config.beacon.sendViaRF = false;
|
|
Config.digi.mode = 0;
|
|
Config.digi.backupDigiMode = false;
|
|
} else if (stationCallsignIsValid && Config.tacticalCallsign != "") {
|
|
beaconPacket = APRSPacketLib::generateBasePacket(Config.tacticalCallsign, "APLRG1", Config.beacon.path);
|
|
Config.aprs_is.active = false;
|
|
Config.beacon.sendViaAPRSIS = false;
|
|
Config.digi.backupDigiMode = false;
|
|
}
|
|
} else {
|
|
Config.beacon.sendViaAPRSIS = false;
|
|
Config.beacon.sendViaRF = false;
|
|
}
|
|
|
|
iGateBeaconPacket = beaconPacket;
|
|
iGateBeaconPacket += ",qAC:=";
|
|
iGateBeaconPacket += Config.beacon.overlay;
|
|
iGateBeaconPacket += encodedGPS;
|
|
|
|
iGateLoRaBeaconPacket = beaconPacket;
|
|
iGateLoRaBeaconPacket += ":=";
|
|
iGateLoRaBeaconPacket += Config.beacon.overlay;
|
|
iGateLoRaBeaconPacket += encodedGPS;
|
|
}
|
|
|
|
double calculateDistanceTo(double latitude, double longitude) {
|
|
return TinyGPSPlus::distanceBetween(Config.beacon.latitude,Config.beacon.longitude, latitude, longitude) / 1000.0;
|
|
}
|
|
|
|
String buildDistanceAndComment(float latitude, float longitude, const String& comment) {
|
|
distance = String(calculateDistanceTo(latitude, longitude),1);
|
|
|
|
String distanceAndComment = String(latitude,5);
|
|
distanceAndComment += "N / ";
|
|
distanceAndComment += String(longitude,5);
|
|
distanceAndComment += "E / ";
|
|
distanceAndComment += distance;
|
|
distanceAndComment += "km";
|
|
|
|
if (comment != "") {
|
|
distanceAndComment += " / ";
|
|
distanceAndComment += comment;
|
|
}
|
|
return distanceAndComment;
|
|
|
|
}
|
|
|
|
void setup() {
|
|
#ifdef HAS_GPS
|
|
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) {
|
|
uint32_t detectedBaud = detectBaudRate();
|
|
if (detectedBaud != 0) {
|
|
Utils::println("GPS: baud rate detected: " + String(detectedBaud));
|
|
} else {
|
|
Utils::println("GPS: no NMEA data found, using default baud rate: " + String(GPS_BAUD));
|
|
gpsSerial.end();
|
|
delay(50);
|
|
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_TX, GPS_RX);
|
|
}
|
|
}
|
|
#endif
|
|
generateBeacons();
|
|
}
|
|
|
|
void getData() {
|
|
while (gpsSerial.available() > 0) {
|
|
gps.encode(gpsSerial.read());
|
|
}
|
|
}
|
|
|
|
} |