[RTTY] Use common print class

This commit is contained in:
jgromes 2023-04-29 22:54:27 +02:00
parent 089a81faec
commit d79ed24a26
2 changed files with 25 additions and 544 deletions

View file

@ -3,118 +3,9 @@
#include <math.h> #include <math.h>
#if !defined(RADIOLIB_EXCLUDE_RTTY) #if !defined(RADIOLIB_EXCLUDE_RTTY)
ITA2String::ITA2String(char c) {
asciiLen = 1;
#if !defined(RADIOLIB_STATIC_ONLY)
strAscii = new char[1];
#endif
strAscii[0] = c;
ita2Len = 0;
}
ITA2String::ITA2String(const char* str) {
asciiLen = strlen(str);
#if !defined(RADIOLIB_STATIC_ONLY)
strAscii = new char[asciiLen + 1];
#endif
strcpy(strAscii, str);
ita2Len = 0;
}
ITA2String::~ITA2String() {
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] strAscii;
#endif
}
size_t ITA2String::length() {
// length returned by this method is different than the length of ASCII-encoded strAscii
// ITA2-encoded string length varies based on how many number and characters the string contains
if(ita2Len == 0) {
// ITA2 length wasn't calculated yet, call byteArr() to calculate it
byteArr();
}
return(ita2Len);
}
uint8_t* ITA2String::byteArr() {
// create temporary array 2x the string length (figures may be 3 bytes)
#if defined(RADIOLIB_STATIC_ONLY)
uint8_t temp[RADIOLIB_STATIC_ARRAY_SIZE*2 + 1];
#else
uint8_t* temp = new uint8_t[asciiLen*2 + 1];
#endif
size_t arrayLen = 0;
bool flagFigure = false;
for(size_t i = 0; i < asciiLen; i++) {
uint16_t code = getBits(strAscii[i]);
uint8_t shift = (code >> 5) & 0b11111;
uint8_t character = code & 0b11111;
// check if the code is letter or figure
if(shift == RADIOLIB_ITA2_FIGS) {
// check if this is the first figure in sequence
if(!flagFigure) {
flagFigure = true;
temp[arrayLen++] = RADIOLIB_ITA2_FIGS;
}
// add the character code
temp[arrayLen++] = character & 0b11111;
// check the following character (skip for message end)
if(i < (asciiLen - 1)) {
uint16_t nextCode = getBits(strAscii[i+1]);
uint8_t nextShift = (nextCode >> 5) & 0b11111;
if(nextShift == RADIOLIB_ITA2_LTRS) {
// next character is a letter, terminate figure shift
temp[arrayLen++] = RADIOLIB_ITA2_LTRS;
flagFigure = false;
}
} else {
// reached the end of the message, terminate figure shift
temp[arrayLen++] = RADIOLIB_ITA2_LTRS;
flagFigure = false;
}
} else {
temp[arrayLen++] = character & 0b11111;
}
}
// save ITA2 string length
ita2Len = arrayLen;
uint8_t* arr = new uint8_t[arrayLen];
memcpy(arr, temp, arrayLen);
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] temp;
#endif
return(arr);
}
uint16_t ITA2String::getBits(char c) {
// search ITA2 table
uint16_t code = 0x0000;
for(uint8_t i = 0; i < RADIOLIB_ITA2_LENGTH; i++) {
if(RADIOLIB_NONVOLATILE_READ_BYTE(&ITA2Table[i][0]) == c) {
// character is in letter shift
code = (RADIOLIB_ITA2_LTRS << 5) | i;
break;
} else if(RADIOLIB_NONVOLATILE_READ_BYTE(&ITA2Table[i][1]) == c) {
// character is in figures shift
code = (RADIOLIB_ITA2_FIGS << 5) | i;
break;
}
}
return(code);
}
RTTYClient::RTTYClient(PhysicalLayer* phy) { RTTYClient::RTTYClient(PhysicalLayer* phy) {
phyLayer = phy; phyLayer = phy;
lineFeed = "\r\n";
#if !defined(RADIOLIB_EXCLUDE_AFSK) #if !defined(RADIOLIB_EXCLUDE_AFSK)
audioClient = nullptr; audioClient = nullptr;
#endif #endif
@ -123,31 +14,18 @@ RTTYClient::RTTYClient(PhysicalLayer* phy) {
#if !defined(RADIOLIB_EXCLUDE_AFSK) #if !defined(RADIOLIB_EXCLUDE_AFSK)
RTTYClient::RTTYClient(AFSKClient* audio) { RTTYClient::RTTYClient(AFSKClient* audio) {
phyLayer = audio->phyLayer; phyLayer = audio->phyLayer;
lineFeed = "\r\n";
audioClient = audio; audioClient = audio;
} }
#endif #endif
int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t enc, uint8_t stopBits) { int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t enc, uint8_t stopBits) {
// save configuration // save configuration
encoding = enc; RadioLibPrint::encoding = enc;
stopBitsNum = stopBits; stopBitsNum = stopBits;
baseFreqHz = base; baseFreqHz = base;
shiftFreqHz = shift; shiftFreqHz = shift;
switch(encoding) {
case RADIOLIB_ASCII:
dataBitsNum = 7;
break;
case RADIOLIB_ASCII_EXTENDED:
dataBitsNum = 8;
break;
case RADIOLIB_ITA2:
dataBitsNum = 5;
break;
default:
return(RADIOLIB_ERR_UNSUPPORTED_ENCODING);
}
// calculate duration of 1 bit // calculate duration of 1 bit
bitDuration = (uint32_t)1000000/rate; bitDuration = (uint32_t)1000000/rate;
@ -177,22 +55,21 @@ void RTTYClient::idle() {
mark(); mark();
} }
size_t RTTYClient::write(const char* str) {
if(str == NULL) {
return(0);
}
return(RTTYClient::write((uint8_t *)str, strlen(str)));
}
size_t RTTYClient::write(uint8_t* buff, size_t len) {
size_t n = 0;
for(size_t i = 0; i < len; i++) {
n += RTTYClient::write(buff[i]);
}
return(n);
}
size_t RTTYClient::write(uint8_t b) { size_t RTTYClient::write(uint8_t b) {
uint8_t dataBitsNum = 0;
switch(RadioLibPrint::encoding) {
case RADIOLIB_ASCII:
dataBitsNum = 7;
break;
case RADIOLIB_ASCII_EXTENDED:
dataBitsNum = 8;
break;
case RADIOLIB_ITA2:
dataBitsNum = 5;
break;
default:
return(0);
}
space(); space();
uint16_t maxDataMask = 0x01 << (dataBitsNum - 1); uint16_t maxDataMask = 0x01 << (dataBitsNum - 1);
@ -211,204 +88,6 @@ size_t RTTYClient::write(uint8_t b) {
return(1); return(1);
} }
#if defined(RADIOLIB_BUILD_ARDUINO)
size_t RTTYClient::print(__FlashStringHelper* fstr) {
// read flash string length
size_t len = 0;
PGM_P p = reinterpret_cast<PGM_P>(fstr);
while(true) {
char c = RADIOLIB_NONVOLATILE_READ_BYTE(p++);
len++;
if(c == '\0') {
break;
}
}
// dynamically allocate memory
#if defined(RADIOLIB_STATIC_ONLY)
char str[RADIOLIB_STATIC_ARRAY_SIZE];
#else
char* str = new char[len];
#endif
// copy string from flash
p = reinterpret_cast<PGM_P>(fstr);
for(size_t i = 0; i < len; i++) {
str[i] = RADIOLIB_NONVOLATILE_READ_BYTE(p + i);
}
size_t n = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(str);
n = RTTYClient::print(ita2);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n = RTTYClient::write((uint8_t*)str, len);
}
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] str;
#endif
return(n);
}
size_t RTTYClient::print(const String& str) {
size_t n = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(str.c_str());
n = RTTYClient::print(ita2);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n = RTTYClient::write((uint8_t*)str.c_str(), str.length());
}
return(n);
}
#endif
size_t RTTYClient::print(ITA2String& ita2) {
uint8_t* arr = ita2.byteArr();
size_t n = RTTYClient::write(arr, ita2.length());
delete[] arr;
return(n);
}
size_t RTTYClient::print(const char str[]) {
size_t n = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(str);
n = RTTYClient::print(ita2);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n = RTTYClient::write((uint8_t*)str, strlen(str));
}
return(n);
}
size_t RTTYClient::print(char c) {
size_t n = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(c);
n = RTTYClient::print(ita2);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n = RTTYClient::write(c);
}
return(n);
}
size_t RTTYClient::print(unsigned char b, int base) {
return(RTTYClient::print((unsigned long)b, base));
}
size_t RTTYClient::print(int n, int base) {
return(RTTYClient::print((long)n, base));
}
size_t RTTYClient::print(unsigned int n, int base) {
return(RTTYClient::print((unsigned long)n, base));
}
size_t RTTYClient::print(long n, int base) {
if(base == 0) {
return(RTTYClient::write(n));
} else if(base == DEC) {
if (n < 0) {
int t = RTTYClient::print('-');
n = -n;
return(RTTYClient::printNumber(n, DEC) + t);
}
return(RTTYClient::printNumber(n, DEC));
} else {
return(RTTYClient::printNumber(n, base));
}
}
size_t RTTYClient::print(unsigned long n, int base) {
if(base == 0) {
return(RTTYClient::write(n));
} else {
return(RTTYClient::printNumber(n, base));
}
}
size_t RTTYClient::print(double n, int digits) {
return(RTTYClient::printFloat(n, digits));
}
size_t RTTYClient::println(void) {
size_t n = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String lf = ITA2String("\r\n");
n = RTTYClient::print(lf);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n = RTTYClient::write("\r\n");
}
return(n);
}
#if defined(RADIOLIB_BUILD_ARDUINO)
size_t RTTYClient::println(__FlashStringHelper* fstr) {
size_t n = RTTYClient::print(fstr);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(const String& str) {
size_t n = RTTYClient::print(str);
n += RTTYClient::println();
return(n);
}
#endif
size_t RTTYClient::println(ITA2String& ita2) {
size_t n = RTTYClient::print(ita2);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(const char* str) {
size_t n = RTTYClient::print(str);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(char c) {
size_t n = RTTYClient::print(c);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(unsigned char b, int base) {
size_t n = RTTYClient::print(b, base);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(int num, int base) {
size_t n = RTTYClient::print(num, base);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(unsigned int num, int base) {
size_t n = RTTYClient::print(num, base);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(long num, int base) {
size_t n = RTTYClient::print(num, base);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(unsigned long num, int base) {
size_t n = RTTYClient::print(num, base);
n += RTTYClient::println();
return(n);
}
size_t RTTYClient::println(double d, int digits) {
size_t n = RTTYClient::print(d, digits);
n += RTTYClient::println();
return(n);
}
void RTTYClient::mark() { void RTTYClient::mark() {
Module* mod = phyLayer->getMod(); Module* mod = phyLayer->getMod();
uint32_t start = mod->hal->micros(); uint32_t start = mod->hal->micros();
@ -423,106 +102,6 @@ void RTTYClient::space() {
mod->waitForMicroseconds(start, bitDuration); mod->waitForMicroseconds(start, bitDuration);
} }
size_t RTTYClient::printNumber(unsigned long n, uint8_t base) {
char buf[8 * sizeof(long) + 1];
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
if(base < 2) {
base = 10;
}
do {
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
size_t l = 0;
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(str);
uint8_t* arr = ita2.byteArr();
l = RTTYClient::write(arr, ita2.length());
delete[] arr;
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
l = RTTYClient::write(str);
}
return(l);
}
/// \todo improve ITA2 float print speed (characters are sent one at a time)
size_t RTTYClient::printFloat(double number, uint8_t digits) {
size_t n = 0;
char code[] = {0x00, 0x00, 0x00, 0x00};
if (isnan(number)) strcpy(code, "nan");
if (isinf(number)) strcpy(code, "inf");
if (number > 4294967040.0) strcpy(code, "ovf"); // constant determined empirically
if (number <-4294967040.0) strcpy(code, "ovf"); // constant determined empirically
if(code[0] != 0x00) {
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(code);
uint8_t* arr = ita2.byteArr();
n = RTTYClient::write(arr, ita2.length());
delete[] arr;
return(n);
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
return(RTTYClient::write(code));
}
}
// Handle negative numbers
if (number < 0.0) {
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String("-");
uint8_t* arr = ita2.byteArr();
n += RTTYClient::write(arr, ita2.length());
delete[] arr;
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n += RTTYClient::print('-');
}
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for(uint8_t i = 0; i < digits; ++i) {
rounding /= 10.0;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
n += RTTYClient::print(int_part);
// Print the decimal point, but only if there are digits beyond
if(digits > 0) {
if(encoding == RADIOLIB_ITA2) {
ITA2String ita2 = ITA2String(".");
uint8_t* arr = ita2.byteArr();
n += RTTYClient::write(arr, ita2.length());
delete[] arr;
} else if((encoding == RADIOLIB_ASCII) || (encoding == RADIOLIB_ASCII_EXTENDED)) {
n += RTTYClient::print('.');
}
}
// Extract digits from the remainder one at a time
while(digits-- > 0) {
remainder *= 10.0;
unsigned int toPrint = (unsigned int)(remainder);
n += RTTYClient::print(toPrint);
remainder -= toPrint;
}
return n;
}
int16_t RTTYClient::transmitDirect(uint32_t freq, uint32_t freqHz) { int16_t RTTYClient::transmitDirect(uint32_t freq, uint32_t freqHz) {
#if !defined(RADIOLIB_EXCLUDE_AFSK) #if !defined(RADIOLIB_EXCLUDE_AFSK)
if(audioClient != nullptr) { if(audioClient != nullptr) {

View file

@ -7,81 +7,14 @@
#include "../PhysicalLayer/PhysicalLayer.h" #include "../PhysicalLayer/PhysicalLayer.h"
#include "../AFSK/AFSK.h" #include "../AFSK/AFSK.h"
#include "../Print/Print.h"
#define RADIOLIB_ITA2_FIGS 0x1B #include "../Print/ITA2String.h"
#define RADIOLIB_ITA2_LTRS 0x1F
#define RADIOLIB_ITA2_LENGTH 32
// ITA2 character table: - position in array corresponds to 5-bit ITA2 code
// - characters to the left are in letters shift, characters to the right in figures shift
// - characters marked 0x7F do not have ASCII equivalent
static const char ITA2Table[RADIOLIB_ITA2_LENGTH][2] RADIOLIB_NONVOLATILE = {
{'\0', '\0'}, {'E', '3'}, {'\n', '\n'}, {'A', '-'}, {' ', ' '}, {'S', '\''}, {'I', '8'}, {'U', '7'},
{'\r', '\r'}, {'D', 0x05}, {'R', '4'}, {'J', '\a'}, {'N', ','}, {'F', '!'}, {'C', ':'}, {'K', '('},
{'T', '5'}, {'Z', '+'}, {'L', ')'}, {'W', '2'}, {'H', 0x7F}, {'Y', '6'}, {'P', '0'}, {'Q', '1'},
{'O', '9'}, {'B', '?'}, {'G', '&'}, {0x7F, 0x7F}, {'M', '.'}, {'X', '/'}, {'V', ';'}, {0x7F, 0x7F}
};
/*!
\class ITA2String
\brief ITA2-encoded string.
*/
class ITA2String {
public:
/*!
\brief Default single-character constructor.
\param c ASCII-encoded character to encode as ITA2.
*/
explicit ITA2String(char c);
/*!
\brief Default string constructor.
\param str ASCII-encoded string to encode as ITA2.
*/
explicit ITA2String(const char* str);
/*!
\brief Default destructor.
*/
~ITA2String();
/*!
\brief Gets the length of the ITA2 string. This number is not the same as the length of ASCII-encoded string!
\returns Length of ITA2-encoded string.
*/
size_t length();
/*!
\brief Gets the ITA2 representation of the ASCII string set in constructor.
\returns Pointer to dynamically allocated array, which contains ITA2-encoded bytes.
It is the caller's responsibility to deallocate this memory!
*/
uint8_t* byteArr();
#if !defined(RADIOLIB_GODMODE)
private:
#endif
#if defined(RADIOLIB_STATIC_ONLY)
char strAscii[RADIOLIB_STATIC_ARRAY_SIZE];
#else
char* strAscii;
#endif
size_t asciiLen;
size_t ita2Len;
static uint16_t getBits(char c);
};
// supported encoding schemes
#define RADIOLIB_ASCII 0
#define RADIOLIB_ASCII_EXTENDED 1
#define RADIOLIB_ITA2 2
/*! /*!
\class RTTYClient \class RTTYClient
\brief Client for RTTY communication. The public interface is the same as Arduino Serial. \brief Client for RTTY communication. The public interface is the same as Arduino Serial.
*/ */
class RTTYClient { class RTTYClient: public RadioLibPrint {
public: public:
/*! /*!
\brief Constructor for 2-FSK mode. \brief Constructor for 2-FSK mode.
@ -121,39 +54,13 @@ class RTTYClient {
*/ */
int16_t standby(); int16_t standby();
size_t write(const char* str); /*!
size_t write(uint8_t* buff, size_t len); \brief Write one byte. Implementation of interface of the RadioLibPrint/Print class.
\param b Byte to write.
\returns 1 if the byte was written, 0 otherwise.
*/
size_t write(uint8_t b); size_t write(uint8_t b);
#if defined(RADIOLIB_BUILD_ARDUINO)
size_t print(__FlashStringHelper*);
size_t print(const String &);
#endif
size_t print(ITA2String &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(double, int = 2);
size_t println(void);
#if defined(RADIOLIB_BUILD_ARDUINO)
size_t println(__FlashStringHelper*);
size_t println(const String &);
#endif
size_t println(ITA2String &);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(double, int = 2);
#if !defined(RADIOLIB_GODMODE) #if !defined(RADIOLIB_GODMODE)
private: private:
#endif #endif
@ -162,19 +69,14 @@ class RTTYClient {
AFSKClient* audioClient; AFSKClient* audioClient;
#endif #endif
uint8_t encoding = RADIOLIB_ASCII;
uint32_t baseFreq = 0, baseFreqHz = 0; uint32_t baseFreq = 0, baseFreqHz = 0;
uint32_t shiftFreq = 0, shiftFreqHz = 0; uint32_t shiftFreq = 0, shiftFreqHz = 0;
uint32_t bitDuration = 0; uint32_t bitDuration = 0;
uint8_t dataBitsNum = 0;
uint8_t stopBitsNum = 0; uint8_t stopBitsNum = 0;
void mark(); void mark();
void space(); void space();
size_t printNumber(unsigned long, uint8_t);
size_t printFloat(double, uint8_t);
int16_t transmitDirect(uint32_t freq = 0, uint32_t freqHz = 0); int16_t transmitDirect(uint32_t freq = 0, uint32_t freqHz = 0);
}; };