[AX25] Added AX.25 support

pull/113/head
jgromes 2020-02-14 08:08:59 +01:00
rodzic 4fa214a0fd
commit ddb478afff
8 zmienionych plików z 966 dodań i 0 usunięć

Wyświetl plik

@ -29,6 +29,7 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github
* __HTTP__ for modules: ESP8266
* __RTTY__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101 and nRF24L01
* __Morse Code__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101 and nRF24L01
* __AX.25__ for modules: SX127x, RFM9x, SX126x, RF69, SX1231 and CC1101
### Supported platforms:
* __AVR__ - tested with hardware on Uno, Mega and Leonardo

Wyświetl plik

@ -0,0 +1,167 @@
/*
RadioLib AX.25 Frame Example
This example shows how to send various
AX.25 frames using SX1278's FSK modem.
Other modules that can be used for AX.25:
- SX127x/RFM9x
- RF69
- SX1231
- CC1101
- SX126x
- nRF24
Using raw AX.25 frames requires some
knowledge of the protocol, refer to
AX25_Transmit for basic operation.
Frames shown in this example are not
exhaustive; all possible AX.25 frames
should be supported.
*/
// include the library
#include <RadioLib.h>
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 fsk = new Module(10, 2, 9, 3);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 fsk = RadioShield.ModuleA;
// create AX.25 client instance using the FSK module
AX25Client ax25(&fsk);
void setup() {
Serial.begin(9600);
// initialize SX1278
Serial.print(F("[SX1278] Initializing ... "));
// carrier frequency: 434.0 MHz
// bit rate: 1.2 kbps (1200 baud AFSK AX.25)
// frequency deviation: 0.5 kHz (1200 baud AFSK AX.25)
int state = fsk.beginFSK(434.0, 1.2, 0.5);
// when using one of the non-LoRa modules for AX.25
// (RF69, CC1101, etc.), use the basic begin() method
// int state = fsk.begin();
if(state == 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 == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
}
void loop() {
// create AX.25 Unnumbered Information frame
// destination station callsign: "NJ7P"
// destination station SSID: 0
// source station callsign: "N7LEM"
// source station SSID: 0
// control field: UI, P/F not used, unnumbered frame
// protocol identifier: no layer 3 protocol implemented
// information field: "Hello World!"
AX25Frame frameUI("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_U_UNNUMBERED_INFORMATION |
AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_UNNUMBERED_FRAME,
AX25_PID_NO_LAYER_3, "Hello World (unnumbered)!");
// send the frame
Serial.print(F("[AX.25] Sending UI frame ... "));
int state = ax25.sendFrame(&frameUI);
if (state == ERR_NONE) {
// the packet was successfully transmitted
Serial.println(F("success!"));
} else {
// some error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
delay(1000);
// create AX.25 Receive Ready frame
// destination station callsign: "NJ7P"
// destination station SSID: 0
// source station callsign: "N7LEM"
// source station SSID: 0
// control field: RR, P/F not used, supervisory frame
AX25Frame frameRR("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_S_RECEIVE_READY |
AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_SUPERVISORY_FRAME);
// set receive sequence number (0 - 7)
frameRR.setRecvSequence(0);
// send the frame
Serial.print(F("[AX.25] Sending RR frame ... "));
state = ax25.sendFrame(&frameRR);
if (state == ERR_NONE) {
// the packet was successfully transmitted
Serial.println(F("success!"));
} else {
// some error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
delay(1000);
// create AX.25 Information frame
// destination station callsign: "NJ7P"
// destination station SSID: 0
// source station callsign: "N7LEM"
// source station SSID: 0
// control field: P/F not used, information frame
// protocol identifier: no layer 3 protocol implemented
// information field: "Hello World (numbered)!"
AX25Frame frameI("NJ7P", 0, "N7LEM", 0, AX25_CONTROL_POLL_FINAL_DISABLED |
AX25_CONTROL_INFORMATION_FRAME, AX25_PID_NO_LAYER_3,
"Hello World (numbered)!");
// set receive sequence number (0 - 7)
frameI.setRecvSequence(0);
// set send sequence number (0 - 7)
frameI.setSendSequence(0);
// send the frame
Serial.print(F("[AX.25] Sending I frame ... "));
state = ax25.sendFrame(&frameI);
if (state == ERR_NONE) {
// the packet was successfully transmitted
Serial.println(F("success!"));
} else {
// some error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
delay(1000);
}

Wyświetl plik

@ -0,0 +1,88 @@
/*
RadioLib AX.25 Transmit Example
This example sends AX.25 messages using
SX1278's FSK modem.
Other modules that can be used for AX.25:
- SX127x/RFM9x
- RF69
- SX1231
- CC1101
- SX126x
- nRF24
*/
// include the library
#include <RadioLib.h>
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 fsk = new Module(10, 2, 9, 3);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 fsk = RadioShield.ModuleA;
// create AX.25 client instance using the FSK module
AX25Client ax25(&fsk);
void setup() {
Serial.begin(9600);
// initialize SX1278
Serial.print(F("[SX1278] Initializing ... "));
// carrier frequency: 434.0 MHz
// bit rate: 1.2 kbps (1200 baud AFSK AX.25)
// frequency deviation: 0.5 kHz (1200 baud AFSK AX.25)
int state = fsk.beginFSK(434.0, 1.2, 0.5);
// when using one of the non-LoRa modules for AX.25
// (RF69, CC1101, etc.), use the basic begin() method
// int state = fsk.begin();
if(state == 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 == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
}
void loop() {
// send AX.25 unnumbered infomration frame
Serial.print(F("[AX.25] Sending UI frame ... "));
// destination station callsign: "NJ7P"
// destination station SSID: 0
int state = ax25.transmit("Hello World!", "NJ7P");
if (state == ERR_NONE) {
// the packet was successfully transmitted
Serial.println(F("success!"));
} else {
// some error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
delay(1000);
}

Wyświetl plik

@ -39,6 +39,8 @@ HTTPClient KEYWORD1
RTTYClient KEYWORD1
MorseClient KEYWORD1
PagerClient KEYWORD1
AX25Client KEYWORD1
AX25Frame KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -176,6 +178,12 @@ getNumBytes KEYWORD2
sendSMS KEYWORD2
shutdown KEYWORD2
# AX.25
setRepeaters KEYWORD2
setRecvSequence KEYWORD2
setSendSequence KEYWORD2
sendFrame KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
@ -262,3 +270,7 @@ ERR_SPI_CMD_INVALID LITERAL1
ERR_SPI_CMD_FAILED LITERAL1
ERR_INVALID_SLEEP_PERIOD LITERAL1
ERR_INVALID_RX_PERIOD LITERAL1
ERR_INVALID_CALLSIGN LITERAL1
ERR_INVALID_NUM_REPEATERS LITERAL1
ERR_INVALID_REPEATER_CALLSIGN LITERAL1

Wyświetl plik

@ -67,6 +67,7 @@
// physical layer protocols
#include "protocols/PhysicalLayer/PhysicalLayer.h"
#include "protocols/AX25/AX25.h"
#include "protocols/Morse/Morse.h"
#include "protocols/RTTY/RTTY.h"

Wyświetl plik

@ -540,6 +540,29 @@
*/
#define ERR_INVALID_RX_PERIOD -709
// AX.25-specific status codes
/*!
\brief The provided callsign is invalid.
The specified callsign is longer than 6 ASCII characters.
*/
#define ERR_INVALID_CALLSIGN -801
/*!
\brief The provided repeater configuration is invalid.
The specified number of repeaters does not match number of repeater IDs or their callsigns.
*/
#define ERR_INVALID_NUM_REPEATERS -802
/*!
\brief One of the provided repeater callsigns is invalid.
The specified callsign is longer than 6 ASCII characters.
*/
#define ERR_INVALID_REPEATER_CALLSIGN -803
/*!
\}
*/

Wyświetl plik

@ -0,0 +1,382 @@
#include "AX25.h"
AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control)
: AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, 0, NULL, 0) {
}
AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info)
: AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, protocolID, (uint8_t*)info, strlen(info)) {
}
AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, uint8_t* info, uint16_t infoLen) {
// destination callsign/SSID
memcpy(this->destCallsign, destCallsign, strlen(destCallsign));
this->destCallsign[strlen(destCallsign)] = '\0';
this->destSSID = destSSID;
// source callsign/SSID
memcpy(this->srcCallsign, srcCallsign, strlen(srcCallsign));
this->srcCallsign[strlen(srcCallsign)] = '\0';
this->srcSSID = srcSSID;
// set repeaters
this->numRepeaters = 0;
#ifndef RADIOLIB_STATIC_ONLY
this->repeaterCallsigns = NULL;
this->repeaterSSIDs = NULL;
#endif
// control field
this->control = control;
// sequence numbers
this->rcvSeqNumber = 0;
this->sendSeqNumber = 0;
// PID field
this->protocolID = protocolID;
// info field
this->infoLen = infoLen;
if(infoLen > 0) {
#ifndef RADIOLIB_STATIC_ONLY
this->info = new uint8_t[infoLen];
#endif
memcpy(this->info, info, infoLen);
}
}
AX25Frame::~AX25Frame() {
#ifndef RADIOLIB_STATIC_ONLY
// deallocate info field
if(infoLen > 0) {
delete[] this->info;
}
// deallocate repeaters
if(this->numRepeaters > 0) {
for(uint8_t i = 0; i < this->numRepeaters; i++) {
delete[] this->repeaterCallsigns[i];
}
delete[] this->repeaterCallsigns;
delete[] this->repeaterSSIDs;
}
#endif
}
int16_t AX25Frame::setRepeaters(char** repeaterCallsigns, uint8_t* repeaterSSIDs, uint8_t numRepeaters) {
// check number of repeaters
if((numRepeaters < 1) || (numRepeaters > 8)) {
return(ERR_INVALID_NUM_REPEATERS);
}
// check repeater configuration
if(!(((repeaterCallsigns == NULL) && (repeaterSSIDs == NULL) && (numRepeaters == 0)) ||
((repeaterCallsigns != NULL) && (repeaterSSIDs != NULL) && (numRepeaters != 0)))) {
return(ERR_INVALID_NUM_REPEATERS);
}
for(uint16_t i = 0; i < numRepeaters; i++) {
if(strlen(repeaterCallsigns[i]) > AX25_MAX_CALLSIGN_LEN) {
return(ERR_INVALID_REPEATER_CALLSIGN);
}
}
// create buffers
#ifndef RADIOLIB_STATIC_ONLY
this->repeaterCallsigns = new char*[numRepeaters];
for(uint8_t i = 0; i < numRepeaters; i++) {
this->repeaterCallsigns[i] = new char[strlen(repeaterCallsigns[i])];
}
this->repeaterSSIDs = new uint8_t[numRepeaters];
#endif
// copy data
this->numRepeaters = numRepeaters;
for(uint8_t i = 0; i < numRepeaters; i++) {
memcpy(this->repeaterCallsigns[i], repeaterCallsigns[i], strlen(repeaterCallsigns[i]));
}
memcpy(this->repeaterSSIDs, repeaterSSIDs, numRepeaters);
return(ERR_NONE);
}
void AX25Frame::setRecvSequence(uint8_t seqNumber) {
this->rcvSeqNumber = seqNumber;
}
void AX25Frame::setSendSequence(uint8_t seqNumber) {
this->sendSeqNumber = seqNumber;
}
AX25Client::AX25Client(PhysicalLayer* phy) {
_phy = phy;
}
int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preambleLen) {
// set source SSID
_srcSSID = srcSSID;
// check source callsign length (6 characters max)
if(strlen(srcCallsign) > AX25_MAX_CALLSIGN_LEN) {
return(ERR_INVALID_CALLSIGN);
}
// copy callsign
memcpy(_srcCallsign, srcCallsign, strlen(srcCallsign));
_srcCallsign[strlen(srcCallsign)] = '\0';
// save preamble length
_preambleLen = preambleLen;
// disable physical layer data shaping and set encoding to NRZ
int16_t state = _phy->setDataShaping(0.0);
RADIOLIB_ASSERT(state);
state = _phy->setEncoding(0);
return(state);
}
int16_t AX25Client::transmit(const char* str, const char* destCallsign, uint8_t destSSID) {
// create control field
uint8_t controlField = AX25_CONTROL_U_UNNUMBERED_INFORMATION | AX25_CONTROL_POLL_FINAL_DISABLED | AX25_CONTROL_UNNUMBERED_FRAME;
// build the frame
AX25Frame frame(destCallsign, destSSID, _srcCallsign, _srcSSID, controlField, AX25_PID_NO_LAYER_3, (uint8_t*)str, strlen(str));
// send Unnumbered Information frame
return(sendFrame(&frame));
}
int16_t AX25Client::sendFrame(AX25Frame* frame) {
// check destination callsign length (6 characters max)
if(strlen(frame->destCallsign) > AX25_MAX_CALLSIGN_LEN) {
return(ERR_INVALID_CALLSIGN);
}
// check repeater configuration
#ifndef RADIOLIB_STATIC_ONLY
if(!(((frame->repeaterCallsigns == NULL) && (frame->repeaterSSIDs == NULL) && (frame->numRepeaters == 0)) ||
((frame->repeaterCallsigns != NULL) && (frame->repeaterSSIDs != NULL) && (frame->numRepeaters != 0)))) {
return(ERR_INVALID_NUM_REPEATERS);
}
for(uint16_t i = 0; i < frame->numRepeaters; i++) {
if(strlen(frame->repeaterCallsigns[i]) > AX25_MAX_CALLSIGN_LEN) {
return(ERR_INVALID_REPEATER_CALLSIGN);
}
}
#endif
// calculate frame length without FCS (destination address, source address, repeater addresses, control, PID, info)
size_t frameBuffLen = ((2 + frame->numRepeaters)*(AX25_MAX_CALLSIGN_LEN + 1)) + 1 + 1 + frame->infoLen;
// create frame buffer without preamble, start or stop flags
#ifndef RADIOLIB_STATIC_ONLY
uint8_t* frameBuff = new uint8_t[frameBuffLen + 2];
#else
uint8_t frameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
#endif
uint8_t* frameBuffPtr = frameBuff;
// set destination callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit
memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN);
for(uint8_t i = 0; i < strlen(frame->destCallsign); i++) {
*(frameBuffPtr + i) = frame->destCallsign[i] << 1;
}
frameBuffPtr += AX25_MAX_CALLSIGN_LEN;
// set destination SSID
*(frameBuffPtr++) = AX25_SSID_COMMAND_DEST | AX25_SSID_RESERVED_BITS | (frame->destSSID & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_CONTINUE;
// set source callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit
memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN);
for(uint8_t i = 0; i < strlen(frame->srcCallsign); i++) {
*(frameBuffPtr + i) = frame->srcCallsign[i] << 1;
}
frameBuffPtr += AX25_MAX_CALLSIGN_LEN;
// set source SSID
*(frameBuffPtr++) = AX25_SSID_COMMAND_SOURCE | AX25_SSID_RESERVED_BITS | (frame->srcSSID & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_END;
// set repeater callsigns
for(uint16_t i = 0; i < frame->numRepeaters; i++) {
memset(frameBuffPtr, ' ' << 1, AX25_MAX_CALLSIGN_LEN);
for(uint8_t j = 0; j < strlen(frame->repeaterCallsigns[i]); j++) {
*(frameBuffPtr + j) = frame->repeaterCallsigns[i][j] << 1;
}
frameBuffPtr += AX25_MAX_CALLSIGN_LEN;
*(frameBuffPtr++) = AX25_SSID_HAS_NOT_BEEN_REPEATED | AX25_SSID_RESERVED_BITS | (frame->repeaterSSIDs[i] & 0x0F) << 1 | AX25_SSID_HDLC_EXTENSION_CONTINUE;
}
// set HDLC extension end bit
*frameBuffPtr |= AX25_SSID_HDLC_EXTENSION_END;
// set sequence numbers of the frames that have it
uint8_t controlField = frame->control;
if((frame->control & 0x01) == 0) {
// information frame, set both sequence numbers
controlField |= frame->rcvSeqNumber << 5;
controlField |= frame->sendSeqNumber << 1;
} else if((frame->control & 0x02) == 0) {
// supervisory frame, set only receive sequence number
controlField |= frame->rcvSeqNumber << 5;
}
// set control field
*(frameBuffPtr++) = controlField;
// set PID field of the frames that have it
if(frame->protocolID != 0x00) {
*(frameBuffPtr++) = frame->protocolID;
}
// set info field of the frames that have it
if(frame->infoLen > 0) {
memcpy(frameBuffPtr, frame->info, frame->infoLen);
frameBuffPtr += frame->infoLen;
}
// flip bit order
for(size_t i = 0; i < frameBuffLen; i++) {
frameBuff[i] = flipBits(frameBuff[i]);
}
// calculate FCS
uint16_t fcs = getFrameCheckSequence(frameBuff, frameBuffLen);
*(frameBuffPtr++) = (uint8_t)((fcs >> 8) & 0xFF);
*(frameBuffPtr++) = (uint8_t)(fcs & 0xFF);
// prepare buffer for the final frame (stuffed, with added preamble + flags and NRZI-encoded)
#ifndef RADIOLIB_STATIC_ONLY
// worst-case scenario: sequence of 1s, will have 120% of the original length, stuffed frame also includes both flags
uint8_t* stuffedFrameBuff = new uint8_t[_preambleLen + 1 + (6*frameBuffLen)/5 + 2];
#else
uint8_t stuffedFrameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
#endif
// stuff bits (skip preamble and both flags)
uint16_t stuffedFrameBuffLenBits = 8*(_preambleLen + 1);
uint8_t count = 0;
for(uint16_t i = 0; i < frameBuffLen + 2; i++) {
for(int8_t shift = 7; shift >= 0; shift--) {
uint16_t stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8);
if((frameBuff[i] >> shift) & 0x01) {
// copy 1 and increment counter
SET_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
stuffedFrameBuffLenBits++;
count++;
// check 5 consecutive 1s
if(count == 5) {
// get the new position in stuffed frame
stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8);
// insert 0 and reset counter
CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
stuffedFrameBuffLenBits++;
count = 0;
}
} else {
// copy 0 and reset counter
CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
stuffedFrameBuffLenBits++;
count = 0;
}
}
}
// deallocate memory
#ifndef RADIOLIB_STATIC_ONLY
delete[] frameBuff;
#endif
// set preamble bytes and start flag field
for(uint16_t i = 0; i < _preambleLen + 1; i++) {
stuffedFrameBuff[i] = AX25_FLAG;
}
// get stuffed frame length in bytes
size_t stuffedFrameBuffLen = stuffedFrameBuffLenBits/8 + 1;
uint8_t trailingLen = stuffedFrameBuffLenBits % 8;
// set end flag field (may be split into two bytes due to misalignment caused by extra stuffing bits)
if(trailingLen != 0) {
stuffedFrameBuffLen++;
stuffedFrameBuff[stuffedFrameBuffLen - 2] = AX25_FLAG >> trailingLen;
stuffedFrameBuff[stuffedFrameBuffLen - 1] = AX25_FLAG << (8 - trailingLen);
} else {
stuffedFrameBuff[stuffedFrameBuffLen - 1] = AX25_FLAG;
}
// convert to NRZI
for(size_t i = _preambleLen + 1; i < stuffedFrameBuffLen*8; i++) {
size_t currBitPos = i + 7 - 2*(i%8);
size_t prevBitPos = (i - 1) + 7 - 2*((i - 1)%8);
if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos)) {
// bit is 1, no change, copy previous bit
if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) {
SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
} else {
CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
}
} else {
// bit is 0, transition, copy inversion of the previous bit
if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) {
CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
} else {
SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
}
}
}
// transmit
int16_t state = _phy->transmit(stuffedFrameBuff, stuffedFrameBuffLen);
// deallocate memory
#ifndef RADIOLIB_STATIC_ONLY
delete[] stuffedFrameBuff;
#endif
return(state);
}
/*
CCITT CRC implementation based on https://github.com/kicksat/ax25
Licensed under Creative Commons Attribution-ShareAlike 4.0 International
https://creativecommons.org/licenses/by-sa/4.0/
*/
uint16_t AX25Client::getFrameCheckSequence(uint8_t* buff, size_t len) {
uint8_t outBit = 0;
uint16_t mask = 0x0000;
uint16_t shiftReg = CRC_CCITT_INIT;
for(size_t i = 0; i < len; i++) {
for(uint8_t b = 0x80; b > 0x00; b /= 2) {
outBit = (shiftReg & 0x01) ? 0x01 : 0x00;
shiftReg >>= 1;
mask = XOR((buff[i] & b), outBit) ? CRC_CCITT_POLY_REVERSED : 0x0000;
shiftReg ^= mask;
}
}
return(flipBits16(~shiftReg));
}
uint8_t AX25Client::flipBits(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
uint16_t AX25Client::flipBits16(uint16_t i) {
i = (i & 0xFF00) >> 8 | (i & 0x00FF) << 8;
i = (i & 0xF0F0) >> 4 | (i & 0x0F0F) << 4;
i = (i & 0xCCCC) >> 2 | (i & 0x3333) << 2;
i = (i & 0xAAAA) >> 1 | (i & 0x5555) << 1;
return i;
}

Wyświetl plik

@ -0,0 +1,292 @@
#ifndef _RADIOLIB_AX25_H
#define _RADIOLIB_AX25_H
#include "../../TypeDef.h"
#include "../PhysicalLayer/PhysicalLayer.h"
// macros to access bits in byte array, from http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html
#define SET_BIT_IN_ARRAY(A, k) ( A[(k/8)] |= (1 << (k%8)) )
#define CLEAR_BIT_IN_ARRAY(A, k) ( A[(k/8)] &= ~(1 << (k%8)) )
#define TEST_BIT_IN_ARRAY(A, k) ( A[(k/8)] & (1 << (k%8)) )
#define GET_BIT_IN_ARRAY(A, k) ( (A[(k/8)] & (1 << (k%8))) ? 1 : 0 )
// CRC-CCITT calculation macros
#define XOR(A, B) ( ((A) || (B)) && !((A) && (B)) )
#define CRC_CCITT_POLY 0x1021 // generator polynomial
#define CRC_CCITT_POLY_REVERSED 0x8408 // CRC_CCITT_POLY in reversed bit order
#define CRC_CCITT_INIT 0xFFFF // initial value
// maximum callsign length in bytes
#define AX25_MAX_CALLSIGN_LEN 6
// flag field MSB LSB DESCRIPTION
#define AX25_FLAG 0b01111110 // 7 0 AX.25 frame start/end flag
// address field
#define AX25_SSID_COMMAND_DEST 0b10000000 // 7 7 frame type: command (set in destination SSID)
#define AX25_SSID_COMMAND_SOURCE 0b00000000 // 7 7 command (set in source SSID)
#define AX25_SSID_RESPONSE_DEST 0b00000000 // 7 7 response (set in destination SSID)
#define AX25_SSID_RESPONSE_SOURCE 0b10000000 // 7 7 response (set in source SSID)
#define AX25_SSID_HAS_NOT_BEEN_REPEATED 0b00000000 // 7 7 not repeated yet (set in repeater SSID)
#define AX25_SSID_HAS_BEEN_REPEATED 0b10000000 // 7 7 repeated (set in repeater SSID)
#define AX25_SSID_RESERVED_BITS 0b01100000 // 6 5 reserved bits in SSID
#define AX25_SSID_HDLC_EXTENSION_CONTINUE 0b00000000 // 0 0 HDLC extension bit: next octet contains more address information
#define AX25_SSID_HDLC_EXTENSION_END 0b00000001 // 0 0 address field end
// control field
#define AX25_CONTROL_U_SET_ASYNC_BAL_MODE 0b01101100 // 7 2 U frame type: set asynchronous balanced mode (connect request)
#define AX25_CONTROL_U_SET_ASYNC_BAL_MODE_EXT 0b00101100 // 7 2 set asynchronous balanced mode extended (connect request with module 128)
#define AX25_CONTROL_U_DISCONNECT 0b01000000 // 7 2 disconnect request
#define AX25_CONTROL_U_DISCONNECT_MODE 0b00001100 // 7 2 disconnect mode (system busy or disconnected)
#define AX25_CONTROL_U_UNNUMBERED_ACK 0b01100000 // 7 2 unnumbered acknowledge
#define AX25_CONTROL_U_FRAME_REJECT 0b10000100 // 7 2 frame reject
#define AX25_CONTROL_U_UNNUMBERED_INFORMATION 0b00000000 // 7 2 unnumbered information
#define AX25_CONTROL_U_EXHANGE_IDENTIFICATION 0b10101100 // 7 2 exchange ID
#define AX25_CONTROL_U_TEST 0b11100000 // 7 2 test
#define AX25_CONTROL_POLL_FINAL_ENABLED 0b00010000 // 4 4 control field poll/final bit: enabled
#define AX25_CONTROL_POLL_FINAL_DISABLED 0b00000000 // 4 4 disabled
#define AX25_CONTROL_S_RECEIVE_READY 0b00000000 // 3 2 S frame type: receive ready (system ready to receive)
#define AX25_CONTROL_S_RECEIVE_NOT_READY 0b00000100 // 3 2 receive not ready (TNC buffer full)
#define AX25_CONTROL_S_REJECT 0b00001000 // 3 2 reject (out of sequence or duplicate)
#define AX25_CONTROL_S_SELECTIVE_REJECT 0b00001100 // 3 2 selective reject (single frame repeat request)
#define AX25_CONTROL_INFORMATION_FRAME 0b00000000 // 0 0 frame type: information (I frame)
#define AX25_CONTROL_SUPERVISORY_FRAME 0b00000001 // 1 0 supervisory (S frame)
#define AX25_CONTROL_UNNUMBERED_FRAME 0b00000011 // 1 0 unnumbered (U frame)
// protocol identifier field
#define AX25_PID_ISO_8208 0x01
#define AX25_PID_TCP_IP_COMPRESSED 0x06
#define AX25_PID_TCP_IP_UNCOMPRESSED 0x07
#define AX25_PID_SEGMENTATION_FRAGMENT 0x08
#define AX25_PID_TEXNET_DATAGRAM_PROTOCOL 0xC3
#define AX25_PID_LINK_QUALITY_PROTOCOL 0xC4
#define AX25_PID_APPLETALK 0xCA
#define AX25_PID_APPLETALK_ARP 0xCB
#define AX25_PID_ARPA_INTERNET_PROTOCOL 0xCC
#define AX25_PID_ARPA_ADDRESS_RESOLUTION 0xCD
#define AX25_PID_FLEXNET 0xCE
#define AX25_PID_NET_ROM 0xCF
#define AX25_PID_NO_LAYER_3 0xF0
#define AX25_PID_ESCAPE_CHARACTER 0xFF
/*!
\class AX25Frame
\brief Abstraction of AX.25 frame format.
*/
class AX25Frame {
public:
/*!
\brief Callsign of the destination station.
*/
char destCallsign[AX25_MAX_CALLSIGN_LEN + 1];
/*!
\brief SSID of the destination station.
*/
uint8_t destSSID;
/*!
\brief Callsign of the source station.
*/
char srcCallsign[AX25_MAX_CALLSIGN_LEN + 1];
/*!
\brief SSID of the source station.
*/
uint8_t srcSSID;
/*!
\brief Number of repeaters to be used.
*/
uint8_t numRepeaters;
/*!
\brief The control field.
*/
uint8_t control;
/*!
\brief The protocol identifier (PID) field.
*/
uint8_t protocolID;
/*!
\brief Number of bytes in the information field.
*/
uint16_t infoLen;
/*!
\brief Receive sequence number.
*/
uint8_t rcvSeqNumber;
/*!
\brief Send sequence number.
*/
uint16_t sendSeqNumber;
#ifndef RADIOLIB_STATIC_ONLY
uint8_t* info;
char** repeaterCallsigns;
uint8_t* repeaterSSIDs;
#else
uint8_t info[RADIOLIB_STATIC_ARRAY_SIZE];
char repeaterCallsigns[8][AX25_MAX_CALLSIGN_LEN + 1];
uint8_t repeaterSSIDs[8];
#endif
/*!
\brief Overloaded constructor, for frames without info field.
\param destCallsign Callsign of the destination station.
\param destSSID SSID of the destination station.
\param srcCallsign Callsign of the source station.
\param srcSSID SSID of the source station.
\param control The control field.
*/
AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control);
/*!
\brief Overloaded constructor, for frames with C-string info field.
\param destCallsign Callsign of the destination station.
\param destSSID SSID of the destination station.
\param srcCallsign Callsign of the source station.
\param srcSSID SSID of the source station.
\param control The control field.
\param protocolID The protocol identifier (PID) field. Set to zero if the frame doesn't have this field.
\param info Information field, in the form of null-terminated C-string.
*/
AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info);
/*!
\brief Default constructor.
\param destCallsign Callsign of the destination station.
\param destSSID SSID of the destination station.
\param srcCallsign Callsign of the source station.
\param srcSSID SSID of the source station.
\param control The control field.
\param protocolID The protocol identifier (PID) field. Set to zero if the frame doesn't have this field.
\param info Information field, in the form of arbitrary binary buffer.
\param infoLen Number of bytes in the information field.
*/
AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, uint8_t* info, uint16_t infoLen);
/*!
\brief Default destructor.
*/
~AX25Frame();
/*!
\brief Method to set the repeater callsigns and SSIDs.
\param repeaterCallsigns Array of repeater callsigns in the form of null-terminated C-strings.
\param repeaterSSIDs Array of repeater SSIDs.
\param numRepeaters Number of repeaters, maximum is 8.
\returns \ref status_codes
*/
int16_t setRepeaters(char** repeaterCallsigns, uint8_t* repeaterSSIDs, uint8_t numRepeaters);
/*!
\brief Method to set receive sequence number.
\param seqNumber Sequence number to set, 0 to 7.
*/
void setRecvSequence(uint8_t seqNumber);
/*!
\brief Method to set send sequence number.
\param seqNumber Sequence number to set, 0 to 7.
*/
void setSendSequence(uint8_t seqNumber);
};
/*!
\class AX25Client
\brief Client for AX25 communication.
*/
class AX25Client {
public:
/*!
\brief Default constructor.
\param phy Pointer to the wireless module providing PhysicalLayer communication.
*/
AX25Client(PhysicalLayer* phy);
// basic methods
/*!
\brief Initialization method.
\param srcCallsign Callsign of the source station.
\param srcSSID 4-bit SSID of the source station (in case there are more stations with the same callsign). Defaults to 0.
\param preambleLen Number of "preamble" bytes (AX25_FLAG) sent ahead of the actual AX.25 frame. Does not include the first AX25_FLAG byte, which is considered part of the frame. Defaults to 8.
\returns \ref status_codes
*/
int16_t begin(const char* srcCallsign, uint8_t srcSSID = 0x00, uint8_t preambleLen = 8);
/*!
\brief Transmit unnumbered information (UI) frame.
\param str Data to be sent.
\param destCallsign Callsign of the destination station.
\param destSSID 4-bit SSID of the destination station (in case there are more stations with the same callsign). Defaults to 0.
\returns \ref status_codes
*/
int16_t transmit(const char* str, const char* destCallsign, uint8_t destSSID = 0x00);
/*!
\brief Transmit arbitrary AX.25 frame.
\param frame Frame to be sent.
\returns \ref status_codes
*/
int16_t sendFrame(AX25Frame* frame);
#ifndef RADIOLIB_GODMODE
private:
#endif
PhysicalLayer* _phy;
char _srcCallsign[AX25_MAX_CALLSIGN_LEN + 1];
uint8_t _srcSSID;
uint16_t _preambleLen;
uint16_t getFrameCheckSequence(uint8_t* buff, size_t len);
uint8_t flipBits(uint8_t b);
uint16_t flipBits16(uint16_t i);
};
#endif