acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

AX25.cpp (15265B)

      1 #include "AX25.h"
      2 #if !defined(RADIOLIB_EXCLUDE_AX25)
      3 
      4 AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control)
      5 : AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, 0, NULL, 0) {
      6 
      7 }
      8 
      9 AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info)
     10   : AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, protocolID, (uint8_t*)info, strlen(info)) {
     11 
     12 }
     13 
     14 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) {
     15   // destination callsign/SSID
     16   memcpy(this->destCallsign, destCallsign, strlen(destCallsign));
     17   this->destCallsign[strlen(destCallsign)] = '\0';
     18   this->destSSID = destSSID;
     19 
     20   // source callsign/SSID
     21   memcpy(this->srcCallsign, srcCallsign, strlen(srcCallsign));
     22   this->srcCallsign[strlen(srcCallsign)] = '\0';
     23   this->srcSSID = srcSSID;
     24 
     25   // set repeaters
     26   this->numRepeaters = 0;
     27   #if !defined(RADIOLIB_STATIC_ONLY)
     28     this->repeaterCallsigns = NULL;
     29     this->repeaterSSIDs = NULL;
     30   #endif
     31 
     32   // control field
     33   this->control = control;
     34 
     35   // sequence numbers
     36   this->rcvSeqNumber = 0;
     37   this->sendSeqNumber = 0;
     38 
     39   // PID field
     40   this->protocolID = protocolID;
     41 
     42   // info field
     43   this->infoLen = infoLen;
     44   if(infoLen > 0) {
     45     #if !defined(RADIOLIB_STATIC_ONLY)
     46       this->info = new uint8_t[infoLen];
     47     #endif
     48     memcpy(this->info, info, infoLen);
     49   }
     50 }
     51 
     52 AX25Frame::AX25Frame(const AX25Frame& frame) {
     53   *this = frame;
     54 }
     55 
     56 AX25Frame::~AX25Frame() {
     57   #if !defined(RADIOLIB_STATIC_ONLY)
     58     // deallocate info field
     59     if(infoLen > 0) {
     60       delete[] this->info;
     61     }
     62 
     63     // deallocate repeaters
     64     if(this->numRepeaters > 0) {
     65       for(uint8_t i = 0; i < this->numRepeaters; i++) {
     66         delete[] this->repeaterCallsigns[i];
     67       }
     68       delete[] this->repeaterCallsigns;
     69       delete[] this->repeaterSSIDs;
     70     }
     71   #endif
     72 }
     73 
     74 AX25Frame& AX25Frame::operator=(const AX25Frame& frame) {
     75   // destination callsign/SSID
     76   memcpy(this->destCallsign, frame.destCallsign, strlen(frame.destCallsign));
     77   this->destCallsign[strlen(frame.destCallsign)] = '\0';
     78   this->destSSID = frame.destSSID;
     79 
     80   // source callsign/SSID
     81   memcpy(this->srcCallsign, frame.srcCallsign, strlen(frame.srcCallsign));
     82   this->srcCallsign[strlen(frame.srcCallsign)] = '\0';
     83   this->srcSSID = frame.srcSSID;
     84 
     85   // repeaters
     86   this->numRepeaters = frame.numRepeaters;
     87   for(uint8_t i = 0; i < this->numRepeaters; i++) {
     88     memcpy(this->repeaterCallsigns[i], frame.repeaterCallsigns[i], strlen(frame.repeaterCallsigns[i]));
     89   }
     90   memcpy(this->repeaterSSIDs, frame.repeaterSSIDs, this->numRepeaters);
     91 
     92   // control field
     93   this->control = frame.control;
     94 
     95   // sequence numbers
     96   this->rcvSeqNumber = frame.rcvSeqNumber;
     97   this->sendSeqNumber = frame.sendSeqNumber;
     98 
     99   // PID field
    100   this->protocolID = frame.protocolID;
    101 
    102   // info field
    103   this->infoLen = frame.infoLen;
    104   memcpy(this->info, frame.info, this->infoLen);
    105 
    106   return(*this);
    107 }
    108 
    109 int16_t AX25Frame::setRepeaters(char** repeaterCallsigns, uint8_t* repeaterSSIDs, uint8_t numRepeaters) {
    110   // check number of repeaters
    111   if((numRepeaters < 1) || (numRepeaters > 8)) {
    112     return(RADIOLIB_ERR_INVALID_NUM_REPEATERS);
    113   }
    114 
    115   // check repeater configuration
    116   if((repeaterCallsigns == NULL) || (repeaterSSIDs == NULL)) {
    117     return(RADIOLIB_ERR_INVALID_NUM_REPEATERS);
    118   }
    119   for(uint16_t i = 0; i < numRepeaters; i++) {
    120     if(strlen(repeaterCallsigns[i]) > RADIOLIB_AX25_MAX_CALLSIGN_LEN) {
    121       return(RADIOLIB_ERR_INVALID_REPEATER_CALLSIGN);
    122     }
    123   }
    124 
    125   // create buffers
    126   #if !defined(RADIOLIB_STATIC_ONLY)
    127     this->repeaterCallsigns = new char*[numRepeaters];
    128     for(uint8_t i = 0; i < numRepeaters; i++) {
    129       this->repeaterCallsigns[i] = new char[strlen(repeaterCallsigns[i]) + 1];
    130     }
    131     this->repeaterSSIDs = new uint8_t[numRepeaters];
    132   #endif
    133 
    134   // copy data
    135   this->numRepeaters = numRepeaters;
    136   for(uint8_t i = 0; i < numRepeaters; i++) {
    137     memcpy(this->repeaterCallsigns[i], repeaterCallsigns[i], strlen(repeaterCallsigns[i]));
    138     this->repeaterCallsigns[i][strlen(repeaterCallsigns[i])] = '\0';
    139   }
    140   memcpy(this->repeaterSSIDs, repeaterSSIDs, numRepeaters);
    141 
    142   return(RADIOLIB_ERR_NONE);
    143 }
    144 
    145 void AX25Frame::setRecvSequence(uint8_t seqNumber) {
    146   this->rcvSeqNumber = seqNumber;
    147 }
    148 
    149 void AX25Frame::setSendSequence(uint8_t seqNumber) {
    150   this->sendSeqNumber = seqNumber;
    151 }
    152 
    153 AX25Client::AX25Client(PhysicalLayer* phy) {
    154   _phy = phy;
    155   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    156   _audio = nullptr;
    157   #endif
    158 }
    159 
    160 #if !defined(RADIOLIB_EXCLUDE_AFSK)
    161 AX25Client::AX25Client(AFSKClient* audio) {
    162   _phy = audio->_phy;
    163   _audio = audio;
    164   _afskMark = RADIOLIB_AX25_AFSK_MARK;
    165   _afskSpace = RADIOLIB_AX25_AFSK_SPACE;
    166 }
    167 
    168 int16_t AX25Client::setCorrection(int16_t mark, int16_t space) {
    169   _afskMark = RADIOLIB_AX25_AFSK_MARK + mark;
    170   _afskSpace = RADIOLIB_AX25_AFSK_SPACE + space;
    171   return(RADIOLIB_ERR_NONE);
    172 }
    173 #endif
    174 
    175 int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preambleLen) {
    176   // set source SSID
    177   _srcSSID = srcSSID;
    178 
    179   // check source callsign length (6 characters max)
    180   if(strlen(srcCallsign) > RADIOLIB_AX25_MAX_CALLSIGN_LEN) {
    181     return(RADIOLIB_ERR_INVALID_CALLSIGN);
    182   }
    183 
    184   // copy callsign
    185   memcpy(_srcCallsign, srcCallsign, strlen(srcCallsign));
    186   _srcCallsign[strlen(srcCallsign)] = '\0';
    187 
    188   // save preamble length
    189   _preambleLen = preambleLen;
    190 
    191   // configure for direct mode
    192   return(_phy->startDirect());
    193 }
    194 
    195 int16_t AX25Client::transmit(const char* str, const char* destCallsign, uint8_t destSSID) {
    196   // create control field
    197   uint8_t controlField = RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION | RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME;
    198 
    199   // build the frame
    200   AX25Frame frame(destCallsign, destSSID, _srcCallsign, _srcSSID, controlField, RADIOLIB_AX25_PID_NO_LAYER_3, (uint8_t*)str, strlen(str));
    201 
    202   // send Unnumbered Information frame
    203   return(sendFrame(&frame));
    204 }
    205 
    206 int16_t AX25Client::sendFrame(AX25Frame* frame) {
    207   // check destination callsign length (6 characters max)
    208   if(strlen(frame->destCallsign) > RADIOLIB_AX25_MAX_CALLSIGN_LEN) {
    209     return(RADIOLIB_ERR_INVALID_CALLSIGN);
    210   }
    211 
    212   // check repeater configuration
    213   #if !defined(RADIOLIB_STATIC_ONLY)
    214     if(!(((frame->repeaterCallsigns == NULL) && (frame->repeaterSSIDs == NULL) && (frame->numRepeaters == 0)) ||
    215          ((frame->repeaterCallsigns != NULL) && (frame->repeaterSSIDs != NULL) && (frame->numRepeaters != 0)))) {
    216       return(RADIOLIB_ERR_INVALID_NUM_REPEATERS);
    217     }
    218     for(uint16_t i = 0; i < frame->numRepeaters; i++) {
    219       if(strlen(frame->repeaterCallsigns[i]) > RADIOLIB_AX25_MAX_CALLSIGN_LEN) {
    220         return(RADIOLIB_ERR_INVALID_REPEATER_CALLSIGN);
    221       }
    222     }
    223   #endif
    224 
    225   // calculate frame length without FCS (destination address, source address, repeater addresses, control, PID, info)
    226   size_t frameBuffLen = ((2 + frame->numRepeaters)*(RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1)) + 1 + 1 + frame->infoLen;
    227   // create frame buffer without preamble, start or stop flags
    228   #if !defined(RADIOLIB_STATIC_ONLY)
    229     uint8_t* frameBuff = new uint8_t[frameBuffLen + 2];
    230   #else
    231     uint8_t frameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
    232   #endif
    233   uint8_t* frameBuffPtr = frameBuff;
    234 
    235   // set destination callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit
    236   memset(frameBuffPtr, ' ' << 1, RADIOLIB_AX25_MAX_CALLSIGN_LEN);
    237   for(size_t i = 0; i < strlen(frame->destCallsign); i++) {
    238     *(frameBuffPtr + i) = frame->destCallsign[i] << 1;
    239   }
    240   frameBuffPtr += RADIOLIB_AX25_MAX_CALLSIGN_LEN;
    241 
    242   // set destination SSID
    243   *(frameBuffPtr++) = RADIOLIB_AX25_SSID_RESPONSE_DEST | RADIOLIB_AX25_SSID_RESERVED_BITS | (frame->destSSID & 0x0F) << 1 | RADIOLIB_AX25_SSID_HDLC_EXTENSION_CONTINUE;
    244 
    245   // set source callsign - all address field bytes are shifted by one bit to make room for HDLC address extension bit
    246   memset(frameBuffPtr, ' ' << 1, RADIOLIB_AX25_MAX_CALLSIGN_LEN);
    247   for(size_t i = 0; i < strlen(frame->srcCallsign); i++) {
    248     *(frameBuffPtr + i) = frame->srcCallsign[i] << 1;
    249   }
    250   frameBuffPtr += RADIOLIB_AX25_MAX_CALLSIGN_LEN;
    251 
    252   // set source SSID
    253   *(frameBuffPtr++) = RADIOLIB_AX25_SSID_COMMAND_SOURCE | RADIOLIB_AX25_SSID_RESERVED_BITS | (frame->srcSSID & 0x0F) << 1 | RADIOLIB_AX25_SSID_HDLC_EXTENSION_CONTINUE;
    254 
    255   // set repeater callsigns
    256   for(uint16_t i = 0; i < frame->numRepeaters; i++) {
    257     memset(frameBuffPtr, ' ' << 1, RADIOLIB_AX25_MAX_CALLSIGN_LEN);
    258     for(size_t j = 0; j < strlen(frame->repeaterCallsigns[i]); j++) {
    259       *(frameBuffPtr + j) = frame->repeaterCallsigns[i][j] << 1;
    260     }
    261     frameBuffPtr += RADIOLIB_AX25_MAX_CALLSIGN_LEN;
    262     *(frameBuffPtr++) = RADIOLIB_AX25_SSID_HAS_NOT_BEEN_REPEATED | RADIOLIB_AX25_SSID_RESERVED_BITS | (frame->repeaterSSIDs[i] & 0x0F) << 1 | RADIOLIB_AX25_SSID_HDLC_EXTENSION_CONTINUE;
    263   }
    264 
    265   // set HDLC extension end bit
    266   *(frameBuffPtr - 1) |= RADIOLIB_AX25_SSID_HDLC_EXTENSION_END;
    267 
    268   // set sequence numbers of the frames that have it
    269   uint8_t controlField = frame->control;
    270   if((frame->control & 0x01) == 0) {
    271     // information frame, set both sequence numbers
    272     controlField |= frame->rcvSeqNumber << 5;
    273     controlField |= frame->sendSeqNumber << 1;
    274   } else if((frame->control & 0x02) == 0) {
    275     // supervisory frame, set only receive sequence number
    276     controlField |= frame->rcvSeqNumber << 5;
    277   }
    278 
    279   // set control field
    280   *(frameBuffPtr++) = controlField;
    281 
    282   // set PID field of the frames that have it
    283   if(frame->protocolID != 0x00) {
    284     *(frameBuffPtr++) = frame->protocolID;
    285   }
    286 
    287   // set info field of the frames that have it
    288   if(frame->infoLen > 0) {
    289     memcpy(frameBuffPtr, frame->info, frame->infoLen);
    290     frameBuffPtr += frame->infoLen;
    291   }
    292 
    293   // flip bit order
    294   for(size_t i = 0; i < frameBuffLen; i++) {
    295     frameBuff[i] = Module::flipBits(frameBuff[i]);
    296   }
    297 
    298   // calculate FCS
    299   uint16_t fcs = getFrameCheckSequence(frameBuff, frameBuffLen);
    300   *(frameBuffPtr++) = (uint8_t)((fcs >> 8) & 0xFF);
    301   *(frameBuffPtr++) = (uint8_t)(fcs & 0xFF);
    302 
    303   // prepare buffer for the final frame (stuffed, with added preamble + flags and NRZI-encoded)
    304   #if !defined(RADIOLIB_STATIC_ONLY)
    305     // worst-case scenario: sequence of 1s, will have 120% of the original length, stuffed frame also includes both flags
    306     uint8_t* stuffedFrameBuff = new uint8_t[_preambleLen + 1 + (6*frameBuffLen)/5 + 2];
    307   #else
    308     uint8_t stuffedFrameBuff[RADIOLIB_STATIC_ARRAY_SIZE];
    309   #endif
    310 
    311   // initialize buffer to all zeros
    312   memset(stuffedFrameBuff, 0x00, _preambleLen + 1 + (6*frameBuffLen)/5 + 2);
    313 
    314   // stuff bits (skip preamble and both flags)
    315   uint16_t stuffedFrameBuffLenBits = 8*(_preambleLen + 1);
    316   uint8_t count = 0;
    317   for(size_t i = 0; i < frameBuffLen + 2; i++) {
    318     for(int8_t shift = 7; shift >= 0; shift--) {
    319       uint16_t stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8);
    320       if((frameBuff[i] >> shift) & 0x01) {
    321         // copy 1 and increment counter
    322         SET_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
    323         stuffedFrameBuffLenBits++;
    324         count++;
    325 
    326         // check 5 consecutive 1s
    327         if(count == 5) {
    328           // get the new position in stuffed frame
    329           stuffedFrameBuffPos = stuffedFrameBuffLenBits + 7 - 2*(stuffedFrameBuffLenBits%8);
    330 
    331           // insert 0 and reset counter
    332           CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
    333           stuffedFrameBuffLenBits++;
    334           count = 0;
    335         }
    336 
    337       } else {
    338         // copy 0 and reset counter
    339         CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, stuffedFrameBuffPos);
    340         stuffedFrameBuffLenBits++;
    341         count = 0;
    342       }
    343 
    344     }
    345   }
    346 
    347   // deallocate memory
    348   #if !defined(RADIOLIB_STATIC_ONLY)
    349     delete[] frameBuff;
    350   #endif
    351 
    352   // set preamble bytes and start flag field
    353   for(uint16_t i = 0; i < _preambleLen + 1; i++) {
    354     stuffedFrameBuff[i] = RADIOLIB_AX25_FLAG;
    355   }
    356 
    357   // get stuffed frame length in bytes
    358   size_t stuffedFrameBuffLen = stuffedFrameBuffLenBits/8 + 1;
    359   uint8_t trailingLen = stuffedFrameBuffLenBits % 8;
    360 
    361   // set end flag field (may be split into two bytes due to misalignment caused by extra stuffing bits)
    362   if(trailingLen != 0) {
    363     stuffedFrameBuffLen++;
    364     stuffedFrameBuff[stuffedFrameBuffLen - 2] |= RADIOLIB_AX25_FLAG >> trailingLen;
    365     stuffedFrameBuff[stuffedFrameBuffLen - 1] = RADIOLIB_AX25_FLAG << (8 - trailingLen);
    366   } else {
    367     stuffedFrameBuff[stuffedFrameBuffLen - 1] = RADIOLIB_AX25_FLAG;
    368   }
    369 
    370   // convert to NRZI
    371   for(size_t i = _preambleLen + 1; i < stuffedFrameBuffLen*8; i++) {
    372     size_t currBitPos = i + 7 - 2*(i%8);
    373     size_t prevBitPos = (i - 1) + 7 - 2*((i - 1)%8);
    374     if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos)) {
    375       // bit is 1, no change, copy previous bit
    376       if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) {
    377         SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
    378       } else {
    379         CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
    380       }
    381 
    382     } else {
    383       // bit is 0, transition, copy inversion of the previous bit
    384       if(TEST_BIT_IN_ARRAY(stuffedFrameBuff, prevBitPos)) {
    385         CLEAR_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
    386       } else {
    387         SET_BIT_IN_ARRAY(stuffedFrameBuff, currBitPos);
    388       }
    389     }
    390   }
    391 
    392   // transmit
    393   int16_t state = RADIOLIB_ERR_NONE;
    394   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    395   if(_audio != nullptr) {
    396     Module* mod = _phy->getMod();
    397     _phy->transmitDirect();
    398 
    399     // iterate over all bytes in the buffer
    400     for(uint32_t i = 0; i < stuffedFrameBuffLen; i++) {
    401 
    402       // check each bit
    403       for(uint16_t mask = 0x80; mask >= 0x01; mask >>= 1) {
    404         uint32_t start = mod->micros();
    405         if(stuffedFrameBuff[i] & mask) {
    406           _audio->tone(_afskMark, false);
    407         } else {
    408           _audio->tone(_afskSpace, false);
    409         }
    410         while(mod->micros() - start < RADIOLIB_AX25_AFSK_TONE_DURATION) {
    411           mod->yield();
    412         }
    413       }
    414 
    415     }
    416 
    417     _audio->noTone();
    418 
    419   } else {
    420   #endif
    421     state = _phy->transmit(stuffedFrameBuff, stuffedFrameBuffLen);
    422   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    423   }
    424   #endif
    425 
    426   // deallocate memory
    427   #if !defined(RADIOLIB_STATIC_ONLY)
    428     delete[] stuffedFrameBuff;
    429   #endif
    430 
    431   return(state);
    432 }
    433 
    434 void AX25Client::getCallsign(char* buff) {
    435   strncpy(buff, _srcCallsign, RADIOLIB_AX25_MAX_CALLSIGN_LEN);
    436 }
    437 
    438 uint8_t AX25Client::getSSID() {
    439   return(_srcSSID);
    440 }
    441 
    442 /*
    443   CCITT CRC implementation based on https://github.com/kicksat/ax25
    444 
    445   Licensed under Creative Commons Attribution-ShareAlike 4.0 International
    446   https://creativecommons.org/licenses/by-sa/4.0/
    447 */
    448 uint16_t AX25Client::getFrameCheckSequence(uint8_t* buff, size_t len) {
    449   uint8_t outBit;
    450   uint16_t mask;
    451   uint16_t shiftReg = CRC_CCITT_INIT;
    452 
    453   for(size_t i = 0; i < len; i++) {
    454     for(uint8_t b = 0x80; b > 0x00; b /= 2) {
    455       outBit = (shiftReg & 0x01) ? 0x01 : 0x00;
    456       shiftReg >>= 1;
    457       mask = XOR((buff[i] & b), outBit) ? CRC_CCITT_POLY_REVERSED : 0x0000;
    458       shiftReg ^= mask;
    459     }
    460   }
    461 
    462   return(Module::flipBits16(~shiftReg));
    463 }
    464 
    465 #endif