From 92a511af719688ffb4fd1a2d9e12eedd02cc5eca Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 17 Oct 2021 02:18:18 +0200 Subject: [PATCH 1/2] Fix #321: add 24-bit encoding support --- src/Catena_TxBuffer.h | 45 ++++++++-- src/lib/Catena_TxBuffer.cpp | 169 ++++++++++++++++++++++++++++++++++-- 2 files changed, 199 insertions(+), 15 deletions(-) diff --git a/src/Catena_TxBuffer.h b/src/Catena_TxBuffer.h index 9bf62de..4d140ee 100644 --- a/src/Catena_TxBuffer.h +++ b/src/Catena_TxBuffer.h @@ -20,9 +20,41 @@ Copyright notice: namespace McciCatena { +// the underlying types for the shared statics +class AbstractTxBufferBase_t + { +public: + /// \brief convert float to 24-bit representation + static uint32_t f2sflt24(float f); + + /// convert a float in [0..1) to signed 12-bit float. + /// + /// \note This a wrapper for the LMIC utility of the same name; + /// doing it this way reduces namespace pollution. + static uint16_t f2sflt12(float f); + + /// convert a float in [0..1) to signed 16-bit float. + /// + /// \note This a wrapper for the LMIC utility of the same name; + /// doing it this way reduces namespace pollution. + static uint16_t f2sflt16(float f); + + /// convert a float in [0..1) to unsigned 12-bit float. + /// + /// \note This a wrapper for the LMIC utility of the same name; + /// doing it this way reduces namespace pollution. + static uint16_t f2uflt12(float f); + + /// convert a float in [0..1) to unsigned 16-bit float. + /// + /// \note This a wrapper for the LMIC utility of the same name; + /// doing it this way reduces namespace pollution. + static uint16_t f2uflt16(float f); + }; + // build a transmit buffer template -class AbstractTxBuffer_t +class AbstractTxBuffer_t : public AbstractTxBufferBase_t { private: uint8_t buf[N]; // this sets the largest buffer size @@ -113,6 +145,11 @@ class AbstractTxBuffer_t x.f = v; put4u(x.ui); } + /// put a 3-byte 24-bit floating point + void put3f(float v) + { + put3(f2sflt24(v)); + } // get address of next byte to be filled uint8_t *getp(void) { @@ -207,12 +244,6 @@ class AbstractTxBuffer_t { put2u(fracAsFloat12); } - - // convert a float in [0..1) to unsigned 16-bit float. - static uint16_t f2sflt12(float f); - static uint16_t f2sflt16(float f); - static uint16_t f2uflt12(float f); - static uint16_t f2uflt16(float f); }; // for backwards compatibilty diff --git a/src/lib/Catena_TxBuffer.cpp b/src/lib/Catena_TxBuffer.cpp index e3c9e48..b12c9f9 100644 --- a/src/lib/Catena_TxBuffer.cpp +++ b/src/lib/Catena_TxBuffer.cpp @@ -19,28 +19,181 @@ Copyright notice: #include #include +#include namespace McciCatena { -template<> -uint16_t AbstractTxBuffer_t<>::f2sflt12(float f) +uint16_t AbstractTxBufferBase_t::f2sflt12(float f) { return LMIC_f2sflt12(f); } -template<> -uint16_t AbstractTxBuffer_t<>::f2sflt16(float f) + +uint16_t AbstractTxBufferBase_t::f2sflt16(float f) { return LMIC_f2sflt16(f); } -template<> -uint16_t AbstractTxBuffer_t<>::f2uflt12(float f) + +uint16_t AbstractTxBufferBase_t::f2uflt12(float f) { return LMIC_f2uflt12(f); } -template<> -uint16_t AbstractTxBuffer_t<>::f2uflt16(float f) + +uint16_t AbstractTxBufferBase_t::f2uflt16(float f) { return LMIC_f2uflt16(f); } + +/* + +Name: AbstractTxBufferBase_t::f2sflt24() + +Function: + Convert a float to a 24-bit form based on IEEE-754. + +Definition: + uint32_t AbstractTxBufferBase_t::f2sflt24( + float f + ); + +Description: + This function returns a value strictly in the range 0..0xFFFFFF, containing + a representation of the input value as a 24-bit floating point number. + + The bits of the result have the following interpretation: + + | 23 | 22..16 | 15..0 + |:----:|:--------:|:---------: + | sign | exponent | mantissa + + This format (which was defined by MCCI based on IEEE 754) can represent + magnitudes in the approximate range 2.168e-19 < abs(x) < 1.8445e19, with + a little more than 5 decimal digits of precision. It can also represent + +0, -0, +inf, -inf, and NaN. Denormals are supported, allowing numbers + to be represented with decreasing precision down to 3.309e-24 < abs(x). + + The sign bit is set if the number is negative, reset otherwise. Note that + negative zero is possible. + + The 7-bit exponent is the binary exponent to be applied to the mantissa, + less 63. (Thus an exponent of zero is represented by 0x3F in this field.) + + The mantissa is the most significant 16 bits of the binary fraction, + starting at bit -1 (the "two-to-the-1/2" bit). The effective mantissa + is the represented mantissa, plus 1.0. + + An exponent value of 0x7F combined with a non-zero mantissa is special; + it means that the number is a "not-a-number" value. All incoming C++ NANs + are mapped onto a single NAN value, 0x7F8000. If the mantissa is + zero, then such values represent positive or negative infinity (as + determined by the sign). + + An exponent value of 0x00 is also special. If the mantissa is zero, then + the number represents positive or negative zero (as determined by the + sign). If the mantissa is non-zero, then the effective mantissa is the + represented mantissa (as a fraction in (0, 1.0), without the added 1.0 + of a regular number, scaled by $2^{-62}$. Numbers of this kind are + called _denormals_. + +Returns: + An integer in the range 0..0xFFFFFF. + +Notes: + No errors are possible; all possible float values will result in a + defined result. + +*/ + +#define FUNCTION "AbstractTxBufferBase_t::f2sflt24" + +uint32_t AbstractTxBufferBase_t::f2sflt24(float f) + { + int iExp; + int iOutputExp; + float normalValue; + uint32_t sign; + uint32_t outputFractionLimit; + + // handle NAN + if (std::isnan(f)) + { + // the bits in a NAN mantissa are all "important", and + // we have to throw the bottom bits away. So just change all + // NAN to a common value. + return 0x7F8000u; + } + + sign = 0; + if (std::signbit(f)) + { + // set the "sign bit" of the result + sign = 1u << 23; + } + + if (std::isinf(f)) + { + // infinity maps directly, but has to be handled before we call + // frexpf() + return 0x7F0000 | sign; + } + + // get a normalized value (in [0.5..1), unless denormalized), and exponent. + normalValue = frexpf(f, &iExp); + + if (sign != 0) + // work with the absolute value of normalValue. + normalValue = -normalValue; + + // handle positive and negative zero + if (normalValue == 0.0f) + return sign; + + // abs(f) is in [0..1). + // Output format exponent is 0..0x7F. 0x7F is NaN; 0x7E is max, 0 is min, + // corresponding to 2^63 to 2^-63 times f, but normalized f is in [1..2). + // So output exp is one smaller. + outputFractionLimit = 0x1FFFFu; + iOutputExp = iExp - 1; + if (iOutputExp > 63) + return sign | 0x7F0000u; + else if (iOutputExp < -62) + { + // start denormalizing. + normalValue = ldexp(normalValue, iOutputExp + 62); + iOutputExp = -63; + outputFractionLimit = 0xFFFFu; + } + + // We need to get the top 17 bits of the fraction. The easy way to do this + // is to adjust the exponent again, and then convert to an int. + uint32_t outputFraction = ldexpf(normalValue, 17) + 0.5f; + + // round-off might cause overflow; denorms get handled differently. + if (outputFraction > outputFractionLimit) + { + // handle regular cases. + if (iOutputExp != -63) + { + // shift right and round. + outputFraction = (outputFraction + 1) >> 1; + iOutputExp += 1; + } + // handle denorms. + else + { + // in this case, the nubmer is actually 0x10000; + // so it's no longer a weird nubmer. But we dont' + // want to divide the outputFraction by 1. + ++iOutputExp; + } + } + + if (iOutputExp > 63) + return sign | 0x7F0000u; + + // now, compose the final result. + return (uint32_t)(sign | ((iOutputExp + 63u) << 16) | (outputFraction & 0xFFFFu)); + } + +#undef FUNCTION } // namespace McciCatena From 6f6f8219eb89a12352befa48a32241c175220acc Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 17 Oct 2021 02:23:14 +0200 Subject: [PATCH 2/2] This is v0.22.0-pre1 --- README.md | 5 +++++ src/CatenaBase.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 619ce17..a4a0a86 100644 --- a/README.md +++ b/README.md @@ -1446,6 +1446,11 @@ This sketch demonstrates the use of the Catena FSM class to implement the `Turns ## Release History +- HEAD includes the following changes + + - fix [#319](https://github.com/mcci-catena/Catena-Arduino-Platform/issues/319): accomodate missing two-arg `UsbSerial::begin()` (v0.21.3-pre1). + - fix [#321](https://github.com/mcci-catena/Catena-Arduino-Platform/issues/321): add 24-bit float encoding support (v0.22.0-pre1). + - v0.21.2 includes the following changes, non breaking, all bug fixes. - Check SessionState for validity when fetching ABP info ([#312](https://github.com/mcci-catena/Catena-Arduino-Platform/issues/312), `v0.21.2-pre1`). diff --git a/src/CatenaBase.h b/src/CatenaBase.h index 611a167..889600a 100644 --- a/src/CatenaBase.h +++ b/src/CatenaBase.h @@ -57,7 +57,7 @@ Copyright notice: (((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local)) #define CATENA_ARDUINO_PLATFORM_VERSION \ - CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 21, 3, 1) /* v0.21.3-1 */ + CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 22, 0, 1) /* v0.22.0-pre1 */ #define CATENA_ARDUINO_PLATFORM_VERSION_GET_MAJOR(v) \ (((v) >> 24u) & 0xFFu)