TNC: recognize and ignore non data frames

This commit is contained in:
Łukasz Nidecki 2021-08-21 22:35:52 +02:00
parent c0eb892a7c
commit 6cd887f91a
3 changed files with 152 additions and 132 deletions

View File

@ -7,7 +7,7 @@ String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay
bool validateKISSFrame(const String &kissFormattedFrame); bool validateKISSFrame(const String &kissFormattedFrame);
String decapsulateKISS(const String& frame); String decapsulateKISS(const String &frame);
/* /*
* https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS * https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS
@ -33,100 +33,112 @@ String decapsulateKISS(const String& frame);
*/ */
String encode_kiss(const String& tnc2FormattedFrame) { String encode_kiss(const String &tnc2FormattedFrame) {
String ax25Frame = ""; String ax25Frame = "";
if (validateTNC2Frame(tnc2FormattedFrame)){ if (validateTNC2Frame(tnc2FormattedFrame)) {
String address = ""; String address = "";
bool dst_addres_written = false; bool dst_addres_written = false;
for (int p=0;p<=tnc2FormattedFrame.indexOf(':');p++){ for (int p = 0; p <= tnc2FormattedFrame.indexOf(':'); p++) {
char currentChar = tnc2FormattedFrame.charAt(p); char currentChar = tnc2FormattedFrame.charAt(p);
if (currentChar == ':' || currentChar == '>' || currentChar == ','){ if (currentChar == ':' || currentChar == '>' || currentChar == ',') {
if (!dst_addres_written && (currentChar == ',' || currentChar == ':')){ if (!dst_addres_written && (currentChar == ',' || currentChar == ':')) {
// ax25 frame DST SRC // ax25 frame DST SRC
// tnc2 frame SRC DST // tnc2 frame SRC DST
ax25Frame = encode_address_ax25(address) + ax25Frame; ax25Frame = encode_address_ax25(address) + ax25Frame;
dst_addres_written = true; dst_addres_written = true;
} else { } else {
ax25Frame += encode_address_ax25(address); ax25Frame += encode_address_ax25(address);
}
address = "";
} else {
address += currentChar;
}
} }
auto lastAddressChar = (uint8_t)ax25Frame.charAt(ax25Frame.length() - 1); address = "";
ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK)); } else {
ax25Frame += (char)APRS_CONTROL_FIELD; address += currentChar;
ax25Frame += (char)APRS_INFORMATION_FIELD; }
ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':')+1);
} }
auto lastAddressChar = (uint8_t) ax25Frame.charAt(ax25Frame.length() - 1);
ax25Frame.setCharAt(ax25Frame.length() - 1, (char) (lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK));
ax25Frame += (char) APRS_CONTROL_FIELD;
ax25Frame += (char) APRS_INFORMATION_FIELD;
ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':') + 1);
}
String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA); String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA);
return kissFrame; return kissFrame;
} }
String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd) { String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd) {
String kissFrame = ""; String kissFrame = "";
kissFrame += (char)FEND; // start of frame kissFrame += (char) FEND; // start of frame
kissFrame += (char)(0x0f & TNCCmd); // TNC0, cmd kissFrame += (char) (0x0f & TNCCmd); // TNC0, cmd
for (int i = 0; i < ax25Frame.length(); ++i) { for (int i = 0; i < ax25Frame.length(); ++i) {
char currentChar = ax25Frame.charAt(i); char currentChar = ax25Frame.charAt(i);
if (currentChar == (char)FEND){ if (currentChar == (char) FEND) {
kissFrame += (char)FESC; kissFrame += (char) FESC;
kissFrame += (char)TFEND; kissFrame += (char) TFEND;
} else if (currentChar == (char)FESC){ } else if (currentChar == (char) FESC) {
kissFrame += (char)FESC; kissFrame += (char) FESC;
kissFrame += (char)TFESC; kissFrame += (char) TFESC;
} else { } else {
kissFrame += currentChar; kissFrame += currentChar;
}
} }
kissFrame += (char)FEND; // end of frame }
return kissFrame; kissFrame += (char) FEND; // end of frame
return kissFrame;
} }
String decapsulateKISS(const String& frame) { String decapsulateKISS(const String &frame) {
String ax25Frame = ""; String ax25Frame = "";
for (int i = 2; i < frame.length() - 1; ++i) { for (int i = 2; i < frame.length() - 1; ++i) {
char currentChar = frame.charAt(i); char currentChar = frame.charAt(i);
if (currentChar == (char)FESC){ if (currentChar == (char) FESC) {
char nextChar = frame.charAt(i+1); char nextChar = frame.charAt(i + 1);
if (nextChar == (char)TFEND){ if (nextChar == (char) TFEND) {
ax25Frame += (char)FEND; ax25Frame += (char) FEND;
} else if (nextChar == (char)TFESC){ } else if (nextChar == (char) TFESC) {
ax25Frame += (char)FESC; ax25Frame += (char) FESC;
} }
i++; i++;
} else { } else {
ax25Frame += currentChar; ax25Frame += currentChar;
}
} }
}
return ax25Frame; return ax25Frame;
} }
String decode_kiss(const String& kissFormattedFrame) { /**
String TNC2Frame = ""; *
* @param inputKISSTNCFrame
* @param dataFrame
* @return Decapsulated TNC2KISS APRS data frame, or raw command data frame
*/
String decode_kiss(const String &inputKISSTNCFrame, bool &dataFrame) {
String TNC2Frame = "";
if (validateKISSFrame(kissFormattedFrame)){ if (validateKISSFrame(inputKISSTNCFrame)) {
String ax25Frame = decapsulateKISS(kissFormattedFrame); dataFrame = inputKISSTNCFrame.charAt(1) == CMD_DATA;
bool isLast = false; if (dataFrame){
String dst_addr = decode_address_ax25(ax25Frame.substring(0, 7), isLast, false); String ax25Frame = decapsulateKISS(inputKISSTNCFrame);
String src_addr = decode_address_ax25(ax25Frame.substring(7, 14), isLast, false); bool isLast = false;
TNC2Frame = src_addr + ">" + dst_addr; String dst_addr = decode_address_ax25(ax25Frame.substring(0, 7), isLast, false);
int digi_info_index = 14; String src_addr = decode_address_ax25(ax25Frame.substring(7, 14), isLast, false);
while (!isLast && digi_info_index + 7 < ax25Frame.length()){ TNC2Frame = src_addr + ">" + dst_addr;
String digi_addr = decode_address_ax25(ax25Frame.substring(digi_info_index, digi_info_index + 7), isLast, true); int digi_info_index = 14;
TNC2Frame += ',' + digi_addr; while (!isLast && digi_info_index + 7 < ax25Frame.length()) {
digi_info_index += 7; String digi_addr = decode_address_ax25(ax25Frame.substring(digi_info_index, digi_info_index + 7), isLast, true);
} TNC2Frame += ',' + digi_addr;
TNC2Frame += ':'; digi_info_index += 7;
TNC2Frame += ax25Frame.substring(digi_info_index + 2); }
TNC2Frame += ':';
TNC2Frame += ax25Frame.substring(digi_info_index + 2);
} else {
// command frame, currently ignored
TNC2Frame += inputKISSTNCFrame;
} }
}
return TNC2Frame; return TNC2Frame;
} }
/** /**
@ -135,32 +147,32 @@ String decode_kiss(const String& kissFormattedFrame) {
* @return * @return
*/ */
String encode_address_ax25(String tnc2Address) { String encode_address_ax25(String tnc2Address) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1; bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
if (tnc2Address.indexOf('-') == -1){ if (tnc2Address.indexOf('-') == -1) {
if (hasBeenDigipited){ if (hasBeenDigipited) {
// ex. TCPIP* in tnc2Address // ex. TCPIP* in tnc2Address
// so we skip last char // so we skip last char
tnc2Address = tnc2Address.substring(0, tnc2Address.length()-1); tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1);
}
tnc2Address += "-0";
} }
tnc2Address += "-0";
}
int separatorIndex = tnc2Address.indexOf('-'); int separatorIndex = tnc2Address.indexOf('-');
int ssid = tnc2Address.substring(separatorIndex + 1).toInt(); int ssid = tnc2Address.substring(separatorIndex + 1).toInt();
// TODO: SSID should not be > 16 // TODO: SSID should not be > 16
String kissAddress = ""; String kissAddress = "";
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
char addressChar; char addressChar;
if (tnc2Address.length() > i && i < separatorIndex){ if (tnc2Address.length() > i && i < separatorIndex) {
addressChar = tnc2Address.charAt(i); addressChar = tnc2Address.charAt(i);
} else { } else {
addressChar = ' '; addressChar = ' ';
}
kissAddress += (char)(addressChar << 1);
} }
kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0)); kissAddress += (char) (addressChar << 1);
return kissAddress; }
kissAddress += (char) ((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0));
return kissAddress;
} }
/** /**
@ -169,30 +181,35 @@ String encode_address_ax25(String tnc2Address) {
* @return * @return
*/ */
String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay) { String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay) {
String TNCAddress = ""; String TNCAddress = "";
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
uint8_t currentCharacter = ax25Address.charAt(i); uint8_t currentCharacter = ax25Address.charAt(i);
currentCharacter >>= 1; currentCharacter >>= 1;
if (currentCharacter != ' '){ if (currentCharacter != ' ') {
TNCAddress += (char)currentCharacter; TNCAddress += (char) currentCharacter;
}
} }
auto ssid_char = (uint8_t) ax25Address.charAt(6); }
bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK; auto ssid_char = (uint8_t) ax25Address.charAt(6);
isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK; bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK;
ssid_char >>= 1; isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK;
ssid_char >>= 1;
int ssid = 0b1111 & ssid_char; int ssid = 0b1111 & ssid_char;
if (ssid){ if (ssid) {
TNCAddress += '-'; TNCAddress += '-';
TNCAddress += ssid; TNCAddress += ssid;
} }
if (isRelay && hasBeenDigipited){ if (isRelay && hasBeenDigipited) {
TNCAddress += '*'; TNCAddress += '*';
} }
return TNCAddress; return TNCAddress;
} }
bool validateTNC2Frame(const String &tnc2FormattedFrame) { return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1); } bool validateTNC2Frame(const String &tnc2FormattedFrame) {
return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1);
}
bool validateKISSFrame(const String &kissFormattedFrame) { return kissFormattedFrame.charAt(0) == (char)FEND && kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char)FEND; } bool validateKISSFrame(const String &kissFormattedFrame) {
return kissFormattedFrame.charAt(0) == (char) FEND &&
kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char) FEND;
}

View File

@ -8,6 +8,6 @@
#define IS_LAST_ADDRESS_POSITION_MASK 0b1 #define IS_LAST_ADDRESS_POSITION_MASK 0b1
String encode_kiss(const String& tnc2FormattedFrame); String encode_kiss(const String& tnc2FormattedFrame);
String decode_kiss(const String& kissFormattedFrame); String decode_kiss(const String &inputKISSTNCFrame, bool &dataFrame);
String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd); String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd);

View File

@ -33,28 +33,31 @@ void handleKISSData(char character, int bufferIndex) {
} }
inTNCData->concat(character); inTNCData->concat(character);
if (character == (char) FEND && inTNCData->length() > 3) { if (character == (char) FEND && inTNCData->length() > 3) {
const String &TNC2DataFrame = decode_kiss(*inTNCData); bool isDataFrame = false;
const String &TNC2DataFrame = decode_kiss(*inTNCData, isDataFrame);
#ifdef LOCAL_KISS_ECHO if (isDataFrame) {
#ifdef LOCAL_KISS_ECHO
Serial.print(inTNCData); Serial.print(inTNCData);
#ifdef ENABLE_BLUETOOTH #ifdef ENABLE_BLUETOOTH
if (SerialBT.hasClient()) { if (SerialBT.hasClient()) {
SerialBT.print(inTNCData); SerialBT.print(inTNCData);
} }
#endif #endif
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
iterateWifiClients([](WiFiClient *client, const String *data){ iterateWifiClients([](WiFiClient *client, const String *data){
if (client->connected()){ if (client->connected()){
client->print(*data); client->print(*data);
client->flush(); client->flush();
} }
}, &inTNCData, clients, MAX_WIFI_CLIENTS); }, &inTNCData, clients, MAX_WIFI_CLIENTS);
#endif
#endif #endif
#endif auto *buffer = new String();
auto *buffer = new String(); buffer->concat(TNC2DataFrame);
buffer->concat(TNC2DataFrame); if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS) {
if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS){ delete buffer;
delete buffer; }
} }
inTNCData->clear(); inTNCData->clear();
} }