Implement IS to RF gating (#6)

* Add aprs string parsing

* Added method for conversion to binary ax25 packet

* Check if message is valid before forwarding to APRS-IS

* Added APRIS data sending into RF

* Added Lora sending for IS packets

* Fixed bugs in packet encoding

* Updated readme
pull/15/head
sh123 2020-06-14 01:15:05 +03:00 zatwierdzone przez GitHub
rodzic c88ed243bc
commit b945b46fbb
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 207 dodań i 35 usunięć

Wyświetl plik

@ -43,7 +43,7 @@ All work was done on ESP32-WROOM with custom made LoRa shield, if your ESP32 boa
- other useful options are
- `cfg.EnableSignalReport` set to `true` to enable signal report, it will be added as a comment to APRS-IS submitted location
- `cfg.EnablePersistentAprsConnection` set to `false` to avoid keeping connection open to APRS-IS
- **[INACTIVE YET]** `cfg.EnableIsToRf` set to `true` to forward APRS-IS traffic to RF
- `cfg.EnableIsToRf` set to `true` to forward APRS-IS traffic to RF, see also `cfg.AprsFilter` for traffic filtering
- **[INACTIVE YET]** `cfg.EnableRepeater` set to `true` to enable packet repeater
# Test Results

Wyświetl plik

@ -3,11 +3,71 @@
namespace AX25 {
Payload::Payload(byte *rxPayload, int payloadLength)
: rptCallsCount_(0)
{
parsePayload(rxPayload, payloadLength);
isValid_ = parsePayload(rxPayload, payloadLength);
}
String Payload::ToText(const String &customComment)
Payload::Payload(String inputText)
: rptCallsCount_(0)
{
isValid_ = parseString(inputText);
}
void Payload::Dump()
{
Serial.println();
Serial.print("valid: "); Serial.println(isValid_);
Serial.println("src: " + srcCall_);
Serial.println("dst: " + dstCall_);
Serial.print("rpt: ");
for (int i = 0; i < rptCallsCount_; i++) {
Serial.print(rptCalls_[i] + " ");
}
Serial.println();
Serial.println("info: " + info_);
}
int Payload::ToBinary(byte *txPayload, int bufferLength)
{
byte *txPtr = txPayload;
byte *txEnd = txPayload + bufferLength;
// destination address
if (!encodeCall(dstCall_, txPtr, CallsignSize)) return 0;
txPtr += CallsignSize;
if (txPtr >= txEnd) return 0;
// source address
if (!encodeCall(srcCall_, txPtr, CallsignSize)) return 0;
txPtr += CallsignSize;
if (txPtr >= txEnd) return 0;
// digipeater addresses
for (int i = 0; i < rptCallsCount_; i++) {
if (!encodeCall(rptCalls_[i], txPtr, CallsignSize)) return 0;
txPtr += CallsignSize;
if (txPtr >= txEnd) return 0;
}
*(txPtr - 1) |= 1;
// control + protocol id
if ((txPtr + 2) >= txEnd) return 0;
*(txPtr++) = AX25Ctrl::UI;
*(txPtr++) = AX25Pid::NoLayer3;
// information field
for (int i = 0; i < info_.length(); i++) {
char c = info_.charAt(i);
if (c == '\0') break;
*(txPtr++) = c;
if (txPtr >= txEnd) break;
}
return (int)(txPtr-txPayload);
}
String Payload::ToText(String customComment)
{
String txt = srcCall_ + String(">") + dstCall_;
@ -26,15 +86,15 @@ String Payload::ToText(const String &customComment)
return txt + String("\n");
}
bool Payload::parsePayload(byte *rxPayload, int payloadLength)
bool Payload::parsePayload(const byte *rxPayload, int payloadLength)
{
byte *rxPtr = rxPayload;
byte *rxEnd = rxPayload + payloadLength;
const byte *rxPtr = rxPayload;
const byte *rxEnd = rxPayload + payloadLength;
// destination address
dstCall_ = decodeCall(rxPtr);
rxPtr += CallsignSize;
if (rxPtr >= rxPayload + payloadLength) return false;
if (rxPtr >= rxEnd) return false;
// source address
srcCall_ = decodeCall(rxPtr);
@ -42,7 +102,7 @@ bool Payload::parsePayload(byte *rxPayload, int payloadLength)
if (rxPtr >= rxEnd) return false;
rptCallsCount_ = 0;
// digipeater addresses
for (int i = 0; i < RptMaxCount; i++) {
if ((rxPayload[(i + 2) * CallsignSize - 1] & 1) == 0) {
@ -69,25 +129,88 @@ bool Payload::parsePayload(byte *rxPayload, int payloadLength)
return true;
}
String Payload::decodeCall(byte *rxPtr)
bool Payload::parseString(String inputText)
{
int rptIndex = inputText.indexOf('>');
int infoIndex = inputText.indexOf(':');
// bad format
if (rptIndex == -1 || infoIndex == -1) return false;
info_ = inputText.substring(infoIndex + 1);
srcCall_ = inputText.substring(0, rptIndex);
String paths = inputText.substring(rptIndex + 1, infoIndex);
rptCallsCount_ = 0;
int index = 0;
while (rptCallsCount_ <= RptMaxCount) {
int nextIndex = paths.indexOf(',', index);
String pathItem = paths.substring(index, nextIndex == -1 ? paths.length() : nextIndex);
if (index == 0) {
dstCall_ = pathItem;
}
else {
rptCallsCount_++;
rptCalls_[rptCallsCount_ - 1] = pathItem;
}
if (nextIndex == -1) break;
index = nextIndex + 1;
}
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];
char ssid;
byte *ptr = rxPtr;
const byte *ptr = rxPtr;
memset(callsign, 0, sizeof(callsign));
for (int i = 0; i < 6; i++) {
for (int i = 0; i < CallsignSize - 1; i++) {
char c = *(ptr++) >> 1;
callsign[i] = (c == ' ') ? '\0' : c;
}
callsign[CallsignSize-1] = '\0';
ssid = (*ptr >> 1);
byte ssid = (*ptr >> 1) & 0x0f;
String result = String((char*)callsign);
if (result.length() > 0 && ssid >= '0' && ssid <= '9') {
if (result.length() > 0 && ssid != 0) {
result += String("-") + String(ssid);
}
return result;

Wyświetl plik

@ -9,11 +9,21 @@ class Payload
{
public:
Payload(byte *rxPayload, int payloadLength);
String ToText(const String &customComment);
Payload(String inputText);
inline bool IsValid() const { return isValid_; }
String ToText(String customComment);
int ToBinary(byte *txPayload, int bufferLength);
void Dump();
private:
String decodeCall(byte *rxPtr);
bool parsePayload(byte *rxPayload, int payloadLength);
String decodeCall(const byte *rxPtr);
bool encodeCall(String callsign, byte *txPtr, int bufferLength);
bool parseString(String inputText);
bool parsePayload(const byte *rxPayload, int payloadLength);
private:
enum AX25Ctrl {
@ -28,6 +38,7 @@ private:
const int RptMaxCount = 7;
private:
bool isValid_;
String srcCall_, dstCall_;
String rptCalls_[7];
int rptCallsCount_;

Wyświetl plik

@ -4,7 +4,7 @@ LoraPrs::LoraPrs()
: serialBt_()
, kissState_(KissState::Void)
, kissCmd_(KissCmd::NoCmd)
{
{
}
void LoraPrs::setup(const LoraPrsConfig &conf)
@ -38,7 +38,7 @@ void LoraPrs::setup(const LoraPrsConfig &conf)
}
}
void LoraPrs::setupWifi(const String &wifiName, const String &wifiKey)
void LoraPrs::setupWifi(const String &wifiName, const String &wifiKey)
{
if (!isClient_) {
Serial.print("WIFI connecting to " + wifiName);
@ -56,8 +56,8 @@ void LoraPrs::setupWifi(const String &wifiName, const String &wifiKey)
}
}
void LoraPrs::reconnectWifi() {
void LoraPrs::reconnectWifi()
{
Serial.print("WIFI re-connecting...");
while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0,0,0,0)) {
@ -69,8 +69,8 @@ void LoraPrs::reconnectWifi() {
Serial.println("ok");
}
bool LoraPrs::reconnectAprsis() {
bool LoraPrs::reconnectAprsis()
{
Serial.print("APRSIS connecting...");
if (!aprsisConn_.connect(aprsHost_.c_str(), aprsPort_)) {
@ -78,7 +78,7 @@ bool LoraPrs::reconnectAprsis() {
return false;
}
Serial.println("ok");
aprsisConn_.print(aprsLogin_);
return true;
}
@ -127,9 +127,8 @@ void LoraPrs::loop()
if (!aprsisConn_.connected()) {
reconnectAprsis();
}
while (aprsisConn_.available() > 0) {
char c = aprsisConn_.read();
Serial.print(c);
if (aprsisConn_.available() > 0) {
onAprsisDataAvailable();
}
}
if (serialBt_.available()) {
@ -141,17 +140,18 @@ void LoraPrs::loop()
delay(10);
}
void LoraPrs::onRfAprsReceived(const String &aprsMessage)
void LoraPrs::onRfAprsReceived(String aprsMessage)
{
Serial.print(aprsMessage);
if (isClient_) return;
if (WiFi.status() != WL_CONNECTED) {
reconnectWifi();
}
if (!aprsisConn_.connected()) {
reconnectAprsis();
}
Serial.print(aprsMessage);
aprsisConn_.print(aprsMessage);
if (!persistentConn_) {
@ -159,6 +159,40 @@ void LoraPrs::onRfAprsReceived(const String &aprsMessage)
}
}
void LoraPrs::onAprsisDataAvailable()
{
String aprsisData;
while (aprsisConn_.available() > 0) {
char c = aprsisConn_.read();
if (c == '\r') continue;
Serial.print(c);
if (c == '\n') break;
aprsisData += c;
}
if (enableIsToRf_ && aprsisData.length() > 0) {
AX25::Payload payload(aprsisData);
if (payload.IsValid()) {
byte buf[512];
int bytesWritten = payload.ToBinary(buf, sizeof(buf));
if (bytesWritten > 0) {
LoRa.beginPacket();
LoRa.write(buf, bytesWritten);
LoRa.endPacket(true);
}
else {
Serial.println("Failed to serialize payload");
}
}
else {
Serial.println("Invalid payload from APRSIS");
}
}
}
void LoraPrs::onLoraDataAvailable(int packetSize)
{
int rxBufIndex = 0;
@ -206,10 +240,13 @@ void LoraPrs::onLoraDataAvailable(int packetSize)
LoRa.setFrequency(loraFreq_);
}
String aprsMsg = AX25::Payload(rxBuf, rxBufIndex).ToText(addSignalReport_ ? signalReport : String());
AX25::Payload payload(rxBuf, rxBufIndex);
if (aprsMsg.length() != 0) {
onRfAprsReceived(aprsMsg);
if (payload.IsValid()) {
onRfAprsReceived(payload.ToText(addSignalReport_ ? signalReport : String()));
}
else {
Serial.println("Invalid payload from LoRA");
}
delay(50);

Wyświetl plik

@ -56,8 +56,9 @@ private:
void onLoraDataAvailable(int packetSize);
void onBtDataAvailable();
void onAprsisDataAvailable();
void onRfAprsReceived(const String &aprsMessage);
void onRfAprsReceived(String aprsMessage);
void kissResetState();