LoRa_APRS_Tracker111/src/station_utils.cpp

360 lines
13 KiB
C++

#include <TinyGPS++.h>
#include <SPIFFS.h>
#include "APRSPacketLib.h"
#include "station_utils.h"
#include "configuration.h"
#include "boards_pinout.h"
#include "power_utils.h"
#include "sleep_utils.h"
#include "lora_utils.h"
#include "bme_utils.h"
#include "display.h"
#include "logger.h"
extern Configuration Config;
extern Beacon *currentBeacon;
extern logging::Logger logger;
extern TinyGPSPlus gps;
extern uint8_t myBeaconsIndex;
extern uint8_t loraIndex;
extern uint32_t lastTx;
extern uint32_t lastTxTime;
extern bool sendUpdate;
extern uint32_t txInterval;
extern double currentHeading;
extern double previousHeading;
extern double lastTxLat;
extern double lastTxLng;
extern double lastTxDistance;
extern bool miceActive;
extern bool smartBeaconValue;
extern uint8_t winlinkStatus;
extern bool winlinkCommentState;
extern int wxModuleType;
extern bool gpsIsActive;
extern bool gpsShouldSleep;
bool sendStandingUpdate = false;
uint8_t updateCounter = Config.sendCommentAfterXBeacons;
bool wxRequestStatus = false;
uint32_t wxRequestTime = 0;
uint32_t lastTelemetryTx = 0;
uint32_t telemetryTx = millis();
uint32_t lastDeleteListenedTracker;
struct nearTracker {
String callsign;
float distance;
int course;
uint32_t lastTime;
};
nearTracker nearTrackers[4];
namespace STATION_Utils {
void nearTrackerInit() {
for (int i = 0; i < 4; i++) {
nearTrackers[i].callsign = "";
nearTrackers[i].distance = 0.0;
nearTrackers[i].course = 0;
nearTrackers[i].lastTime = 0;
}
}
const String getNearTracker(uint8_t position) {
if (nearTrackers[position].callsign == "") {
return "";
} else {
return nearTrackers[position].callsign + "> " + String(nearTrackers[position].distance,2) + "km " + String(nearTrackers[position].course);
}
}
void deleteListenedTrackersbyTime() {
for (int a = 0; a < 4; a++) { // clean nearTrackers[] after time
if (nearTrackers[a].callsign != "" && (millis() - nearTrackers[a].lastTime > Config.rememberStationTime * 60 * 1000)) {
nearTrackers[a].callsign = "";
nearTrackers[a].distance = 0.0;
nearTrackers[a].course = 0;
nearTrackers[a].lastTime = 0;
}
}
for (int b = 0; b < 4 - 1; b++) {
for (int c = 0; c < 4 - b - 1; c++) {
if (nearTrackers[c].callsign == "") { // get "" nearTrackers[] at the end
nearTracker temp = nearTrackers[c];
nearTrackers[c] = nearTrackers[c + 1];
nearTrackers[c + 1] = temp;
}
}
}
lastDeleteListenedTracker = millis();
}
void checkListenedTrackersByTimeAndDelete() {
if (millis() - lastDeleteListenedTracker > Config.rememberStationTime * 60 * 1000) {
deleteListenedTrackersbyTime();
}
}
void orderListenedTrackersByDistance(const String& callsign, float distance, float course) {
bool shouldSortbyDistance = false;
bool callsignInNearTrackers = false;
for (int a = 0; a < 4; a++) { // check if callsign is in nearTrackers[]
if (nearTrackers[a].callsign == callsign) {
callsignInNearTrackers = true;
nearTrackers[a].lastTime = millis(); // update listened millis()
if (nearTrackers[a].distance != distance) { // update distance if needed
nearTrackers[a].distance = distance;
shouldSortbyDistance = true;
}
break;
}
}
if (!callsignInNearTrackers) { // callsign not in nearTrackers[]
for (int b = 0; b < 4; b++) { // if nearTrackers[] is available
if (nearTrackers[b].callsign == "") {
shouldSortbyDistance = true;
nearTrackers[b].callsign = callsign;
nearTrackers[b].distance = distance;
nearTrackers[b].course = int(course);
nearTrackers[b].lastTime = millis();
break;
}
}
if (!shouldSortbyDistance) { // if no more nearTrackers[] available , it compares distances to move and replace
for (int c = 0; c < 4; c++) {
if (nearTrackers[c].distance > distance) {
for (int d = 3; d > c; d--) {
nearTrackers[d] = nearTrackers[d - 1];
}
nearTrackers[c].callsign = callsign;
nearTrackers[c].distance = distance;
nearTrackers[c].course = int(course);
nearTrackers[c].lastTime = millis();
break;
}
}
}
}
if (shouldSortbyDistance) { // sorts by distance (only nearTrackers[] that are not "")
for (int f = 0; f < 4 - 1; f++) {
for (int g = 0; g < 4 - f - 1; g++) {
if (nearTrackers[g].callsign != "" && nearTrackers[g + 1].callsign != "") {
if (nearTrackers[g].distance > nearTrackers[g + 1].distance) {
nearTracker temp = nearTrackers[g];
nearTrackers[g] = nearTrackers[g + 1];
nearTrackers[g + 1] = temp;
}
}
}
}
}
}
void checkSmartBeaconInterval(int speed) {
if (smartBeaconValue) {
if (speed < currentBeacon->slowSpeed) {
txInterval = currentBeacon->slowRate * 1000;
} else if (speed > currentBeacon->fastSpeed) {
txInterval = currentBeacon->fastRate * 1000;
} else {
txInterval = min(currentBeacon->slowRate, currentBeacon->fastSpeed * currentBeacon->fastRate / speed) * 1000;
}
//
Serial.print("intervalo = "); Serial.println(txInterval);
//
}
}
void checkStandingUpdateTime() {
if (!sendUpdate && lastTx >= Config.standingUpdateTime * 60 * 1000) {
sendUpdate = true;
sendStandingUpdate = true;
if (!gpsIsActive) {
SLEEP_Utils::gpsWakeUp();
}
}
}
void checkSmartBeaconValue() {
if (wxRequestStatus && (millis() - wxRequestTime) > 20000) {
wxRequestStatus = false;
}
if(winlinkStatus == 0 && !wxRequestStatus) {
smartBeaconValue = currentBeacon->smartBeaconState;
} else {
smartBeaconValue = false;
}
}
void checkSmartBeaconState() {
if (!smartBeaconValue) {
uint32_t lastTxSmartBeacon = millis() - lastTxTime;
if (lastTxSmartBeacon >= Config.nonSmartBeaconRate * 60 * 1000) {
sendUpdate = true;
}
}
}
void sendBeacon(uint8_t type) {
String packet;
if (Config.bme.sendTelemetry && type == 1) { // WX
packet = APRSPacketLib::generateGPSBeaconPacket(currentBeacon->callsign, "APLRT1", Config.path, "/", APRSPacketLib::encodeGPS(gps.location.lat(),gps.location.lng(), gps.course.deg(), 0.0, currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate, "Wx"));
if (wxModuleType != 0) {
packet += BME_Utils::readDataSensor(0);
} else {
packet += ".../...g...t...r...p...P...h..b.....";
}
} else {
String path = Config.path;
if (gps.speed.kmph() > 200 || gps.altitude.meters() > 9000) { // avoid plane speed and altitude
path = "";
}
if (miceActive) {
packet = APRSPacketLib::generateMiceGPSBeacon(currentBeacon->micE, currentBeacon->callsign, currentBeacon->symbol, currentBeacon->overlay, path, gps.location.lat(), gps.location.lng(), gps.course.deg(), gps.speed.knots(), gps.altitude.meters());
} else {
packet = APRSPacketLib::generateGPSBeaconPacket(currentBeacon->callsign, "APLRT1", path, currentBeacon->overlay, APRSPacketLib::encodeGPS(gps.location.lat(),gps.location.lng(), gps.course.deg(), gps.speed.knots(), currentBeacon->symbol, Config.sendAltitude, gps.altitude.feet(), sendStandingUpdate, "GPS"));
}
}
String comment;
int sendCommentAfterXBeacons;
if (winlinkCommentState) {
comment = " winlink";
sendCommentAfterXBeacons = 1;
} else {
comment = currentBeacon->comment;
sendCommentAfterXBeacons = Config.sendCommentAfterXBeacons;
}
String batteryVoltage = POWER_Utils::getBatteryInfoVoltage();
if (Config.sendBatteryInfo) {
//String batteryVoltage = POWER_Utils::getBatteryInfoVoltage();
String batteryChargeCurrent = POWER_Utils::getBatteryInfoCurrent();
#ifdef HAS_AXP192
comment += " Bat=";
comment += batteryVoltage;
comment += "V (";
comment += batteryChargeCurrent;
comment += "mA)";
#endif
#ifdef HAS_AXP2101
comment += " Bat=";
comment += String(batteryVoltage.toFloat()/1000,2);
comment += "V (";
comment += batteryChargeCurrent;
comment += "%)";
#endif
#if defined(HELTEC_V3_GPS) || defined(HELTEC_WIRELESS_TRACKER) || defined(TTGO_T_DECK_GPS)
comment += " Bat=";
comment += String(batteryVoltage.toFloat(),2);
comment += "V";
#endif
}
if (comment != "") {
updateCounter++;
if (updateCounter >= sendCommentAfterXBeacons) {
packet += comment;
updateCounter = 0;
}
}
#ifdef HAS_TFT
cleanTFT();
#endif
displayShow("<<< TX >>>", "", packet,100);
LoRa_Utils::sendNewPacket(packet);
if (smartBeaconValue) {
lastTxLat = gps.location.lat();
lastTxLng = gps.location.lng();
previousHeading = currentHeading;
lastTxDistance = 0.0;
}
lastTxTime = millis();
sendUpdate = false;
#ifdef HAS_TFT
cleanTFT();
#endif
if (currentBeacon->gpsEcoMode) {
gpsShouldSleep = true;
}
#if !defined(HAS_AXP192) && !defined(HAS_AXP2101) && defined(BATTERY_PIN)
if (batteryVoltage.toFloat() < 3.0) {
POWER_Utils::shutdown();
}
#endif
}
void checkTelemetryTx() {
if (Config.bme.active && Config.bme.sendTelemetry && sendStandingUpdate) {
lastTx = millis() - lastTxTime;
telemetryTx = millis() - lastTelemetryTx;
if ((lastTelemetryTx == 0 || telemetryTx > 10 * 60 * 1000) && lastTx > 10 * 1000) {
sendBeacon(1);
lastTelemetryTx = millis();
}
}
}
void saveIndex(uint8_t type, uint8_t index) {
String filePath;
if (type == 0) {
filePath = "/callsignIndex.txt";
} else {
filePath = "/freqIndex.txt";
}
File fileIndex = SPIFFS.open(filePath, "w");
if(!fileIndex) {
return;
}
String dataToSave = String(index);
if (fileIndex.println(dataToSave)) {
if (type == 0) {
logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "Main", "New Callsign Index saved to SPIFFS");
} else {
logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "Main", "New Frequency Index saved to SPIFFS");
}
}
fileIndex.close();
}
void loadIndex(uint8_t type) {
String filePath;
if (type == 0) {
filePath = "/callsignIndex.txt";
} else {
filePath = "/freqIndex.txt";
}
File fileIndex = SPIFFS.open(filePath);
if(!fileIndex) {
return;
} else {
while (fileIndex.available()) {
String firstLine = fileIndex.readStringUntil('\n');
if (type == 0) {
myBeaconsIndex = firstLine.toInt();
logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "Main", "Callsign Index: %s", firstLine);
} else {
loraIndex = firstLine.toInt();
logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "LoRa", "LoRa Freq Index: %s", firstLine);
}
}
fileIndex.close();
}
}
}