Refactor callsign management into separate module and add support for baseic WIDEn-n digirepating

pull/15/head
sh123 2020-06-14 14:52:22 +03:00
rodzic c042747fff
commit 9c06c93ec0
7 zmienionych plików z 268 dodań i 112 usunięć

141
ax25_callsign.cpp 100644
Wyświetl plik

@ -0,0 +1,141 @@
#include "ax25_callsign.h"
namespace AX25 {
Callsign::Callsign()
: isValid_(false)
, call_()
, ssid_(0)
{
}
Callsign::Callsign(const Callsign &callsign)
: isValid_(callsign.isValid_)
, call_(callsign.call_)
, ssid_(callsign.ssid_)
{
}
Callsign& Callsign::operator=(const Callsign &callsign)
{
isValid_ = callsign.isValid_;
call_ = callsign.call_;
ssid_ = callsign.ssid_;
return *this;
}
Callsign::Callsign(const byte *rxPayload, int payloadLength)
: isValid_(false)
, call_()
, ssid_(0)
{
isValid_ = fromBinary(rxPayload, payloadLength);
}
Callsign::Callsign(String callsign)
: isValid_(false)
, call_()
, ssid_(0)
{
isValid_ = fromString(callsign);
}
bool Callsign::ToBinary(byte *txPayload, int bufferLength) const
{
return encode(txPayload, bufferLength);
}
String Callsign::ToString() const
{
String result = call_;
if (ssid_ != 0) {
result += "-" + String(ssid_);
}
return result;
}
bool Callsign::Digirepeat(const String &ownCallsign)
{
// only WIDE supported
if (call_.startsWith("WIDE*")) return false;
if (call_.startsWith("WIDE") && call_.length() >= 5) {
char wideLevel = call_.charAt(4);
if ((wideLevel == '1' || wideLevel == '2' || wideLevel == '3') && ssid_ > 0) {
if (--ssid_ == 0) {
call_ += "*";
}
return true;
}
}
return false;
}
bool Callsign::encode(byte *txPtr, int bufferLength) const
{
if (bufferLength < CallsignSize) return false;
byte *ptr = txPtr;
memset(ptr, 0, bufferLength);
for (int i = 0; i < CallsignSize - 1; i++) {
if (i < call_.length()) {
char c = call_.charAt(i);
*(ptr++) = c << 1;
}
else {
*(ptr++) = char(' ') << 1;
}
}
*(txPtr + CallsignSize - 1) = ssid_ << 1;
return true;
}
bool Callsign::fromString(String callsign)
{
// "ABCDEF-XX"
if (callsign.length() > CallsignSize + 2 || callsign.length() == 0) return false;
int delimIndex = callsign.indexOf('-');
// "ABCDEF-"
if (delimIndex = callsign.length() - 1) return false;
call_ = callsign;
ssid_ = 0;
if (delimIndex == -1) {
// "ABCDEFG"
if (callsign.length() >= CallsignSize) return false;
}
else {
call_ = callsign.substring(0, delimIndex);
ssid_ = callsign.substring(delimIndex + 1).toInt();
}
return true;
}
bool Callsign::fromBinary(const byte *rxPtr, int length)
{
if (length < CallsignSize) return false;
byte callsign[CallsignSize];
const byte *ptr = rxPtr;
memset(callsign, 0, sizeof(callsign));
for (int i = 0; i < CallsignSize - 1; i++) {
char c = *(ptr++) >> 1;
callsign[i] = (c == ' ') ? '\0' : c;
}
callsign[CallsignSize-1] = '\0';
ssid_ = (*ptr >> 1) & 0x0f;
call_ = String((char*)callsign);
return true;
}
} // AX25

42
ax25_callsign.h 100644
Wyświetl plik

@ -0,0 +1,42 @@
#ifndef AX25_CALLSIGN_H
#define AX25_CALLSIGN_H
#include <Arduino.h>
namespace AX25 {
class Callsign
{
public:
Callsign();
Callsign(const Callsign &callsign);
Callsign& operator=(const Callsign &callsign);
Callsign(const byte *rxPayload, int payloadLength);
Callsign(String inputText);
inline bool IsValid() const { return isValid_; };
String ToString() const;
bool ToBinary(byte *txPayload, int bufferLength) const;
bool Digirepeat(const String &ownCallsign);
private:
bool encode(byte *txPtr, int bufferLength) const;
bool fromString(String callsign);
bool fromBinary(const byte *rxPtr, int payloadLength);
private:
const int CallsignSize = 7;
private:
bool isValid_;
String call_;
byte ssid_;
};
} // AX25
#endif // APRSMSG_H

Wyświetl plik

@ -5,47 +5,47 @@ namespace AX25 {
Payload::Payload(byte *rxPayload, int payloadLength) Payload::Payload(byte *rxPayload, int payloadLength)
: rptCallsCount_(0) : rptCallsCount_(0)
{ {
isValid_ = parsePayload(rxPayload, payloadLength); isValid_ = fromBinary(rxPayload, payloadLength);
} }
Payload::Payload(String inputText) Payload::Payload(String inputText)
: rptCallsCount_(0) : rptCallsCount_(0)
{ {
isValid_ = parseString(inputText); isValid_ = fromString(inputText);
} }
void Payload::Dump() void Payload::Dump()
{ {
Serial.println(); Serial.println();
Serial.print("valid: "); Serial.println(isValid_); Serial.print("valid: "); Serial.println(isValid_);
Serial.println("src: " + srcCall_); Serial.println("src: " + srcCall_.ToString());
Serial.println("dst: " + dstCall_); Serial.println("dst: " + dstCall_.ToString());
Serial.print("rpt: "); Serial.print("rpt: ");
for (int i = 0; i < rptCallsCount_; i++) { for (int i = 0; i < rptCallsCount_; i++) {
Serial.print(rptCalls_[i] + " "); Serial.print(rptCalls_[i].ToString() + " ");
} }
Serial.println(); Serial.println();
Serial.println("info: " + info_); Serial.println("info: " + info_);
} }
int Payload::ToBinary(byte *txPayload, int bufferLength) int Payload::ToBinary(byte *txPayload, int bufferLength) const
{ {
byte *txPtr = txPayload; byte *txPtr = txPayload;
byte *txEnd = txPayload + bufferLength; byte *txEnd = txPayload + bufferLength;
// destination address // destination address
if (!encodeCall(dstCall_, txPtr, CallsignSize)) return 0; if (!dstCall_.ToBinary(txPtr, CallsignSize)) return 0;
txPtr += CallsignSize; txPtr += CallsignSize;
if (txPtr >= txEnd) return 0; if (txPtr >= txEnd) return 0;
// source address // source address
if (!encodeCall(srcCall_, txPtr, CallsignSize)) return 0; if (!srcCall_.ToBinary(txPtr, CallsignSize)) return 0;
txPtr += CallsignSize; txPtr += CallsignSize;
if (txPtr >= txEnd) return 0; if (txPtr >= txEnd) return 0;
// digipeater addresses // digipeater addresses
for (int i = 0; i < rptCallsCount_; i++) { for (int i = 0; i < rptCallsCount_; i++) {
if (!encodeCall(rptCalls_[i], txPtr, CallsignSize)) return 0; if (!rptCalls_[i].ToBinary(txPtr, CallsignSize)) return 0;
txPtr += CallsignSize; txPtr += CallsignSize;
if (txPtr >= txEnd) return 0; if (txPtr >= txEnd) return 0;
} }
@ -67,13 +67,13 @@ int Payload::ToBinary(byte *txPayload, int bufferLength)
return (int)(txPtr-txPayload); return (int)(txPtr-txPayload);
} }
String Payload::ToText(String customComment) String Payload::ToString(String customComment)
{ {
String txt = srcCall_ + String(">") + dstCall_; String txt = srcCall_.ToString() + String(">") + dstCall_.ToString();
for (int i = 0; i < rptCallsCount_; i++) { for (int i = 0; i < rptCallsCount_; i++) {
if (rptCalls_[i].length() > 0) { if (rptCalls_[i].IsValid()) {
txt += String(",") + rptCalls_[i]; txt += String(",") + rptCalls_[i].ToString();
} }
} }
@ -86,18 +86,30 @@ String Payload::ToText(String customComment)
return txt + String("\n"); return txt + String("\n");
} }
bool Payload::parsePayload(const byte *rxPayload, int payloadLength) bool Payload::Digirepeat(const String &ownCallsign)
{
for (int i = 0; i < rptCallsCount_; i++) {
if (rptCalls_[i].Digirepeat(ownCallsign)) {
return true;
}
}
return false;
}
bool Payload::fromBinary(const byte *rxPayload, int payloadLength)
{ {
const byte *rxPtr = rxPayload; const byte *rxPtr = rxPayload;
const byte *rxEnd = rxPayload + payloadLength; const byte *rxEnd = rxPayload + payloadLength;
if (payloadLength < CallsignSize) return false;
// destination address // destination address
dstCall_ = decodeCall(rxPtr); dstCall_ = AX25::Callsign(rxPtr, CallsignSize);
rxPtr += CallsignSize; rxPtr += CallsignSize;
if (rxPtr >= rxEnd) return false; if (rxPtr >= rxEnd) return false;
// source address // source address
srcCall_ = decodeCall(rxPtr); srcCall_ = AX25::Callsign(rxPtr, CallsignSize);
rxPtr += CallsignSize; rxPtr += CallsignSize;
if (rxPtr >= rxEnd) return false; if (rxPtr >= rxEnd) return false;
@ -106,7 +118,7 @@ bool Payload::parsePayload(const byte *rxPayload, int payloadLength)
// digipeater addresses // digipeater addresses
for (int i = 0; i < RptMaxCount; i++) { for (int i = 0; i < RptMaxCount; i++) {
if ((rxPayload[(i + 2) * CallsignSize - 1] & 1) == 0) { if ((rxPayload[(i + 2) * CallsignSize - 1] & 1) == 0) {
rptCalls_[i] = decodeCall(rxPtr); rptCalls_[i] = AX25::Callsign(rxPtr, CallsignSize);
rptCallsCount_++; rptCallsCount_++;
rxPtr += CallsignSize; rxPtr += CallsignSize;
if (rxPtr >= rxEnd) return false; if (rxPtr >= rxEnd) return false;
@ -129,7 +141,7 @@ bool Payload::parsePayload(const byte *rxPayload, int payloadLength)
return true; return true;
} }
bool Payload::parseString(String inputText) bool Payload::fromString(String inputText)
{ {
int rptIndex = inputText.indexOf('>'); int rptIndex = inputText.indexOf('>');
int infoIndex = inputText.indexOf(':'); int infoIndex = inputText.indexOf(':');
@ -160,60 +172,4 @@ bool Payload::parseString(String inputText)
return true; return true;
} }
bool Payload::encodeCall(String inputText, byte *txPtr, int bufferLength)
{
if (bufferLength < CallsignSize) return false;
String callsign = inputText;
byte ssid = 0;
int delimIndex = inputText.indexOf('-');
if (delimIndex + 1 >= inputText.length()) return false;
if (delimIndex != -1) {
callsign = inputText.substring(0, delimIndex);
ssid = inputText.substring(delimIndex + 1).toInt();
}
byte *ptr = txPtr;
memset(ptr, 0, bufferLength);
for (int i = 0; i < CallsignSize - 1; i++) {
if (i < callsign.length()) {
char c = callsign.charAt(i);
*(ptr++) = c << 1;
}
else {
*(ptr++) = char(' ') << 1;
}
}
*(txPtr + CallsignSize - 1) = ssid << 1;
return true;
}
String Payload::decodeCall(const byte *rxPtr)
{
byte callsign[CallsignSize];
const byte *ptr = rxPtr;
memset(callsign, 0, sizeof(callsign));
for (int i = 0; i < CallsignSize - 1; i++) {
char c = *(ptr++) >> 1;
callsign[i] = (c == ' ') ? '\0' : c;
}
callsign[CallsignSize-1] = '\0';
byte ssid = (*ptr >> 1) & 0x0f;
String result = String((char*)callsign);
if (result.length() > 0 && ssid != 0) {
result += String("-") + String(ssid);
}
return result;
}
} // AX25 } // AX25

Wyświetl plik

@ -1,8 +1,10 @@
#ifndef APRSMSG_H #ifndef AX25_PAYLOAD_H
#define APRSMSG_H #define AX25_PAYLOAD_H
#include <Arduino.h> #include <Arduino.h>
#include "ax25_callsign.h"
namespace AX25 { namespace AX25 {
class Payload class Payload
@ -13,17 +15,15 @@ public:
inline bool IsValid() const { return isValid_; } inline bool IsValid() const { return isValid_; }
String ToText(String customComment); String ToString(String customComment);
int ToBinary(byte *txPayload, int bufferLength); int ToBinary(byte *txPayload, int bufferLength) const;
bool Digirepeat(const String &ownCallsign);
void Dump(); void Dump();
private: private:
String decodeCall(const byte *rxPtr); bool fromString(String inputText);
bool encodeCall(String callsign, byte *txPtr, int bufferLength); bool fromBinary(const byte *rxPayload, int payloadLength);
bool parseString(String inputText);
bool parsePayload(const byte *rxPayload, int payloadLength);
private: private:
enum AX25Ctrl { enum AX25Ctrl {
@ -39,8 +39,8 @@ private:
private: private:
bool isValid_; bool isValid_;
String srcCall_, dstCall_; Callsign srcCall_, dstCall_;
String rptCalls_[7]; Callsign rptCalls_[7];
int rptCallsCount_; int rptCallsCount_;
String info_; String info_;
}; };

Wyświetl plik

@ -34,6 +34,7 @@ void initializeConfig() {
cfg.EnableSignalReport = true; cfg.EnableSignalReport = true;
cfg.EnableAutoFreqCorrection = true; cfg.EnableAutoFreqCorrection = true;
cfg.EnablePersistentAprsConnection = true; cfg.EnablePersistentAprsConnection = true;
cfg.EnableRfToIs = true;
cfg.EnableIsToRf = false; cfg.EnableIsToRf = false;
cfg.EnableRepeater = false; cfg.EnableRepeater = false;
} }

Wyświetl plik

@ -11,6 +11,7 @@ void LoraPrs::setup(const LoraPrsConfig &conf)
{ {
isClient_ = conf.IsClientMode; isClient_ = conf.IsClientMode;
loraFreq_ = conf.LoraFreq; loraFreq_ = conf.LoraFreq;
ownCallsign_ = conf.AprsLogin;
aprsLogin_ = String("user ") + conf.AprsLogin + String(" pass ") + aprsLogin_ = String("user ") + conf.AprsLogin + String(" pass ") +
conf.AprsPass + String(" vers ") + CfgLoraprsVersion; conf.AprsPass + String(" vers ") + CfgLoraprsVersion;
@ -25,6 +26,7 @@ void LoraPrs::setup(const LoraPrsConfig &conf)
autoCorrectFreq_ = conf.EnableAutoFreqCorrection; autoCorrectFreq_ = conf.EnableAutoFreqCorrection;
addSignalReport_ = conf.EnableSignalReport; addSignalReport_ = conf.EnableSignalReport;
persistentConn_ = conf.EnablePersistentAprsConnection; persistentConn_ = conf.EnablePersistentAprsConnection;
enableRfToIs_ = conf.EnableRfToIs;
enableIsToRf_ = conf.EnableIsToRf; enableIsToRf_ = conf.EnableIsToRf;
enableRepeater_ = conf.EnableRepeater; enableRepeater_ = conf.EnableRepeater;
@ -140,12 +142,8 @@ void LoraPrs::loop()
delay(10); delay(10);
} }
void LoraPrs::onRfAprsReceived(String aprsMessage) void LoraPrs::sendToAprsis(String aprsMessage)
{ {
Serial.print(aprsMessage);
if (isClient_) return;
if (WiFi.status() != WL_CONNECTED) { if (WiFi.status() != WL_CONNECTED) {
reconnectWifi(); reconnectWifi();
} }
@ -173,24 +171,28 @@ void LoraPrs::onAprsisDataAvailable()
if (enableIsToRf_ && aprsisData.length() > 0) { if (enableIsToRf_ && aprsisData.length() > 0) {
AX25::Payload payload(aprsisData); AX25::Payload payload(aprsisData);
sendToLora(payload);
}
}
if (payload.IsValid()) { bool LoraPrs::sendToLora(const AX25::Payload &payload)
{
if (!payload.IsValid()) {
Serial.println("Invalid payload from APRSIS");
return false;
}
byte buf[512]; byte buf[512];
int bytesWritten = payload.ToBinary(buf, sizeof(buf)); int bytesWritten = payload.ToBinary(buf, sizeof(buf));
if (bytesWritten > 0) { if (bytesWritten <= 0) {
Serial.println("Failed to serialize payload");
return false;
}
LoRa.beginPacket(); LoRa.beginPacket();
LoRa.write(buf, bytesWritten); LoRa.write(buf, bytesWritten);
LoRa.endPacket(true); LoRa.endPacket(true);
} return true;
else {
Serial.println("Failed to serialize payload");
}
}
else {
Serial.println("Invalid payload from APRSIS");
}
}
} }
void LoraPrs::onLoraDataAvailable(int packetSize) void LoraPrs::onLoraDataAvailable(int packetSize)
@ -243,7 +245,15 @@ void LoraPrs::onLoraDataAvailable(int packetSize)
AX25::Payload payload(rxBuf, rxBufIndex); AX25::Payload payload(rxBuf, rxBufIndex);
if (payload.IsValid()) { if (payload.IsValid()) {
onRfAprsReceived(payload.ToText(addSignalReport_ ? signalReport : String())); String textPayload = payload.ToString(addSignalReport_ ? signalReport : String());
Serial.print(textPayload);
if (enableRfToIs_ && !isClient_) {
sendToAprsis(textPayload);
}
if (enableRepeater_ && payload.Digirepeat(ownCallsign_)) {
sendToLora(payload);
}
} }
else { else {
Serial.println("Invalid payload from LoRA"); Serial.println("Invalid payload from LoRA");

Wyświetl plik

@ -34,6 +34,7 @@ struct LoraPrsConfig
bool EnableSignalReport; bool EnableSignalReport;
bool EnableAutoFreqCorrection; bool EnableAutoFreqCorrection;
bool EnablePersistentAprsConnection; bool EnablePersistentAprsConnection;
bool EnableRfToIs;
bool EnableIsToRf; bool EnableIsToRf;
bool EnableRepeater; bool EnableRepeater;
}; };
@ -58,7 +59,8 @@ private:
void onBtDataAvailable(); void onBtDataAvailable();
void onAprsisDataAvailable(); void onAprsisDataAvailable();
void onRfAprsReceived(String aprsMessage); void sendToAprsis(String aprsMessage);
bool sendToLora(const AX25::Payload &payload);
void kissResetState(); void kissResetState();
@ -83,6 +85,7 @@ private:
}; };
const String CfgLoraprsVersion = "LoRAPRS 0.1"; const String CfgLoraprsVersion = "LoRAPRS 0.1";
const String CfgAprsSoftware = "APZLRA";
const byte CfgPinSs = 5; const byte CfgPinSs = 5;
const byte CfgPinRst = 26; const byte CfgPinRst = 26;
@ -92,6 +95,8 @@ private:
bool isClient_; bool isClient_;
long loraFreq_; long loraFreq_;
String ownCallsign_;
String aprsHost_; String aprsHost_;
int aprsPort_; int aprsPort_;
String aprsLogin_; String aprsLogin_;
@ -99,6 +104,7 @@ private:
bool autoCorrectFreq_; bool autoCorrectFreq_;
bool addSignalReport_; bool addSignalReport_;
bool persistentConn_; bool persistentConn_;
bool enableRfToIs_;
bool enableIsToRf_; bool enableIsToRf_;
bool enableRepeater_; bool enableRepeater_;