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

RTTY.cpp (13412B)

      1 #include "RTTY.h"
      2 #if !defined(RADIOLIB_EXCLUDE_RTTY)
      3 
      4 ITA2String::ITA2String(char c) {
      5   _len = 1;
      6   #if !defined(RADIOLIB_STATIC_ONLY)
      7   _str = new char[1];
      8   #endif
      9   _str[0] = c;
     10   _ita2Len = 0;
     11 }
     12 
     13 ITA2String::ITA2String(const char* str) {
     14   _len = strlen(str);
     15   #if !defined(RADIOLIB_STATIC_ONLY)
     16   _str = new char[_len + 1];
     17   #endif
     18   strcpy(_str, str);
     19   _ita2Len = 0;
     20 }
     21 
     22 ITA2String::~ITA2String() {
     23   #if !defined(RADIOLIB_STATIC_ONLY)
     24     delete[] _str;
     25   #endif
     26 }
     27 
     28 size_t ITA2String::length() {
     29   // length returned by this method is different than the length of ASCII-encoded _str
     30   // ITA2-encoded string length varies based on how many number and characters the string contains
     31 
     32   if(_ita2Len == 0) {
     33     // ITA2 length wasn't calculated yet, call byteArr() to calculate it
     34     byteArr();
     35   }
     36 
     37   return(_ita2Len);
     38 }
     39 
     40 uint8_t* ITA2String::byteArr() {
     41   // create temporary array 2x the string length (figures may be 3 bytes)
     42   #if defined(RADIOLIB_STATIC_ONLY)
     43     uint8_t temp[RADIOLIB_STATIC_ARRAY_SIZE*2 + 1];
     44   #else
     45     uint8_t* temp = new uint8_t[_len*2 + 1];
     46   #endif
     47 
     48   size_t arrayLen = 0;
     49   bool flagFigure = false;
     50   for(size_t i = 0; i < _len; i++) {
     51     uint16_t code = getBits(_str[i]);
     52     uint8_t shift = (code >> 5) & 0b11111;
     53     uint8_t character = code & 0b11111;
     54     // check if the code is letter or figure
     55     if(shift == RADIOLIB_ITA2_FIGS) {
     56       // check if this is the first figure in sequence
     57       if(!flagFigure) {
     58         flagFigure = true;
     59         temp[arrayLen++] = RADIOLIB_ITA2_FIGS;
     60       }
     61 
     62       // add the character code
     63       temp[arrayLen++] = character & 0b11111;
     64 
     65       // check the following character (skip for message end)
     66       if(i < (_len - 1)) {
     67         uint16_t nextCode = getBits(_str[i+1]);
     68         uint8_t nextShift = (nextCode >> 5) & 0b11111;
     69         if(nextShift == RADIOLIB_ITA2_LTRS) {
     70           // next character is a letter, terminate figure shift
     71           temp[arrayLen++] = RADIOLIB_ITA2_LTRS;
     72           flagFigure = false;
     73         }
     74       } else {
     75         // reached the end of the message, terminate figure shift
     76         temp[arrayLen++] = RADIOLIB_ITA2_LTRS;
     77         flagFigure = false;
     78       }
     79     } else {
     80       temp[arrayLen++] = character & 0b11111;
     81     }
     82   }
     83 
     84   // save ITA2 string length
     85   _ita2Len = arrayLen;
     86 
     87   uint8_t* arr = new uint8_t[arrayLen];
     88   memcpy(arr, temp, arrayLen);
     89   #if !defined(RADIOLIB_STATIC_ONLY)
     90     delete[] temp;
     91   #endif
     92 
     93   return(arr);
     94 }
     95 
     96 uint16_t ITA2String::getBits(char c) {
     97   // search ITA2 table
     98   uint16_t code = 0x0000;
     99   for(uint8_t i = 0; i < RADIOLIB_ITA2_LENGTH; i++) {
    100     if(RADIOLIB_NONVOLATILE_READ_BYTE(&ITA2Table[i][0]) == c) {
    101       // character is in letter shift
    102       code = (RADIOLIB_ITA2_LTRS << 5) | i;
    103       break;
    104     } else if(RADIOLIB_NONVOLATILE_READ_BYTE(&ITA2Table[i][1]) == c) {
    105       // character is in figures shift
    106       code = (RADIOLIB_ITA2_FIGS << 5) | i;
    107       break;
    108     }
    109   }
    110 
    111   return(code);
    112 }
    113 
    114 RTTYClient::RTTYClient(PhysicalLayer* phy) {
    115   _phy = phy;
    116   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    117   _audio = nullptr;
    118   #endif
    119 }
    120 
    121 #if !defined(RADIOLIB_EXCLUDE_AFSK)
    122 RTTYClient::RTTYClient(AFSKClient* audio) {
    123   _phy = audio->_phy;
    124   _audio = audio;
    125 }
    126 #endif
    127 
    128 int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t encoding, uint8_t stopBits) {
    129   // save configuration
    130   _encoding = encoding;
    131   _stopBits = stopBits;
    132   _baseHz = base;
    133   _shiftHz = shift;
    134 
    135   switch(encoding) {
    136     case RADIOLIB_ASCII:
    137       _dataBits = 7;
    138       break;
    139     case RADIOLIB_ASCII_EXTENDED:
    140       _dataBits = 8;
    141       break;
    142     case RADIOLIB_ITA2:
    143       _dataBits = 5;
    144       break;
    145     default:
    146       return(RADIOLIB_ERR_UNSUPPORTED_ENCODING);
    147   }
    148 
    149   // calculate duration of 1 bit
    150   _bitDuration = (uint32_t)1000000/rate;
    151 
    152   // calculate module carrier frequency resolution
    153   uint32_t step = round(_phy->getFreqStep());
    154 
    155   // check minimum shift value
    156   if(shift < step / 2) {
    157     return(RADIOLIB_ERR_INVALID_RTTY_SHIFT);
    158   }
    159 
    160   // round shift to multiples of frequency step size
    161   if(shift % step < (step / 2)) {
    162     _shift = shift / step;
    163   } else {
    164     _shift = (shift / step) + 1;
    165   }
    166 
    167   // calculate 24-bit frequency
    168   _base = (base * 1000000.0) / _phy->getFreqStep();
    169 
    170   // configure for direct mode
    171   return(_phy->startDirect());
    172 }
    173 
    174 void RTTYClient::idle() {
    175   mark();
    176 }
    177 
    178 size_t RTTYClient::write(const char* str) {
    179   if(str == NULL) {
    180     return(0);
    181   }
    182   return(RTTYClient::write((uint8_t *)str, strlen(str)));
    183 }
    184 
    185 size_t RTTYClient::write(uint8_t* buff, size_t len) {
    186   size_t n = 0;
    187   for(size_t i = 0; i < len; i++) {
    188     n += RTTYClient::write(buff[i]);
    189   }
    190   return(n);
    191 }
    192 
    193 size_t RTTYClient::write(uint8_t b) {
    194   space();
    195 
    196   uint16_t maxDataMask = 0x01 << (_dataBits - 1);
    197   for(uint16_t mask = 0x01; mask <= maxDataMask; mask <<= 1) {
    198     if(b & mask) {
    199       mark();
    200     } else {
    201       space();
    202     }
    203   }
    204 
    205   for(uint8_t i = 0; i < _stopBits; i++) {
    206     mark();
    207   }
    208 
    209   return(1);
    210 }
    211 
    212 size_t RTTYClient::print(__FlashStringHelper* fstr) {
    213   // read flash string length
    214   size_t len = 0;
    215   PGM_P p = reinterpret_cast<PGM_P>(fstr);
    216   while(true) {
    217     char c = RADIOLIB_NONVOLATILE_READ_BYTE(p++);
    218     len++;
    219     if(c == '\0') {
    220       break;
    221     }
    222   }
    223 
    224   // dynamically allocate memory
    225   #if defined(RADIOLIB_STATIC_ONLY)
    226     char str[RADIOLIB_STATIC_ARRAY_SIZE];
    227   #else
    228     char* str = new char[len];
    229   #endif
    230 
    231   // copy string from flash
    232   p = reinterpret_cast<PGM_P>(fstr);
    233   for(size_t i = 0; i < len; i++) {
    234     str[i] = RADIOLIB_NONVOLATILE_READ_BYTE(p + i);
    235   }
    236 
    237   size_t n = 0;
    238   if(_encoding == RADIOLIB_ITA2) {
    239     ITA2String ita2 = ITA2String(str);
    240     n = RTTYClient::print(ita2);
    241   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    242     n = RTTYClient::write((uint8_t*)str, len);
    243   }
    244   #if !defined(RADIOLIB_STATIC_ONLY)
    245     delete[] str;
    246   #endif
    247   return(n);
    248 }
    249 
    250 size_t RTTYClient::print(ITA2String& ita2) {
    251   uint8_t* arr = ita2.byteArr();
    252   size_t n = RTTYClient::write(arr, ita2.length());
    253   delete[] arr;
    254   return(n);
    255 }
    256 
    257 size_t RTTYClient::print(const String& str) {
    258   size_t n = 0;
    259   if(_encoding == RADIOLIB_ITA2) {
    260     ITA2String ita2 = ITA2String(str.c_str());
    261     n = RTTYClient::print(ita2);
    262   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    263     n = RTTYClient::write((uint8_t*)str.c_str(), str.length());
    264   }
    265   return(n);
    266 }
    267 
    268 size_t RTTYClient::print(const char str[]) {
    269   size_t n = 0;
    270   if(_encoding == RADIOLIB_ITA2) {
    271     ITA2String ita2 = ITA2String(str);
    272     n = RTTYClient::print(ita2);
    273   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    274     n = RTTYClient::write((uint8_t*)str, strlen(str));
    275   }
    276   return(n);
    277 }
    278 
    279 size_t RTTYClient::print(char c) {
    280   size_t n = 0;
    281   if(_encoding == RADIOLIB_ITA2) {
    282     ITA2String ita2 = ITA2String(c);
    283     n = RTTYClient::print(ita2);
    284   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    285     n = RTTYClient::write(c);
    286   }
    287   return(n);
    288 }
    289 
    290 size_t RTTYClient::print(unsigned char b, int base) {
    291   return(RTTYClient::print((unsigned long)b, base));
    292 }
    293 
    294 size_t RTTYClient::print(int n, int base) {
    295   return(RTTYClient::print((long)n, base));
    296 }
    297 
    298 size_t RTTYClient::print(unsigned int n, int base) {
    299   return(RTTYClient::print((unsigned long)n, base));
    300 }
    301 
    302 size_t RTTYClient::print(long n, int base) {
    303   if(base == 0) {
    304     return(RTTYClient::write(n));
    305   } else if(base == DEC) {
    306     if (n < 0) {
    307       int t = RTTYClient::print('-');
    308       n = -n;
    309       return(RTTYClient::printNumber(n, DEC) + t);
    310     }
    311     return(RTTYClient::printNumber(n, DEC));
    312   } else {
    313     return(RTTYClient::printNumber(n, base));
    314   }
    315 }
    316 
    317 size_t RTTYClient::print(unsigned long n, int base) {
    318   if(base == 0) {
    319     return(RTTYClient::write(n));
    320   } else {
    321     return(RTTYClient::printNumber(n, base));
    322   }
    323 }
    324 
    325 size_t RTTYClient::print(double n, int digits) {
    326   return(RTTYClient::printFloat(n, digits));
    327 }
    328 
    329 size_t RTTYClient::println(void) {
    330   size_t n = 0;
    331   if(_encoding == RADIOLIB_ITA2) {
    332     ITA2String lf = ITA2String("\r\n");
    333     n = RTTYClient::print(lf);
    334   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    335     n = RTTYClient::write("\r\n");
    336   }
    337   return(n);
    338 }
    339 
    340 size_t RTTYClient::println(__FlashStringHelper* fstr) {
    341   size_t n = RTTYClient::print(fstr);
    342   n += RTTYClient::println();
    343   return(n);
    344 }
    345 
    346 size_t RTTYClient::println(ITA2String& ita2) {
    347   size_t n = RTTYClient::print(ita2);
    348   n += RTTYClient::println();
    349   return(n);
    350 }
    351 
    352 size_t RTTYClient::println(const String& str) {
    353   size_t n = RTTYClient::print(str);
    354   n += RTTYClient::println();
    355   return(n);
    356 }
    357 
    358 size_t RTTYClient::println(const char* str) {
    359   size_t n = RTTYClient::print(str);
    360   n += RTTYClient::println();
    361   return(n);
    362 }
    363 
    364 size_t RTTYClient::println(char c) {
    365   size_t n = RTTYClient::print(c);
    366   n += RTTYClient::println();
    367   return(n);
    368 }
    369 
    370 size_t RTTYClient::println(unsigned char b, int base) {
    371   size_t n = RTTYClient::print(b, base);
    372   n += RTTYClient::println();
    373   return(n);
    374 }
    375 
    376 size_t RTTYClient::println(int num, int base) {
    377   size_t n = RTTYClient::print(num, base);
    378   n += RTTYClient::println();
    379   return(n);
    380 }
    381 
    382 size_t RTTYClient::println(unsigned int num, int base) {
    383   size_t n = RTTYClient::print(num, base);
    384   n += RTTYClient::println();
    385   return(n);
    386 }
    387 
    388 size_t RTTYClient::println(long num, int base) {
    389   size_t n = RTTYClient::print(num, base);
    390   n += RTTYClient::println();
    391   return(n);
    392 }
    393 
    394 size_t RTTYClient::println(unsigned long num, int base) {
    395   size_t n = RTTYClient::print(num, base);
    396   n += RTTYClient::println();
    397   return(n);
    398 }
    399 
    400 size_t RTTYClient::println(double d, int digits) {
    401   size_t n = RTTYClient::print(d, digits);
    402   n += RTTYClient::println();
    403   return(n);
    404 }
    405 
    406 void RTTYClient::mark() {
    407   Module* mod = _phy->getMod();
    408   uint32_t start = mod->micros();
    409   transmitDirect(_base + _shift, _baseHz + _shiftHz);
    410   while(mod->micros() - start < _bitDuration) {
    411     mod->yield();
    412   }
    413 }
    414 
    415 void RTTYClient::space() {
    416   Module* mod = _phy->getMod();
    417   uint32_t start = mod->micros();
    418   transmitDirect(_base, _baseHz);
    419   while(mod->micros() - start < _bitDuration) {
    420     mod->yield();
    421   }
    422 }
    423 
    424 size_t RTTYClient::printNumber(unsigned long n, uint8_t base) {
    425   char buf[8 * sizeof(long) + 1];
    426   char *str = &buf[sizeof(buf) - 1];
    427 
    428   *str = '\0';
    429 
    430   if(base < 2) {
    431     base = 10;
    432   }
    433 
    434   do {
    435     char c = n % base;
    436     n /= base;
    437 
    438     *--str = c < 10 ? c + '0' : c + 'A' - 10;
    439   } while(n);
    440 
    441   size_t l = 0;
    442   if(_encoding == RADIOLIB_ITA2) {
    443     ITA2String ita2 = ITA2String(str);
    444     uint8_t* arr = ita2.byteArr();
    445     l = RTTYClient::write(arr, ita2.length());
    446     delete[] arr;
    447   } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    448     l = RTTYClient::write(str);
    449   }
    450 
    451   return(l);
    452 }
    453 
    454 /// \todo improve ITA2 float print speed (characters are sent one at a time)
    455 size_t RTTYClient::printFloat(double number, uint8_t digits)  {
    456   size_t n = 0;
    457 
    458   char code[] = {0x00, 0x00, 0x00, 0x00};
    459   if (isnan(number)) strcpy(code, "nan");
    460   if (isinf(number)) strcpy(code, "inf");
    461   if (number > 4294967040.0) strcpy(code, "ovf");  // constant determined empirically
    462   if (number <-4294967040.0) strcpy(code, "ovf");  // constant determined empirically
    463 
    464   if(code[0] != 0x00) {
    465     if(_encoding == RADIOLIB_ITA2) {
    466       ITA2String ita2 = ITA2String(code);
    467       uint8_t* arr = ita2.byteArr();
    468       n = RTTYClient::write(arr, ita2.length());
    469       delete[] arr;
    470       return(n);
    471     } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    472       return(RTTYClient::write(code));
    473     }
    474   }
    475 
    476   // Handle negative numbers
    477   if (number < 0.0) {
    478     if(_encoding == RADIOLIB_ITA2) {
    479       ITA2String ita2 = ITA2String("-");
    480       uint8_t* arr = ita2.byteArr();
    481       n += RTTYClient::write(arr, ita2.length());
    482       delete[] arr;
    483     } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    484       n += RTTYClient::print('-');
    485     }
    486     number = -number;
    487   }
    488 
    489   // Round correctly so that print(1.999, 2) prints as "2.00"
    490   double rounding = 0.5;
    491   for(uint8_t i = 0; i < digits; ++i) {
    492     rounding /= 10.0;
    493   }
    494   number += rounding;
    495 
    496   // Extract the integer part of the number and print it
    497   unsigned long int_part = (unsigned long)number;
    498   double remainder = number - (double)int_part;
    499   n += RTTYClient::print(int_part);
    500 
    501   // Print the decimal point, but only if there are digits beyond
    502   if(digits > 0) {
    503     if(_encoding == RADIOLIB_ITA2) {
    504       ITA2String ita2 = ITA2String(".");
    505       uint8_t* arr = ita2.byteArr();
    506       n += RTTYClient::write(arr, ita2.length());
    507       delete[] arr;
    508     } else if((_encoding == RADIOLIB_ASCII) || (_encoding == RADIOLIB_ASCII_EXTENDED)) {
    509       n += RTTYClient::print('.');
    510     }
    511   }
    512 
    513   // Extract digits from the remainder one at a time
    514   while(digits-- > 0) {
    515     remainder *= 10.0;
    516     unsigned int toPrint = (unsigned int)(remainder);
    517     n += RTTYClient::print(toPrint);
    518     remainder -= toPrint;
    519   }
    520 
    521   return n;
    522 }
    523 
    524 int16_t RTTYClient::transmitDirect(uint32_t freq, uint32_t freqHz) {
    525   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    526   if(_audio != nullptr) {
    527     return(_audio->tone(freqHz));
    528   }
    529   #endif
    530   return(_phy->transmitDirect(freq));
    531 }
    532 
    533 int16_t RTTYClient::standby() {
    534   #if !defined(RADIOLIB_EXCLUDE_AFSK)
    535   if(_audio != nullptr) {
    536     return(_audio->noTone());
    537   }
    538   #endif
    539   return(_phy->standby());
    540 }
    541 
    542 #endif