LoRa_APRS_Tracker111/src/station_utils.cpp

378 lines
14 KiB
C++

#include <TinyGPS++.h>
#include <SPIFFS.h>
#include "APRSPacketLib.h"
#include "station_utils.h"
#include "battery_utils.h"
#include "configuration.h"
#include "boards_pinout.h"
#include "power_utils.h"
#include "sleep_utils.h"
#include "lora_utils.h"
#include "ble_utils.h"
#include "wx_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 double currentHeading;
extern double previousHeading;
extern double lastTxLat;
extern double lastTxLng;
extern double lastTxDistance;
extern bool miceActive;
extern bool smartBeaconActive;
extern bool winlinkCommentState;
extern int wxModuleType;
extern bool wxModuleFound;
extern bool gpsIsActive;
extern bool gpsShouldSleep;
bool sendStandingUpdate = false;
uint8_t updateCounter = 100;
bool sendStartTelemetry = true;
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 checkStandingUpdateTime() {
if (!sendUpdate && lastTx >= Config.standingUpdateTime * 60 * 1000) {
sendUpdate = true;
sendStandingUpdate = true;
if (!gpsIsActive) {
SLEEP_Utils::gpsWakeUp();
}
}
}
void sendBeacon(uint8_t type) {
if (sendStartTelemetry && Config.battery.sendVoltage && Config.battery.voltageAsTelemetry) {
String sender = currentBeacon->callsign;
for (int i = sender.length(); i < 9; i++) {
sender += ' ';
}
String basePacket = currentBeacon->callsign;
basePacket += ">APLRT1";
if (Config.path != "") {
basePacket += ",";
basePacket += Config.path;
}
basePacket += "::";
basePacket += sender;
basePacket += ":";
String tempPacket = basePacket;
tempPacket += "EQNS.0,0.01,0";
displayShow("<<< TX >>>", "Telemetry Packet:", "Equation Coefficients",100);
LoRa_Utils::sendNewPacket(tempPacket);
delay(3000);
tempPacket = basePacket;
tempPacket += "UNIT.VDC";
displayShow("<<< TX >>>", "Telemetry Packet:", "Unit/Label",100);
LoRa_Utils::sendNewPacket(tempPacket);
delay(3000);
tempPacket = basePacket;
tempPacket += "PARM.V_Batt";
displayShow("<<< TX >>>", "Telemetry Packet:", "Parameter Name",100);
LoRa_Utils::sendNewPacket(tempPacket);
delay(3000);
sendStartTelemetry = false;
}
String packet;
if (Config.wxsensor.sendTelemetry && wxModuleFound && 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 += WX_Utils::readDataSensor(0);
} else {
packet += ".../...g...t...";
}
} 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 || Config.battery.sendVoltageAlways) {
if (winlinkCommentState) comment = " winlink";
sendCommentAfterXBeacons = 1;
} else {
comment = currentBeacon->comment;
sendCommentAfterXBeacons = Config.sendCommentAfterXBeacons;
}
String batteryVoltage = POWER_Utils::getBatteryInfoVoltage();
bool shouldSleepLowVoltage = false;
#if defined(BATTERY_PIN) || defined(HAS_AXP192) || defined(HAS_AXP2101)
if (Config.battery.monitorVoltage && batteryVoltage.toFloat() < Config.battery.sleepVoltage) {
shouldSleepLowVoltage = true;
}
//
//BATTERY_Utils::checkLowVoltageAndSleep(batteryVoltage.toFloat());
//
#endif
if (Config.battery.sendVoltage && !Config.battery.voltageAsTelemetry) {
String batteryChargeCurrent = POWER_Utils::getBatteryInfoCurrent();
#if defined(HAS_AXP192)
comment += " Bat=";
comment += batteryVoltage;
comment += "V (";
comment += batteryChargeCurrent;
comment += "mA)";
#elif defined(HAS_AXP2101)
comment += " Bat=";
comment += String(batteryVoltage.toFloat(),2);
comment += "V (";
comment += batteryChargeCurrent;
comment += "%)";
#elif defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101)
comment += " Bat=";
comment += String(batteryVoltage.toFloat(),2);
comment += "V";
comment += BATTERY_Utils::getPercentVoltageBattery(batteryVoltage.toFloat());
comment += "%";
#endif
}
if (shouldSleepLowVoltage) {
packet += " **LowVoltagePowerOff**";
} else {
if (comment != "" || (Config.battery.sendVoltage && Config.battery.voltageAsTelemetry)) {
updateCounter++;
if (updateCounter >= sendCommentAfterXBeacons) {
if (comment != "") packet += comment;
if (Config.battery.sendVoltage && Config.battery.voltageAsTelemetry) packet += BATTERY_Utils::generateEncodedTelemetry(batteryVoltage.toFloat());
updateCounter = 0;
}
}
}
#ifdef HAS_TFT
cleanTFT();
#endif
displayShow("<<< TX >>>", "", packet,100);
LoRa_Utils::sendNewPacket(packet);
if (Config.bluetooth.type == 0 || Config.bluetooth.type == 2) { // send Tx packets to Phone too
BLE_Utils::sendToPhone(packet);
}
if (shouldSleepLowVoltage) {
delay(3000);
POWER_Utils::shutdown();
}
if (smartBeaconActive) {
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;
}
}
void checkTelemetryTx() {
if (Config.wxsensor.active && Config.wxsensor.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();
}
}
}