[APRS] Added support for APRS over LoRa
This commit is contained in:
parent
1bc7c5771c
commit
247ca753f1
2 changed files with 67 additions and 12 deletions
|
@ -6,9 +6,15 @@
|
||||||
|
|
||||||
APRSClient::APRSClient(AX25Client* ax) {
|
APRSClient::APRSClient(AX25Client* ax) {
|
||||||
axClient = ax;
|
axClient = ax;
|
||||||
|
phyLayer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t APRSClient::begin(char sym, bool alt) {
|
APRSClient::APRSClient(PhysicalLayer* phy) {
|
||||||
|
axClient = nullptr;
|
||||||
|
phyLayer = phy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t APRSClient::begin(char sym, char* callsign, uint8_t ssid, bool alt) {
|
||||||
RADIOLIB_CHECK_RANGE(sym, ' ', '}', RADIOLIB_ERR_INVALID_SYMBOL);
|
RADIOLIB_CHECK_RANGE(sym, ' ', '}', RADIOLIB_ERR_INVALID_SYMBOL);
|
||||||
symbol = sym;
|
symbol = sym;
|
||||||
|
|
||||||
|
@ -18,6 +24,16 @@ int16_t APRSClient::begin(char sym, bool alt) {
|
||||||
table = '/';
|
table = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((!src) && (this->phyLayer != nullptr)) {
|
||||||
|
return(RADIOLIB_ERR_INVALID_CALLSIGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen(callsign) > RADIOLIB_AX25_MAX_CALLSIGN_LEN) {
|
||||||
|
return(RADIOLIB_ERR_INVALID_CALLSIGN);
|
||||||
|
}
|
||||||
|
memcpy(this->src, callsign, strlen(callsign));
|
||||||
|
this->id = ssid;
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +46,7 @@ int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, char* lat
|
||||||
if(time != NULL) {
|
if(time != NULL) {
|
||||||
len += strlen(time);
|
len += strlen(time);
|
||||||
}
|
}
|
||||||
char* info = new char[len];
|
char* info = new char[len + 1];
|
||||||
#else
|
#else
|
||||||
char info[RADIOLIB_STATIC_ARRAY_SIZE];
|
char info[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||||
#endif
|
#endif
|
||||||
|
@ -49,6 +65,7 @@ int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, char* lat
|
||||||
// timestamp and message
|
// timestamp and message
|
||||||
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "%s%s%c%s%c%s", time, lat, table, lon, symbol, msg);
|
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "%s%s%c%s%c%s", time, lat, table, lon, symbol, msg);
|
||||||
}
|
}
|
||||||
|
info[len] = '\0';
|
||||||
|
|
||||||
// send the frame
|
// send the frame
|
||||||
int16_t state = sendFrame(destCallsign, destSSID, info);
|
int16_t state = sendFrame(destCallsign, destSSID, info);
|
||||||
|
@ -211,7 +228,12 @@ int16_t APRSClient::sendMicE(float lat, float lon, uint16_t heading, uint16_t sp
|
||||||
info[infoPos++] = '\0';
|
info[infoPos++] = '\0';
|
||||||
|
|
||||||
// send the frame
|
// send the frame
|
||||||
int16_t state = sendFrame(destCallsign, 0, info);
|
uint8_t destSSID = 0;
|
||||||
|
if(this->phyLayer != nullptr) {
|
||||||
|
// TODO make SSID configurable?
|
||||||
|
destSSID = 1;
|
||||||
|
}
|
||||||
|
int16_t state = sendFrame(destCallsign, destSSID, info);
|
||||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
delete[] info;
|
delete[] info;
|
||||||
#endif
|
#endif
|
||||||
|
@ -219,15 +241,31 @@ int16_t APRSClient::sendMicE(float lat, float lon, uint16_t heading, uint16_t sp
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t APRSClient::sendFrame(char* destCallsign, uint8_t destSSID, char* info) {
|
int16_t APRSClient::sendFrame(char* destCallsign, uint8_t destSSID, char* info) {
|
||||||
// get AX.25 callsign
|
// encoding depends on whether AX.25 should be used or not
|
||||||
char srcCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1];
|
if(this->axClient != nullptr) {
|
||||||
axClient->getCallsign(srcCallsign);
|
// AX.25/classical mode, get AX.25 callsign
|
||||||
|
char srcCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1];
|
||||||
|
axClient->getCallsign(srcCallsign);
|
||||||
|
|
||||||
AX25Frame frameUI(destCallsign, destSSID, srcCallsign, axClient->getSSID(), RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION |
|
AX25Frame frameUI(destCallsign, destSSID, srcCallsign, axClient->getSSID(), RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION |
|
||||||
RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME,
|
RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME,
|
||||||
RADIOLIB_AX25_PID_NO_LAYER_3, (const char*)info);
|
RADIOLIB_AX25_PID_NO_LAYER_3, (const char*)info);
|
||||||
|
|
||||||
return(axClient->sendFrame(&frameUI));
|
return(axClient->sendFrame(&frameUI));
|
||||||
|
|
||||||
|
} else if(this->phyLayer != nullptr) {
|
||||||
|
// non-AX.25/LoRa mode
|
||||||
|
size_t len = RADIOLIB_APRS_LORA_HEADER_LEN + strlen(this->src) + 4 + strlen(destCallsign) + 11 + strlen(info);
|
||||||
|
Serial.println(len);
|
||||||
|
uint8_t* buff = new uint8_t[len];
|
||||||
|
snprintf(buff, len, RADIOLIB_APRS_LORA_HEADER "%s-%d>%s,WIDE%d-%d:%s", this->src, this->id, destCallsign, destSSID, destSSID, info);
|
||||||
|
|
||||||
|
int16_t res = this->phyLayer->transmit(buff, strlen(buff));
|
||||||
|
delete[] buff;
|
||||||
|
return(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -58,6 +58,10 @@
|
||||||
// alias for unused altitude in Mic-E
|
// alias for unused altitude in Mic-E
|
||||||
#define RADIOLIB_APRS_MIC_E_ALTITUDE_UNUSED -1000000
|
#define RADIOLIB_APRS_MIC_E_ALTITUDE_UNUSED -1000000
|
||||||
|
|
||||||
|
// special header applied for APRS over LoRa
|
||||||
|
#define RADIOLIB_APRS_LORA_HEADER "<\xff\x01"
|
||||||
|
#define RADIOLIB_APRS_LORA_HEADER_LEN (3)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class APRSClient
|
\class APRSClient
|
||||||
\brief Client for APRS communication.
|
\brief Client for APRS communication.
|
||||||
|
@ -65,20 +69,28 @@
|
||||||
class APRSClient {
|
class APRSClient {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
\brief Default constructor.
|
\brief Constructor for "classic" mode using AX.25/AFSK.
|
||||||
\param ax Pointer to the instance of AX25Client to be used for APRS.
|
\param ax Pointer to the instance of AX25Client to be used for APRS.
|
||||||
*/
|
*/
|
||||||
explicit APRSClient(AX25Client* ax);
|
explicit APRSClient(AX25Client* ax);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Constructor for LoRa mode.
|
||||||
|
\param phy Pointer to the wireless module providing PhysicalLayer communication.
|
||||||
|
*/
|
||||||
|
explicit APRSClient(PhysicalLayer* phy);
|
||||||
|
|
||||||
// basic methods
|
// basic methods
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Initialization method.
|
\brief Initialization method.
|
||||||
\param sym APRS symbol to be displayed.
|
\param sym APRS symbol to be displayed.
|
||||||
|
\param callsign Source callsign. Required and only used for APRS over LoRa, ignored in classic mode.
|
||||||
|
\param ssid Source SSID. Only used for APRS over LoRa, ignored in classic mode, defaults to 0.
|
||||||
\param alt Whether to use the primary (false) or alternate (true) symbol table. Defaults to primary table.
|
\param alt Whether to use the primary (false) or alternate (true) symbol table. Defaults to primary table.
|
||||||
\returns \ref status_codes
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
int16_t begin(char sym, bool alt = false);
|
int16_t begin(char sym, char* callsign = NULL, uint8_t ssid = 0, bool alt = false);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Transmit position.
|
\brief Transmit position.
|
||||||
|
@ -120,10 +132,15 @@ class APRSClient {
|
||||||
private:
|
private:
|
||||||
#endif
|
#endif
|
||||||
AX25Client* axClient;
|
AX25Client* axClient;
|
||||||
|
PhysicalLayer* phyLayer;
|
||||||
|
|
||||||
// default APRS symbol (car)
|
// default APRS symbol (car)
|
||||||
char symbol = '>';
|
char symbol = '>';
|
||||||
char table = '/';
|
char table = '/';
|
||||||
|
|
||||||
|
// source callsign when using APRS over LoRa
|
||||||
|
char src[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1] = { 0 };
|
||||||
|
uint8_t id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue