[APRS] Added APRS support

pull/427/head
jgromes 2021-11-28 22:56:36 +01:00
rodzic 3eb831d7e1
commit df466486aa
7 zmienionych plików z 306 dodań i 1 usunięć

Wyświetl plik

@ -35,6 +35,8 @@ SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x
* [__Hellschreiber__](https://www.sigidwiki.com/wiki/Hellschreiber) using 2-FSK or AFSK for modules:
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
* [__APRS__](https://www.sigidwiki.com/wiki/APRS) using AFSK for modules:
SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x
### Supported Arduino platforms:
* __Arduino__
@ -76,7 +78,6 @@ The list above is by no means exhaustive - RadioLib code is independent of the u
### In development:
* __AX5243__ FSK module
* __LoRaWAN__ protocol for SX127x, RFM9x and SX126x modules
* __APRS__ protocol for all the modules that can transmit AX.25
* ___and more!___
## Frequently Asked Questions

Wyświetl plik

@ -0,0 +1,123 @@
/*
RadioLib APRS Position Example
This example sends APRS position reports
using SX1278's FSK modem. The data is
modulated as AFSK at 1200 baud using Bell
202 tones.
DO NOT transmit in APRS bands unless
you have a ham radio license!
Other modules that can be used for APRS:
- SX127x/RFM9x
- RF69
- SX1231
- CC1101
- nRF24
- Si443x/RFM2x
For default module settings, see the wiki page
https://github.com/jgromes/RadioLib/wiki/Default-configuration
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 radio = new Module(10, 2, 9, 3);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 radio = RadioShield.ModuleA;
// create AFSK client instance using the FSK module
// pin 5 is connected to SX1278 DIO2
AFSKClient audio(&radio, 5);
// create AX.25 client instance using the AFSK instance
AX25Client ax25(&audio);
// create APRS client isntance using the AX.25 client
APRSClient aprs(&ax25);
void setup() {
Serial.begin(9600);
// initialize SX1278
// NOTE: moved to ISM band on purpose
// DO NOT transmit in APRS bands without ham radio license!
Serial.print(F("[SX1278] Initializing ... "));
int state = radio.beginFSK(434.0);
// when using one of the non-LoRa modules for AX.25
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
// int state = radio.begin();
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
// initialize AX.25 client
Serial.print(F("[AX.25] Initializing ... "));
// source station callsign: "N7LEM"
// source station SSID: 0
// preamble length: 8 bytes
state = ax25.begin("N7LEM");
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
// initialize APRS client
Serial.print(F("[APRS] Initializing ... "));
// symbol: '>' (car)
state = aprs.begin('>');
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
}
void loop() {
Serial.print(F("[APRS] Sending position ... "));
// send a location without message or timestamp
int state = aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E");
delay(500);
// send a location with message and without timestamp
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!");
delay(500);
// send a location with message and timestamp
state |= aprs.sendPosition("N0CALL", 0, "4911.67N", "01635.96E", "I'm here!", "093045z");
delay(500);
if(state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
}
// wait one minute before transmitting again
delay(60000);
}

Wyświetl plik

@ -48,6 +48,7 @@ SSTVClient KEYWORD1
HellClient KEYWORD1
AFSKClient KEYWORD1
FSK4Client KEYWORD1
APRSClient KEYWORD1
# SSTV modes
Scottie1 KEYWORD1
@ -217,6 +218,9 @@ printGlyph KEYWORD2
tone KEYWORD2
noTone KEYWORD2
# APRS
sendPosition KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

Wyświetl plik

@ -21,6 +21,7 @@
- SSTV (SSTVClient)
- Hellschreiber (HellClient)
- 4-FSK (FSK4Client)
- APRS (APRSClient)
\par Quick Links
Documentation for most common methods can be found in its reference page (see the list above).\n
@ -89,6 +90,7 @@
#include "protocols/RTTY/RTTY.h"
#include "protocols/SSTV/SSTV.h"
#include "protocols/FSK4/FSK4.h"
#include "protocols/APRS/APRS.h"
// only create Radio class when using RadioShield
#if defined(RADIOLIB_RADIOSHIELD)

Wyświetl plik

@ -245,6 +245,13 @@
*/
#define RADIOLIB_ERR_INVALID_OOK_RSSI_PEAK_TYPE (-108)
// APRS status codes
/*!
\brief Supplied APRS symbol is invalid.
*/
#define RADIOLIB_ERR_INVALID_SYMBOL (-201)
// RTTY status codes
/*!

Wyświetl plik

@ -0,0 +1,67 @@
#include "APRS.h"
APRSClient::APRSClient(AX25Client* ax) {
_ax = ax;
}
int16_t APRSClient::begin(char symbol, bool alt) {
RADIOLIB_CHECK_RANGE(symbol, ' ', '}', RADIOLIB_ERR_INVALID_SYMBOL);
_symbol = symbol;
if(alt) {
_table = '\\';
} else {
_table = '/';
}
return(RADIOLIB_ERR_NONE);
}
int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, char* lat, char* lon, char* msg, char* time) {
#if !defined(RADIOLIB_STATIC_ONLY)
size_t len = 1 + strlen(lat) + 1 + strlen(lon);
if(msg != NULL) {
len += 1 + strlen(msg);
}
if(time != NULL) {
len += strlen(time);
}
char* info = new char[len];
#else
char info[RADIOLIB_STATIC_ARRAY_SIZE];
#endif
// build the info field
if((msg == NULL) && (time == NULL)) {
// no message, no timestamp
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_NO_MSG "%s%c%s%c", lat, _table, lon, _symbol);
} else if((msg != NULL) && (time == NULL)) {
// message, no timestamp
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_MSG "%s%c%s%c%s", lat, _table, lon, _symbol, msg);
} else if((msg == NULL) && (time != NULL)) {
// timestamp, no message
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_NO_MSG "%s%s%c%s%c", time, lat, _table, lon, _symbol);
} else {
// timestamp and message
sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "%s%s%c%s%c%s", time, lat, _table, lon, _symbol, msg);
}
// send the frame
int16_t state = sendFrame(destCallsign, destSSID, info);
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] info;
#endif
return(state);
}
int16_t APRSClient::sendFrame(char* destCallsign, uint8_t destSSID, char* info) {
// get AX.25 callsign
char srcCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1];
_ax->getCallsign(srcCallsign);
AX25Frame frameUI(destCallsign, destSSID, srcCallsign, _ax->getSSID(), RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION |
RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME,
RADIOLIB_AX25_PID_NO_LAYER_3, (const char*)info);
return(_ax->sendFrame(&frameUI));
}

Wyświetl plik

@ -0,0 +1,101 @@
#if !defined(_RADIOLIB_RADIOLIB_APRS_H)
#define _RADIOLIB_RADIOLIB_APRS_H
#include "../../TypeDef.h"
#if !defined(RADIOLIB_EXCLUDE_APRS)
#include "../PhysicalLayer/PhysicalLayer.h"
#include "../AX25/AX25.h"
// APRS data type identifiers
#define RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_NO_MSG "!"
#define RADIOLIB_APRS_DATA_TYPE_GPS_RAW "$"
#define RADIOLIB_APRS_DATA_TYPE_ITEM ")"
#define RADIOLIB_APRS_DATA_TYPE_TEST ","
#define RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_NO_MSG "/"
#define RADIOLIB_APRS_DATA_TYPE_MSG ":"
#define RADIOLIB_APRS_DATA_TYPE_OBJECT ";"
#define RADIOLIB_APRS_DATA_TYPE_STATION_CAPABILITES "<"
#define RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_MSG "="
#define RADIOLIB_APRS_DATA_TYPE_STATUS ">"
#define RADIOLIB_APRS_DATA_TYPE_QUERY "?"
#define RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "@"
#define RADIOLIB_APRS_DATA_TYPE_TELEMETRY "T"
#define RADIOLIB_APRS_DATA_TYPE_MAIDENHEAD_BEACON "["
#define RADIOLIB_APRS_DATA_TYPE_WEATHER_REPORT "_"
#define RADIOLIB_APRS_DATA_TYPE_USER_DEFINED "{"
#define RADIOLIB_APRS_DATA_TYPE_THIRD_PARTY "}"
/*!
\class APRSClient
\brief Client for APRS communication.
*/
class APRSClient {
public:
/*!
\brief Default constructor.
\param ax Pointer to the instance of AX25Client to be used for APRS.
*/
explicit APRSClient(AX25Client* ax);
// basic methods
/*!
\brief Initialization method.
\param symbol APRS symbol to be displayed.
\param alt Whether to use the primary (false) or alternate (true) symbol table. Defaults to primary table.
\returns \ref status_codes
*/
int16_t begin(char symbol, bool alt = false);
/*!
\brief Transmit position.
\param destCallsign Destination station callsign.
\param destSSID Destination station SSID.
\param lat Latitude as a null-terminated string.
\param long Longitude as a null-terminated string.
\param msg Message to be transmitted. Defaults to NULL (no message).
\param msg Position timestamp. Defaults to NULL (no timestamp).
\returns \ref status_codes
*/
int16_t sendPosition(char* destCallsign, uint8_t destSSID, char* lat, char* lon, char* msg = NULL, char* time = NULL);
/*!
\brief Transmit generic APRS frame.
\param destCallsign Destination station callsign.
\param destSSID Destination station SSID.
\param info AX.25 info field contents.
\returns \ref status_codes
*/
int16_t sendFrame(char* destCallsign, uint8_t destSSID, char* info);
#if !defined(RADIOLIB_GODMODE)
private:
#endif
AX25Client* _ax;
// default APRS symbol (car)
char _symbol = '>';
char _table = '/';
};
#endif
#endif