Compare commits

..

3 Commits

Author SHA1 Message Date
Ricardo Guzman (Richonguzman) e6d44c1b7f replaced string for hash in 25segBuffer 2026-02-23 18:59:27 -03:00
Ricardo Guzman (Richonguzman) d4fc99466f update digi2000 2026-02-23 18:07:26 -03:00
Ricardo Guzman (Richonguzman) 41f09af7b5 digipeater logic improved 2026-02-23 17:32:52 -03:00
4 changed files with 85 additions and 79 deletions
+6 -7
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -37,8 +37,7 @@ namespace STATION_Utils {
void deleteNotHeard(); void deleteNotHeard();
void updateLastHeard(const String& station); void updateLastHeard(const String& station);
bool wasHeard(const String& station); bool wasHeard(const String& station);
void clean25SegBuffer(); bool isIn25SegHashBuffer(const String& station, const String& textMessage);
bool check25SegBuffer(const String& station, const String& textMessage);
void processOutputPacketBufferUltraEcoMode(); void processOutputPacketBufferUltraEcoMode();
void processOutputPacketBuffer(); void processOutputPacketBuffer();
void addToOutputPacketBuffer(const String& packet, bool flag = false); void addToOutputPacketBuffer(const String& packet, bool flag = false);
-1
View File
@@ -188,7 +188,6 @@ void loop() {
} }
if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backupDigiMode)) { // If Digi enabled if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backupDigiMode)) { // If Digi enabled
STATION_Utils::clean25SegBuffer();
DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi
} }
+48 -53
View File
@@ -43,24 +43,29 @@ extern bool backupDigiMode;
namespace DIGI_Utils { namespace DIGI_Utils {
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) { String cleanPathAsterisks(String path) {
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign); String terms[] = {",WIDE1*", ",WIDE2*", "*"};
if (!crossFreq) { for (String term : terms) {
String packetToRepeat = packet.substring(0, packet.indexOf(",") + 1); int index = path.indexOf(term);
String tempPath = path; if (index != -1) path.remove(index, term.length()); // less memory than: tempPath.replace("*", "");
int digiMode = Config.digi.mode; }
return path;
}
if (path.indexOf("WIDE1-1") != -1 && (digiMode == 2 || digiMode == 3)) { String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) {
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign);
String suffix = thirdParty ? ":}" : ":";
int suffixIndex = packet.indexOf(suffix);
String packetToRepeat;
if (!crossFreq) {
int digiMode = Config.digi.mode;
String tempPath = path;
if (tempPath.indexOf("WIDE1-1") != -1 && (digiMode == 2 || digiMode == 3)) { // WIDE1-1 Digipeater
if (tempPath.indexOf("*") != -1 ) return ""; // "*" shouldn't be in WIDE1-1 (only) type of packet
tempPath.replace("WIDE1-1", stationCallsign + "*"); tempPath.replace("WIDE1-1", stationCallsign + "*");
} else if (path.indexOf("WIDE2-") != -1 && digiMode == 3) { } else if (tempPath.indexOf("WIDE2-") != -1 && digiMode == 3) { // WIDE2-n Digipeater
int wide1AsteriskIndex = path.indexOf(",WIDE1*"); // less memory than: tempPath.replace(",WIDE1*", ""); tempPath = cleanPathAsterisks(path);
if (wide1AsteriskIndex != -1) {
tempPath.remove(wide1AsteriskIndex, 7);
}
int asteriskIndex = path.indexOf("*"); // less memory than: tempPath.replace("*", "");
if (asteriskIndex != -1) {
tempPath.remove(asteriskIndex, 1);
}
if (path.indexOf("WIDE2-1") != -1) { if (path.indexOf("WIDE2-1") != -1) {
tempPath.replace("WIDE2-1", stationCallsign + "*"); tempPath.replace("WIDE2-1", stationCallsign + "*");
} else if (path.indexOf("WIDE2-2") != -1) { } else if (path.indexOf("WIDE2-2") != -1) {
@@ -69,32 +74,22 @@ namespace DIGI_Utils {
return ""; return "";
} }
} }
packetToRepeat = packet.substring(0, packet.indexOf(",") + 1);
packetToRepeat += tempPath; packetToRepeat += tempPath;
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(packet.indexOf(thirdParty ? ":}" : ":")));
return packetToRepeat;
} else { // CrossFreq Digipeater } else { // CrossFreq Digipeater
String suffix = thirdParty ? ":}" : ":"; packetToRepeat = cleanPathAsterisks(packet.substring(0, suffixIndex));
int suffixIndex = packet.indexOf(suffix); if (packetToRepeat.indexOf(stationCallsign) != -1) return ""; // stationCallsign shouldn't be in path
String packetToRepeat = packet.substring(0, suffixIndex);
String terms[] = {",WIDE1*", ",WIDE2*", "*"};
for (String term : terms) {
int index = packetToRepeat.indexOf(term);
if (index != -1) {
packetToRepeat.remove(index, term.length());
}
}
packetToRepeat += ","; packetToRepeat += ",";
packetToRepeat += stationCallsign; packetToRepeat += stationCallsign;
packetToRepeat += "*"; packetToRepeat += "*";
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(suffixIndex));
return packetToRepeat;
} }
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(suffixIndex));
return packetToRepeat;
} }
String generateDigipeatedPacket(const String& packet, bool thirdParty){ String generateDigipeatedPacket(const String& packet, bool thirdParty){
String temp; String temp;
if (thirdParty) { // only header is used if (thirdParty) { // only header is used
const String& header = packet.substring(0, packet.indexOf(":}")); const String& header = packet.substring(0, packet.indexOf(":}"));
temp = header.substring(header.indexOf(">") + 1); temp = header.substring(header.indexOf(">") + 1);
} else { } else {
@@ -104,7 +99,7 @@ namespace DIGI_Utils {
int digiMode = Config.digi.mode; int digiMode = Config.digi.mode;
bool crossFreq = abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000; // CrossFreq Digi bool crossFreq = abs(Config.loramodule.txFreq - Config.loramodule.rxFreq) >= 125000; // CrossFreq Digi
if (commaIndex > 2) { // Packet has "path" if (commaIndex > 2) { // "path" found
const String& path = temp.substring(commaIndex + 1); const String& path = temp.substring(commaIndex + 1);
if (digiMode == 2 || backupDigiMode) { if (digiMode == 2 || backupDigiMode) {
bool hasWide = path.indexOf("WIDE1-1") != -1; bool hasWide = path.indexOf("WIDE1-1") != -1;
@@ -154,28 +149,28 @@ namespace DIGI_Utils {
if (Sender == stationCallsign) return; // Avoid listening to self packets if (Sender == stationCallsign) return; // Avoid listening to self packets
if (!thirdPartyPacket && Config.tacticalCallsign == "" && !Utils::callsignIsValid(Sender)) return; // No thirdParty + no tactical y no valid callsign if (!thirdPartyPacket && Config.tacticalCallsign == "" && !Utils::callsignIsValid(Sender)) return; // No thirdParty + no tactical y no valid callsign
if (STATION_Utils::check25SegBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) { if (STATION_Utils::isIn25SegHashBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) return;
STATION_Utils::updateLastHeard(Sender);
Utils::typeOfPacket(temp, 2); // Digi STATION_Utils::updateLastHeard(Sender);
bool queryMessage = false; Utils::typeOfPacket(temp, 2); // Digi
int doubleColonIndex = temp.indexOf("::"); bool queryMessage = false;
if (doubleColonIndex > 10) { // it's a message int doubleColonIndex = temp.indexOf("::");
String AddresseeAndMessage = temp.substring(doubleColonIndex + 2); if (doubleColonIndex > 10) { // it's a message
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":")); String AddresseeAndMessage = temp.substring(doubleColonIndex + 2);
Addressee.trim(); String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
if (Addressee == stationCallsign) { // it's a message for me! Addressee.trim();
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket); if (Addressee == stationCallsign) { // it's a message for me!
} queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
}
if (!queryMessage) {
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
}
} }
} }
if (queryMessage) return; // answer should not be repeated.
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
if (loraPacket != "") {
STATION_Utils::addToOutputPacketBuffer(loraPacket);
if (Config.digi.ecoMode != 1) displayToggle(true);
lastScreenOn = millis();
}
} }
} }
+31 -18
View File
@@ -1,17 +1,17 @@
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU /* Copyright (C) 2025 Ricardo Guzman - CA2RXU
* *
* This file is part of LoRa APRS iGate. * This file is part of LoRa APRS iGate.
* *
* LoRa APRS iGate is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* LoRa APRS iGate is distributed in the hope that it will be useful, * LoRa APRS iGate is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * 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/>. * along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
*/ */
@@ -45,12 +45,10 @@ std::vector<OutputPacketBuffer> outputPacketBuffer;
struct Packet25SegBuffer { struct Packet25SegBuffer {
uint32_t receivedTime; uint32_t receivedTime;
String station; uint32_t hash;
String payload;
}; };
std::vector<Packet25SegBuffer> packet25SegBuffer; std::vector<Packet25SegBuffer> packet25SegBuffer;
bool saveNewDigiEcoModeConfig = false; bool saveNewDigiEcoModeConfig = false;
bool packetIsBeacon = false; bool packetIsBeacon = false;
@@ -166,18 +164,33 @@ namespace STATION_Utils {
return false; return false;
} }
void clean25SegBuffer() { void clean25SegHashBuffer() {
if (!packet25SegBuffer.empty() && (millis() - packet25SegBuffer[0].receivedTime) > 25 * 1000) packet25SegBuffer.erase(packet25SegBuffer.begin()); uint32_t currentTime = millis();
} for (int i = packet25SegBuffer.size() - 1; i >= 0; i--) {
if ((currentTime - packet25SegBuffer[i].receivedTime) > 25 * 1000) {
bool check25SegBuffer(const String& station, const String& textMessage) { packet25SegBuffer.erase(packet25SegBuffer.begin() + i);
if (!packet25SegBuffer.empty()) {
for (int i = 0; i < packet25SegBuffer.size(); i++) {
if (packet25SegBuffer[i].station == station && packet25SegBuffer[i].payload == textMessage) return false;
} }
} }
packet25SegBuffer.emplace_back(Packet25SegBuffer{millis(), station, textMessage}); }
return true;
uint32_t makeHash(const String& station, const String& payload) { // DJB2 Hash
uint32_t h = 5381;
for (size_t i = 0; i < station.length(); i++)
h = ((h << 5) + h) + station[i];
for (size_t i = 0; i < payload.length(); i++)
h = ((h << 5) + h) + payload[i];
return h;
}
bool isIn25SegHashBuffer(const String& station, const String& textMessage) {
uint32_t newHash = makeHash(station, textMessage);
uint32_t currentTime = millis();
clean25SegHashBuffer();
for (int i = 0; i < packet25SegBuffer.size(); i++) {
if (packet25SegBuffer[i].hash == newHash) return true;
}
packet25SegBuffer.push_back({currentTime, newHash});
return false;
} }
void processOutputPacketBufferUltraEcoMode() { void processOutputPacketBufferUltraEcoMode() {