/*
 * VanBus packet receiver for ESP8266 and ESP32
 *
 * Written by Erik Tromp
 *
 * MIT license, all text above must be included in any redistribution.
 */

#include <limits.h>
#include "VanBusRx.h"

// #defines to drive an analyzer pin, for use with e.g. Saleae USB Logic Analyzer.
// What would we want to analyse? Uncomment either or none of the #defines below.
//#define ANALYSE_WAIT_ACK_ISR
//#define ANALYSE_RX_PIN_CHANGE_ISR

#if defined ANALYSE_WAIT_ACK_ISR && defined ANALYSE_RX_PIN_CHANGE_ISR
  #error "Either #define ANALYSE_WAIT_ACK_ISR or #define ANALYSE_RX_PIN_CHANGE_ISR, but not both"
#endif

#if defined ANALYSE_WAIT_ACK_ISR || defined ANALYSE_RX_PIN_CHANGE_ISR
// GPIO pin connected to logic analyser
 #ifdef ARDUINO_ARCH_ESP32
  const int ANALYSER_PIN = GPIO_NUM_5;  // For LilyGO TTGO T7 Mini32 we use IO05 (GPIO 05)
 #else // ! ARDUINO_ARCH_ESP32
  const int ANALYSER_PIN = D8;  // For WEMOS D1 mini board we use D8 (GPIO 15)
 #endif // ARDUINO_ARCH_ESP32
#endif

#if defined ANALYSE_WAIT_ACK_ISR

  #define TRIGGER_ANALYSER_WAIT_ACK_ISR digitalWrite(ANALYSER_PIN, LOW)
  #define RELEASE_ANALYSER_WAIT_ACK_ISR digitalWrite(ANALYSER_PIN, HIGH)
  #define TRIGGER_ANALYSER_RX_PIN_CHANGE_ISR
  #define RELEASE_ANALYSER_RX_PIN_CHANGE_ISR

#elif defined ANALYSE_RX_PIN_CHANGE_ISR

  #define TRIGGER_ANALYSER_WAIT_ACK_ISR
  #define RELEASE_ANALYSER_WAIT_ACK_ISR
  #define TRIGGER_ANALYSER_RX_PIN_CHANGE_ISR digitalWrite(ANALYSER_PIN, LOW)
  #define RELEASE_ANALYSER_RX_PIN_CHANGE_ISR digitalWrite(ANALYSER_PIN, HIGH)

#else

  #define TRIGGER_ANALYSER_WAIT_ACK_ISR
  #define RELEASE_ANALYSER_WAIT_ACK_ISR
  #define TRIGGER_ANALYSER_RX_PIN_CHANGE_ISR
  #define RELEASE_ANALYSER_RX_PIN_CHANGE_ISR

#endif // defined ANALYSE_WAIT_ACK_ISR || defined ANALYSE_RX_PIN_CHANGE_ISR

static const uint16_t VAN_CRC_POLYNOM = 0x0F9D;

#define VAN_CRC_TABLE_SIZE 256
static uint16_t crcTable[VAN_CRC_TABLE_SIZE] = 
{
    0x0000, 0x0F9D, 0x1F3A, 0x10A7, 0x3E74, 0x31E9, 0x214E, 0x2ED3,
    0x7CE8, 0x7375, 0x63D2, 0x6C4F, 0x429C, 0x4D01, 0x5DA6, 0x523B,
    0x764D, 0x79D0, 0x6977, 0x66EA, 0x4839, 0x47A4, 0x5703, 0x589E,
    0x0AA5, 0x0538, 0x159F, 0x1A02, 0x34D1, 0x3B4C, 0x2BEB, 0x2476,
    0x6307, 0x6C9A, 0x7C3D, 0x73A0, 0x5D73, 0x52EE, 0x4249, 0x4DD4,
    0x1FEF, 0x1072, 0x00D5, 0x0F48, 0x219B, 0x2E06, 0x3EA1, 0x313C,
    0x154A, 0x1AD7, 0x0A70, 0x05ED, 0x2B3E, 0x24A3, 0x3404, 0x3B99,
    0x69A2, 0x663F, 0x7698, 0x7905, 0x57D6, 0x584B, 0x48EC, 0x4771,
    0x4993, 0x460E, 0x56A9, 0x5934, 0x77E7, 0x787A, 0x68DD, 0x6740,
    0x357B, 0x3AE6, 0x2A41, 0x25DC, 0x0B0F, 0x0492, 0x1435, 0x1BA8,
    0x3FDE, 0x3043, 0x20E4, 0x2F79, 0x01AA, 0x0E37, 0x1E90, 0x110D,
    0x4336, 0x4CAB, 0x5C0C, 0x5391, 0x7D42, 0x72DF, 0x6278, 0x6DE5,
    0x2A94, 0x2509, 0x35AE, 0x3A33, 0x14E0, 0x1B7D, 0x0BDA, 0x0447,
    0x567C, 0x59E1, 0x4946, 0x46DB, 0x6808, 0x6795, 0x7732, 0x78AF,
    0x5CD9, 0x5344, 0x43E3, 0x4C7E, 0x62AD, 0x6D30, 0x7D97, 0x720A,
    0x2031, 0x2FAC, 0x3F0B, 0x3096, 0x1E45, 0x11D8, 0x017F, 0x0EE2,
    0x1CBB, 0x1326, 0x0381, 0x0C1C, 0x22CF, 0x2D52, 0x3DF5, 0x3268,
    0x6053, 0x6FCE, 0x7F69, 0x70F4, 0x5E27, 0x51BA, 0x411D, 0x4E80,
    0x6AF6, 0x656B, 0x75CC, 0x7A51, 0x5482, 0x5B1F, 0x4BB8, 0x4425,
    0x161E, 0x1983, 0x0924, 0x06B9, 0x286A, 0x27F7, 0x3750, 0x38CD,
    0x7FBC, 0x7021, 0x6086, 0x6F1B, 0x41C8, 0x4E55, 0x5EF2, 0x516F,
    0x0354, 0x0CC9, 0x1C6E, 0x13F3, 0x3D20, 0x32BD, 0x221A, 0x2D87,
    0x09F1, 0x066C, 0x16CB, 0x1956, 0x3785, 0x3818, 0x28BF, 0x2722,
    0x7519, 0x7A84, 0x6A23, 0x65BE, 0x4B6D, 0x44F0, 0x5457, 0x5BCA,
    0x5528, 0x5AB5, 0x4A12, 0x458F, 0x6B5C, 0x64C1, 0x7466, 0x7BFB,
    0x29C0, 0x265D, 0x36FA, 0x3967, 0x17B4, 0x1829, 0x088E, 0x0713,
    0x2365, 0x2CF8, 0x3C5F, 0x33C2, 0x1D11, 0x128C, 0x022B, 0x0DB6,
    0x5F8D, 0x5010, 0x40B7, 0x4F2A, 0x61F9, 0x6E64, 0x7EC3, 0x715E,
    0x362F, 0x39B2, 0x2915, 0x2688, 0x085B, 0x07C6, 0x1761, 0x18FC,
    0x4AC7, 0x455A, 0x55FD, 0x5A60, 0x74B3, 0x7B2E, 0x6B89, 0x6414,
    0x4062, 0x4FFF, 0x5F58, 0x50C5, 0x7E16, 0x718B, 0x612C, 0x6EB1,
    0x3C8A, 0x3317, 0x23B0, 0x2C2D, 0x02FE, 0x0D63, 0x1DC4, 0x1259,
};
// Above table is generated by:
//
// void _initCrcTable()
// {
//   #define VAN_CRC_LENGTH 15
//     const uint16_t mask = (1 << (VAN_CRC_LENGTH - 1));
// 
//     for (int i = 0; i < VAN_CRC_TABLE_SIZE; i++)
//     {
//         uint16_t crc = i << 7;
//         for (uint8_t bit = 0; bit < 8; bit++)
//         {
//             if (crc & mask) crc = (crc << 1) ^ VAN_CRC_POLYNOM; else crc <<= 1;
//         }
//         crcTable[i] = crc & 0x7FFF;
//     } // for
// } // _initCrcTable
//
// See also: https://github.com/0xCAFEDECAF/VanBus/blob/0ef35582dbcc6809175b3e11802e1eb84c561fb2/VanBusRx.cpp#L30
//

uint16_t _crc(const uint8_t bytes[], int size)
{
    uint16_t crc15 = 0x7FFF;

    for (int i = 1; i < size - 2; i++)  // Skip first byte (SOF, 0x0E) and last 2 (CRC)
    {
        uint8_t byte = bytes[i];

        // XOR-in next input byte into MSB of crc, that's our new intermediate divident
        uint8_t pos = (uint8_t)( (crc15 >> 7) ^ byte);

        // Shift out the MSB used for division per lookup table and XOR with the remainder
        crc15 = (uint16_t)((crc15 << 8) ^ (uint16_t)(crcTable[pos]));
    } // for

    crc15 ^= 0x7FFF;
    crc15 <<= 1;  // Shift left 1 bit to turn 15 bit result into 16 bit representation

    return crc15;
} // _crc

// Returns the Flags field of a VAN packet
uint8_t TVanPacketRxDesc::CommandFlags() const
{
    // Bits:
    // 3 : always 1
    // 2 (Request AcK, RAK) : 1 = requesting ack; 0 = no ack requested
    // 1 (Read/Write, R/W) : 1 = read; 0 = write
    // 0 (Remote Transmission Request, RTR; only when R/W == 1) : 1 = request for in-frame response
    return bytes[2] & 0x0F;
} // TVanPacketRxDesc::Flags

// Returns a pointer to the data bytes of a VAN packet
const uint8_t* TVanPacketRxDesc::Data() const
{
    return bytes + 3;
} // TVanPacketRxDesc::Data

// Returns the data length of a VAN packet
int TVanPacketRxDesc::DataLen() const
{
    // Total size minus SOF (1 byte), IDEN (1.5 bytes), COM (0.5 bytes) and CRC + EOD (2 bytes)
    return size - 5;
} // TVanPacketRxDesc::DataLen

// Calculates the CRC of a VAN packet
uint16_t TVanPacketRxDesc::Crc() const
{
    return _crc(bytes, size);
} // TVanPacketRxDesc::Crc

// Checks the CRC value of a VAN packet
bool TVanPacketRxDesc::CheckCrc() const
{
    uint16_t crc15 = 0x7FFF;

    for (int i = 1; i < size; i++)  // Skip first byte (SOF, 0x0E)
    {
        uint8_t byte = bytes[i];

        // XOR-in next input byte into MSB of crc, that's our new intermediate divident
        uint8_t pos = (uint8_t)( (crc15 >> 7) ^ byte);

        // Shift out the MSB used for division per lookup table and XOR with the remainder
        crc15 = (uint16_t)((crc15 << 8) ^ (uint16_t)(crcTable[pos]));
    } // for

    crc15 &= 0x7FFF;

    // Packet is OK if crc15 == 0x19B7
    return crc15 == 0x19B7;
} // TVanPacketRxDesc::CheckCrc

// Checks the CRC value of a VAN packet. If OK, increases the "repair" counters.
bool TVanPacketRxDesc::CheckCrcFix(bool mustCount, uint32_t* pCounter1, uint32_t* pCounter2)
{
    if (CheckCrc())
    {
        if (mustCount)
        {
            // Increase general counters
            VanBusRx.nCorrupt++;
            VanBusRx.nRepaired++;

            // Increase specific counter(s)
            (*pCounter1)++;
            if (pCounter2) (*pCounter2)++;
        } // if
        return true;
    } // if
    return false;
} // TVanPacketRxDesc::CheckCrcFix

// CRC packet errors which can be fixed by inserting an extra bit indicate that the bit time measurements can be
// extended somewhat.
#ifdef ARDUINO_ARCH_ESP32
 #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
  static long int addToBitTime = CPU_CYCLES(12);
 #else // ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 0)
  static long int addToBitTime = CPU_CYCLES(6);
 #endif
#else // ! ARDUINO_ARCH_ESP32
 static long int addToBitTime = CPU_CYCLES(0);
#endif

// Checks the CRC value of a VAN packet. If not, tries to repair it by flipping each bit.
// Yes, we can sometimes repair a corrupt packet by flipping one or two bits :-)
// Optional parameter 'wantToCount' is a pointer-to-method of class TVanPacketRxDesc, returning a boolean.
// It can be used to limit the repair statistics to take only specific types of packets into account.
//
// Examples of invocation:
//
//   if (! pkt.CheckCrcAndRepair()) return -1; // Unrecoverable CRC error
//   if (! pkt.CheckCrcAndRepair(&TVanPacketRxDesc::IsSatnavPacket)) return -1; // Unrecoverable CRC error
//
// Note: let's keep the counters sane by calling this only once.
bool TVanPacketRxDesc::CheckCrcAndRepair(bool (TVanPacketRxDesc::*wantToCount)() const)
{
    uint8_t lastBit = bytes[size - 1] & 0x01;

    bytes[size - 1] &= 0xFE;  // Last bit of last byte (LSB of CRC) is always 0

    bool mustCount = wantToCount == 0 || (this->*wantToCount)();
    if (mustCount) VanBusRx.nCountedForRepair++;

    if (CheckCrc())
    {
        // Is flipping the last bit fixing the packet?
        if (lastBit == 0x01 && mustCount)
        {
            VanBusRx.nCorrupt++;
            VanBusRx.nRepaired++;
            VanBusRx.nOneBitErrors++;
        } // if
        return true;
    } // if

    if (lastBit != 0x01)
    {
        // Try to shift one bit to the right, starting from the last byte backwards

        // Typically happens with the following packets:
        // - Before fix: 0E 5E4 C (WA0) 00-EE:2E-E0 - CRC_ERROR
        // - After fix:  0E 5E4 C (WA0) 00-FF:1F-F8 - CRC_OK
        // In this aspect, these packets could be considered "trainer" packets.

        // Save a copy
        uint8_t savedBytes[VAN_MAX_PACKET_SIZE];
        memcpy(savedBytes, bytes, size);

        // Byte 0 can be skipped; it does not count for CRC
        for (int atByte = size - 1; atByte > 0; atByte--)
        {
            for (int atBit = 1; atBit < 8; atBit++)
            {
                                                   // Examples for 'atBit = 8':
                uint16_t rMask = 1 << atBit;       // 1 0000 0000
                uint8_t wMask = rMask >> 1;        //   1000 0000
                uint8_t invMask = ~wMask;          //   0111 1111

                uint16_t b = bytes[atByte];        //   1110 1111
                uint16_t wBit = b & wMask;         //   1000 0000

                if (atBit == 4) b = ~b;
                uint16_t rBit = (b & rMask) >> 1;

                if (rBit == wBit) continue;

                // Insert a bit at 'atBit'.
                // The inserted bit is the bit just in front of it, or its inverse at bit 3 or 7.
                // Examples:
                // EE = 1110 1110 --> 1110 1111 --> 1111 1111 = FF
                // 2E = 0010 1110 --> 0010 1111 --> 0001 1111 = 1F
                // E0 = 1110 0000 --> 1110 1000 --> 1111 1000 = F8
                // 11 = 0001 0001 --> 0001 0000 --> 0000 0000 = 00
                bytes[atByte] = (bytes[atByte] & invMask) | rBit;

                if (CheckCrcFix(mustCount, &VanBusRx.nBitDeletionErrors))
                {
                    addToBitTime += CPU_CYCLES(4);
                    if (addToBitTime > CPU_CYCLES(20)) addToBitTime = CPU_CYCLES(20);
                    return true;
                } // if
            } // for
        } // for

        // Restore the copy
        memcpy(bytes, savedBytes, size);
    } // if

    if (uncertainBit1 != NO_UNCERTAIN_BIT)
    {
        // Flip the bit which is at the position that is marked as "uncertain"

        int uncertainAtByte = (uncertainBit1 - 1) >> 3;

        int uncertainAtBit = (uncertainBit1 - 1) & 0x07;  // 0 = MSB, 7 = LSB
        uncertainAtBit = 7 - uncertainAtBit;  // 0 = LSB, 7 = MSB

        uint8_t uncertainMask = 1 << uncertainAtBit;
        bytes[uncertainAtByte] ^= uncertainMask;  // Flip

        if (CheckCrcFix(mustCount, &VanBusRx.nOneBitErrors, &VanBusRx.nUncertainBitErrors)) return true;

        bytes[uncertainAtByte] ^= uncertainMask;  // Flip back
    } // if

    // One cycle without the uncertain bit flipped, plus (optionally) one cycle with the uncertain bit flipped
    for (int i = 0; i < (uncertainBit1 == NO_UNCERTAIN_BIT ? 1 : 2); i++)
    {
        int uncertainAtByte = 0;
        uint8_t uncertainMask = 0;

        // Second cycle?
        if (i == 1)
        {
            // Flip the bit which is at the position that is marked as "uncertain"

            uncertainAtByte = (uncertainBit1 - 1) >> 3;

            int uncertainAtBit = (uncertainBit1 - 1) & 0x07;  // 0 = MSB, 7 = LSB
            uncertainAtBit = 7 - uncertainAtBit;  // 0 = LSB, 7 = MSB

            uncertainMask = 1 << uncertainAtBit;
            bytes[uncertainAtByte] ^= uncertainMask;  // Flip
        } // if

        // Byte 0 can be skipped; it does not count for CRC
        for (int atByte = 1; atByte < size; atByte++)
        {
            for (int atBit = 0; atBit < 8; atBit++)
            {
                uint8_t mask = 1 << atBit;
                bytes[atByte] ^= mask;  // Flip

                // TODO - is there a way to quickly re-calculate the CRC value when bit is flipped?

                if (CheckCrcFix(mustCount, &VanBusRx.nOneBitErrors))
                {
                    if (i == 1) VanBusRx.nUncertainBitErrors++;
                    return true;
                } // if

                // Try also to flip the preceding bit
                if (atBit != 7)
                {
                    uint8_t mask2 = 1 << (atBit + 1);
                    bytes[atByte] ^= mask2;  // Flip

                    if (CheckCrcFix(mustCount, &VanBusRx.nTwoConsecutiveBitErrors))
                    {
                        if (i == 1) VanBusRx.nUncertainBitErrors++;
                        return true;
                    } // if

                    bytes[atByte] ^= mask2;  // Flip back
                }
                else // atBit == 7
                {
                    // atByte > 0, so atByte - 1 is safe
                    bytes[atByte - 1] ^= 1 << 0;  // Flip

                    if (CheckCrcFix(mustCount, &VanBusRx.nTwoConsecutiveBitErrors))
                    {
                        if (i == 1) VanBusRx.nUncertainBitErrors++;
                        return true;
                    } // if

                    bytes[atByte - 1] ^= 1 << 0;  // Flip back
                } // if

                bytes[atByte] ^= mask;  // Flip back
            } // for
        } // for

        if (i == 1) bytes[uncertainAtByte] ^= uncertainMask;  // Flip back
    } // for

    // Flip two bits. Getting to this point happens very rarely, luckily...

    bool prevBit1 = false;

    for (int atByte1 = 1; atByte1 < size; atByte1++)
    {
        for (int atBit1 = 7; atBit1 >= 0; atBit1--)
        {
            // Only flip the last bit in a sequence of equal bits; take into account the Manchester bits

            uint8_t currMask1 = 1 << atBit1;
            bool currBit1 = (bytes[atByte1] & currMask1) != 0;
            bool skip1 = prevBit1 != currBit1;

            // After bit 4 or bit 0, there was the Manchester bit
            if (atBit1 == 4 || atBit1 == 0)
            {
                prevBit1 = ! currBit1;
            }
            else
            {
                prevBit1 = currBit1;
                uint8_t nextMask1 = 1 << (atBit1 - 1);
                bool nextBit1 = (bytes[atByte1] & nextMask1) != 0;
                skip1 = skip1 || (currBit1 == nextBit1);
            } // if

            if (skip1) continue;

            bytes[atByte1] ^= currMask1;  // Flip

            // Flip second bit

            bool prevBit2 = false;

            for (int atByte2 = atByte1; atByte2 < size; atByte2++)
            {
                for (int atBit2 = 7; atBit2 >= 0; atBit2--)
                {
                    // Only flip the last bit in a sequence of equal bits; take into account the Manchester bits

                    uint8_t currMask2 = 1 << atBit2;
                    bool currBit2 = (bytes[atByte2] & currMask2) != 0;
                    bool skip2 = prevBit2 != currBit2;

                    // After bit 4 or bit 0, there was the Manchester bit
                    if (atBit2 == 4 || atBit2 == 0)
                    {
                        prevBit2 = ! currBit2;
                    }
                    else
                    {
                        prevBit2 = currBit2;
                        uint8_t nextMask2 = 1 << (atBit2 - 1);
                        bool nextBit2 = (bytes[atByte2] & nextMask2) != 0;
                        skip2 = skip2 || (currBit2 == nextBit2);
                    } // if

                    if (skip2) continue;

                    bytes[atByte2] ^= currMask2;  // Flip

                    if (CheckCrcFix(mustCount, &VanBusRx.nTwoSeparateBitErrors)) return true;

                    bytes[atByte2] ^= currMask2;  // Flip back
                } // for
            } // for

            bytes[atByte1] ^= currMask1;  // Flip back
        } // for
    } // for

    if (mustCount) VanBusRx.nCorrupt++;

    return false;
} // TVanPacketRxDesc::CheckCrcAndRepair

// Dumps the raw packet bytes to a stream (e.g. 'Serial').
// Optionally specify the last character; default is "\n" (newline).
// If the last character is "\n", will also print the ASCII representation of each byte (if possible).
void TVanPacketRxDesc::DumpRaw(Stream& s, char last) const
{
    s.printf("Raw: #%04" PRIu32 " (%*" PRIu16 "/%d) %2d(%2d) ",
        seqNo % 10000,
        VanBusRx.size > 100 ? 3 : VanBusRx.size > 10 ? 2 : 1,
        slot + 1,
        VanBusRx.size,
        size - 5 < 0 ? 0 : size - 5,
        size);

    if (size >= 1) s.printf("%02" PRIX8 " ", bytes[0]);  // SOF
    if (size >= 3) s.printf("%03" PRIX16 " %1" PRIX8 " (%s) ", Iden(), CommandFlags(), CommandFlagsStr());

    for (int i = 3; i < size; i++) s.printf("%02" PRIX8 "%c", bytes[i], i == size - 3 ? ':' : i < size - 1 ? '-' : ' ');

    s.print(AckStr());
    s.print(" ");
    s.print(ResultStr());
    s.printf(" %04X", Crc());
    s.printf(" %s", CheckCrc() ? "CRC_OK" : "CRC_ERROR");

    if (uncertainBit1 != NO_UNCERTAIN_BIT) s.printf(" uBit=%d", uncertainBit1);

    if (last == '\n')
    {
        // Print also ASCII character representation of each byte, if possible, otherwise a small center-dot

        s.printf("\n%*s", VanBusRx.size > 100 ? 43 : VanBusRx.size > 10 ? 41 : 39, " ");
        for (int i = 3; i < size - 2; i++)
        {
            if (bytes[i] >= 0x20 && bytes[i] <= 0x7E) s.printf("%2c ", bytes[i]); else s.print(" \u00b7 ");
        } // for
    } // if

    s.print(last);
} // TVanPacketRxDesc::DumpRaw

// Normal bit time (8 microseconds), expressed as number of CPU cycles
#define VAN_NORMAL_BIT_TIME_CPU_CYCLES (CPU_CYCLES(667))

inline __attribute__((always_inline)) unsigned int nBits(uint32_t nCycles)
{
    return (nCycles + CPU_CYCLES(200)) / VAN_NORMAL_BIT_TIME_CPU_CYCLES;
} // nBits

uint32_t averageOneBitTime = 0;

// Calculate number of bits from a number of elapsed CPU cycles
inline __attribute__((always_inline)) unsigned int nBitsTakingIntoAccountJitter(uint32_t nCycles, uint32_t& jitter)
{
    // Here is the heart of the machine; lots of voodoo magic here...

    // Theory:
    // - VAN bus rate = 125 kbit/sec = 125 000 bits/sec
    //   1 bit = 1/125000 = 0.000008 sec = 8.0 usec
    // - CPU rate is 80 MHz
    //   1 cycle @ 80 MHz = 0.0000000125 sec = 0.0125 usec
    // --> So, 1 VAN-bus bit is 8.0 / 0.0125 = 640 cycles

    // Sometimes, samples are stretched, because the ISR is called too late: ESP8266 interrupt service latency can
    // vary. If that happens, we must compress the "sample time" for the next bit.

    // All timing values were found by trial and error
    jitter = 0;

    if (nCycles < CPU_CYCLES(555))
    {
        if (nCycles > CPU_CYCLES(112)) jitter = nCycles - CPU_CYCLES(112);
        return 0;
    } // if

  #define ONE_BIT_BOUNDARY CPU_CYCLES(1281)
    if (nCycles < ONE_BIT_BOUNDARY)
    {
        if (nCycles > CPU_CYCLES(712)) jitter = nCycles - CPU_CYCLES(712);  // 712 --> 1281 = 569
        return 1;
    } // if

    if (nCycles < CPU_CYCLES(1863))
    {
        if (nCycles > CPU_CYCLES(1349)) jitter = nCycles - CPU_CYCLES(1349);  // 1349 --> 1863 = 514
        return 2;
    } // if

  #define THREE_BIT_BOUNDARY CPU_CYCLES(2500)
    if (nCycles < THREE_BIT_BOUNDARY)
    {
        if (nCycles > CPU_CYCLES(1998)) jitter = nCycles - CPU_CYCLES(1998);  // 1998 --> 2500 = 502
        return 3;
    } // if

    if (nCycles < CPU_CYCLES(3166))
    {
        if (nCycles > CPU_CYCLES(2636)) jitter = nCycles - CPU_CYCLES(2636);  // 2636 --> 3166 = 530
        return 4;
    } // if

    if (nCycles < CPU_CYCLES(3819))
    {
        if (nCycles > CPU_CYCLES(3262)) jitter = nCycles - CPU_CYCLES(3262);  // 3262 --> 3819 = 557
        return 5;
    } // if

    // We hardly ever get to this point
    if (nCycles < CPU_CYCLES(4468))
    {
        if (nCycles > CPU_CYCLES(3930)) jitter = nCycles - CPU_CYCLES(3930);  // 3930 --> 4468 = 538
        return 6;
    } // if

    const unsigned int _nBits = nBits(nCycles);
    if (nCycles > _nBits * VAN_NORMAL_BIT_TIME_CPU_CYCLES) jitter = nCycles - _nBits * VAN_NORMAL_BIT_TIME_CPU_CYCLES;

    return _nBits;
} // nBitsTakingIntoAccountJitter

#ifdef ARDUINO_ARCH_ESP32

  hw_timer_t* timer = NULL;
  extern hw_timer_t* txTimer;

  #define RX_TIMER_FREQ (5000000)  // In Hz, must multiple of 1000000
  #define RX_TIMER_DIVIDER (TIMER_BASE_CLK / RX_TIMER_FREQ) // TIMER_BASE_CLK = 80000000, regardless of chosen CPU freq
  #define RX_TIMER_TICKS_PER_MICROSECOND (RX_TIMER_FREQ / 1000000)

#endif // ARDUINO_ARCH_ESP32

void IRAM_ATTR SetTxBitTimer()
{
  #ifdef ARDUINO_ARCH_ESP8266
    timer1_disable();
  #endif // ARDUINO_ARCH_ESP8266

    if (VanBusRx.txTimerIsr)
    {
        // Turn on the Tx bit timer

      #ifdef ARDUINO_ARCH_ESP32

       #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
        timerAlarm(txTimer, VanBusRx.txTimerTicks, true, 0);
       #else // ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
       #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
        timerAlarmDisable(txTimer);
        timerDetachInterrupt(txTimer);
       #endif
        timerAttachInterrupt(txTimer, VanBusRx.txTimerIsr, true);
        timerAlarmWrite(txTimer, VanBusRx.txTimerTicks, true);
       #if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 0)
        timerAlarmEnable(txTimer);
       #endif
       #endif // ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
        timerStart(txTimer);

      #else // ! ARDUINO_ARCH_ESP32

        timer1_attachInterrupt(VanBusRx.txTimerIsr);

        // Clock to timer (prescaler) is always 80 MHz, even if F_CPU is 160 MHz
        timer1_enable(TIMER_DIVIDER, TIM_EDGE, TIM_LOOP);

        timer1_write(VanBusRx.txTimerTicks);

      #endif // ARDUINO_ARCH_ESP32

    } // if
} // SetTxBitTimer

// If the timeout expires, the packet is VAN_RX_DONE. 'ack' has already been initially set to VAN_NO_ACK,
// and then to VAN_ACK if a new bit was received within the time-out period.
void IRAM_ATTR WaitAckIsr()
{
    RELEASE_ANALYSER_WAIT_ACK_ISR;

    SetTxBitTimer();

    NO_INTERRUPTS;
    if (VanBusRx._head->state == VAN_RX_WAITING_ACK) VanBusRx._AdvanceHead();
    INTERRUPTS;
} // WaitAckIsr

// Pin level change interrupt handler
void IRAM_ATTR RxPinChangeIsr()
{
    // Pin levels

    // The logic is:
    // - if pinLevel == VAN_LOGICAL_HIGH, we've just had a series of VAN_LOGICAL_LOW bits.
    // - if pinLevel == VAN_LOGICAL_LOW, we've just had a series of VAN_LOGICAL_HIGH bits.

  #ifdef ARDUINO_ARCH_ESP32
    #define GPIP(X_) digitalRead(X_)
  #endif // ARDUINO_ARCH_ESP32

    const int pinLevel = GPIP(VanBusRx.pin);
    static int prevPinLevel = VAN_BIT_RECESSIVE;
    static bool pinLevelChangedDuringInterruptHandling = false;
    (void)pinLevelChangedDuringInterruptHandling;  // Prevent compiler warning "variable set but not used"

    // Number of elapsed CPU cycles
    static uint32_t prev = 0;
    const uint32_t curr = ESP.getCycleCount();  // Store CPU cycle counter value as soon as possible

    TRIGGER_ANALYSER_RX_PIN_CHANGE_ISR;

    const uint32_t nCyclesMeasured = curr - prev;  // Arithmetic has safe roll-over
    prev = curr;

    const bool samePinLevel = (pinLevel == prevPinLevel);

    // Prevent CPU monopolization by noise on bus
    static int noiseCounter = 0;
    if (nCyclesMeasured < 510 || samePinLevel)
    {
        if (++noiseCounter > 30)
        {
            VanBusRx.Disable();
            RELEASE_ANALYSER_RX_PIN_CHANGE_ISR;
            return;
        } // if
    }
    else
    {
        noiseCounter = 0;
    } // if

    // Retrieve context
    TVanPacketRxDesc* rxDesc = VanBusRx._head;
    const PacketReadState_t state = rxDesc->state;

    // Conversion from elapsed CPU cycles to number of bits, including built-up jitter
    static uint32_t jitter = 0;
    uint32_t nCycles = nCyclesMeasured + jitter;

    nCycles += addToBitTime;

    // Experiment

    if (nCyclesMeasured > CPU_CYCLES(600) && nCyclesMeasured < CPU_CYCLES(800))
    {
        averageOneBitTime = averageOneBitTime == 0 ? CPU_CYCLES(700) : (averageOneBitTime * 99 + nCyclesMeasured + 50) / 100;
    } // if

  #if 0
    if (averageOneBitTime > CPU_CYCLES(660))
    {
        if (averageOneBitTime < CPU_CYCLES(693))
        {
            //nCycles = (4 * nCycles + CPU_CYCLES(700) - averageOneBitTime + 2) / 4;
            nCycles += CPU_CYCLES(5);
        }
        else if (averageOneBitTime > CPU_CYCLES(714))
        {
            nCycles -= CPU_CYCLES(10);
        } // if
    } // if
  #endif

    if (state == VAN_RX_VACANT || state == VAN_RX_SEARCHING)
    {
        // During SOF, timing is slightly different. Timing values were found by trial and error.

      #define FOUR_BITS THREE_BIT_BOUNDARY + CPU_CYCLES(10)
        if (nCycles > CPU_CYCLES(2284) && nCycles < FOUR_BITS) nCycles = FOUR_BITS;
        //else if (nCycles > CPU_CYCLES(600) && nCycles < CPU_CYCLES(800)) nCycles -= CPU_CYCLES(20);
        else if (nCycles > CPU_CYCLES(1100) && nCycles < ONE_BIT_BOUNDARY) nCycles -= CPU_CYCLES(20);
    }
    else
    {
        // Sometimes, one long bit is in fact two bits

        if (jitter < CPU_CYCLES(300))
        {
          #define MOVE_TOWARDS_TWO_BITS_AT CPU_CYCLES(1229)
          #define MOVE_TOWARDS_TWO_BITS ONE_BIT_BOUNDARY - MOVE_TOWARDS_TWO_BITS_AT
            if (nCyclesMeasured > CPU_CYCLES(987) && nCyclesMeasured < ONE_BIT_BOUNDARY && nCycles >= CPU_CYCLES(1220))
            {
                nCycles += MOVE_TOWARDS_TWO_BITS;
            } // if
        }
        else if (jitter < CPU_CYCLES(400))
        {
            if (nCyclesMeasured > CPU_CYCLES(900) && nCyclesMeasured < CPU_CYCLES(988))
            {
                nCycles += CPU_CYCLES(22);
            } // if
        } // if
    } // if

    const uint32_t prevJitter = jitter;

    unsigned int nBits = nBitsTakingIntoAccountJitter(nCycles, jitter);

    // Experiment
  #define SMALLEST_JITTER CPU_CYCLES(20)
    if (jitter < SMALLEST_JITTER)
    {
        jitter = 0;
    }
    else if (jitter <= CPU_CYCLES(200))
    {
      #define SMALL_JITTER_RUNDOWN SMALLEST_JITTER
        if (jitter > prevJitter - CPU_CYCLES(15) && jitter < prevJitter + CPU_CYCLES(18)) jitter -= SMALL_JITTER_RUNDOWN;
    }
    else
    {
      #define LARGE_JITTER_RUNDOWN CPU_CYCLES(30)
        if (jitter > prevJitter - CPU_CYCLES(30) && jitter < prevJitter + CPU_CYCLES(5)) jitter -= LARGE_JITTER_RUNDOWN;
    } // if

  #ifdef VAN_RX_ISR_DEBUGGING

    // Record some data to be used for debugging outside this ISR

    TIsrDebugPacket* isrDebugPacket = rxDesc->isrDebugPacket;

    isrDebugPacket->slot = rxDesc->slot;

    TIsrDebugData* debugIsr =
        isrDebugPacket->at < VAN_ISR_DEBUG_BUFFER_SIZE ?
            isrDebugPacket->samples + isrDebugPacket->at :
            NULL;

    // Only write into sample buffer if there is space
    if (debugIsr != NULL)
    {
        debugIsr->nCyclesMeasured = _min(nCyclesMeasured / CPU_F_FACTOR, USHRT_MAX);
        debugIsr->fromJitter = _min(prevJitter / CPU_F_FACTOR, (1 << 10) - 1);
        debugIsr->nBits = _min(nBits, UCHAR_MAX);
        debugIsr->prevPinLevel = prevPinLevel;
        debugIsr->pinLevel = pinLevel;
        debugIsr->fromState = state;
        debugIsr->readBits = 0;
    } // if

    // Macros useful for debugging

    // Just before returning from this ISR, record the pin level, plus some data for debugging
    #define RETURN \
    { \
        const int pinLevelAtReturnFromIsr = GPIP(VanBusRx.pin); \
        pinLevelChangedDuringInterruptHandling = jitter < CPU_CYCLES(100) && pinLevelAtReturnFromIsr != pinLevel; \
        \
        if (debugIsr != NULL) \
        { \
            debugIsr->toJitter = _min(jitter / CPU_F_FACTOR, (1 << 10) - 1); \
            debugIsr->flipBits = flipBits; \
            debugIsr->toState = rxDesc->state; \
            debugIsr->pinLevelAtReturnFromIsr = pinLevelAtReturnFromIsr; \
            debugIsr->atBit = atBit; \
            isrDebugPacket->at++; \
        } \
        RELEASE_ANALYSER_RX_PIN_CHANGE_ISR; \
        return; \
    }

    #define DEBUG_ISR(TO_, FROM_) if (debugIsr != NULL) debugIsr->TO_ = (FROM_);
    #define DEBUG_ISR_M(TO_, FROM_, MAX_) if (debugIsr != NULL) debugIsr->TO_ = _min((FROM_), (MAX_));

  #else

    // Just before returning from this ISR, record the pin level
    #define RETURN \
    { \
        const int pinLevelAtReturnFromIsr = GPIP(VanBusRx.pin); \
        pinLevelChangedDuringInterruptHandling = jitter < CPU_CYCLES(100) && pinLevelAtReturnFromIsr != pinLevel; \
        RELEASE_ANALYSER_RX_PIN_CHANGE_ISR; \
        return; \
    }

    #define DEBUG_ISR(TO_, FROM_)
    #define DEBUG_ISR_M(TO_, FROM_, MAX_)

  #endif // VAN_RX_ISR_DEBUGGING

    prevPinLevel = pinLevel;

    uint16_t flipBits = 0;

    // Media access detection for packet transmission
    if (pinLevel == VAN_BIT_RECESSIVE)
    {
        // Pin level just changed to 'recessive', so that was the end of the media access ('dominant')
        VanBusRx.lastMediaAccessAt = curr;
    } // if

    static unsigned int atBit = 0;
    static uint16_t readBits = 0;

  #ifdef VAN_RX_IFS_DEBUGGING

    TIfsDebugPacket* ifsDebugPacket = &rxDesc->ifsDebugPacket;

    TIfsDebugData* debugIfs =
        ifsDebugPacket->at < VAN_IFS_DEBUG_BUFFER_SIZE ?
            ifsDebugPacket->samples + ifsDebugPacket->at :
            NULL;

    if (state == VAN_RX_VACANT || state == VAN_RX_SEARCHING)
    {
        // Only write into sample buffer if there is space
        if (debugIfs != NULL)
        {
            debugIfs->nCyclesMeasured = _min(nCyclesMeasured / CPU_F_FACTOR, USHRT_MAX);
            debugIfs->nBits = _min(nBits, UCHAR_MAX);
            debugIfs->pinLevel = pinLevel;
            debugIfs->fromState = state;
            debugIfs->toState = state;  // Can be overwritten later
            ifsDebugPacket->at++;
        } // if
    } // if

    // Macro useful for debugging
    #define DEBUG_IFS(TO_, FROM_) if (debugIfs != NULL) debugIfs->TO_ = (FROM_);

  #else

    #define DEBUG_IFS(TO, FROM)

  #endif // VAN_RX_IFS_DEBUGGING

    if (state == VAN_RX_WAITING_ACK)
    {
        if (
            // If another bit came after the "ACK", it is not an "ACK" but the first "1" bit of the next byte
            (rxDesc->ack == VAN_ACK && pinLevel == VAN_LOGICAL_LOW)

            // If the "ACK" came too soon, it is not an "ACK" but the first "1" bit of the next byte
            || nCycles < CPU_CYCLES(650)

            // If the "ACK" lasted more than 1 time slot, it is not an "ACK" but the first "1" bit of the next byte

          #ifdef ARDUINO_ARCH_ESP32
           #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
            #define ACK_TOO_LONG_AFTER (1400)
           #elif ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
            #define ACK_TOO_LONG_AFTER (1700)
           #else
            #define ACK_TOO_LONG_AFTER (1400)
           #endif
          #else
            #define ACK_TOO_LONG_AFTER (1000)
          #endif
            || nCycles > CPU_CYCLES(ACK_TOO_LONG_AFTER)

          #ifdef ARDUINO_ARCH_ESP8266
            || pinLevelChangedDuringInterruptHandling
          #endif
           )
        {
          #ifdef ARDUINO_ARCH_ESP8266
            timer1_disable();  // TODO - this can be removed? Seems useless.
          #endif // ARDUINO_ARCH_ESP8266

            // Go back to state VAN_RX_LOADING
            rxDesc->state = VAN_RX_LOADING;
            DEBUG_IFS(toState, VAN_RX_LOADING);
        }
        else
        {
            rxDesc->ack = VAN_ACK;

            RELEASE_ANALYSER_WAIT_ACK_ISR;

            // The timer ISR 'WaitAckIsr' will call 'VanBusRx._AdvanceHead()'
        } // if
    } // if

    if (state == VAN_RX_VACANT)
    {
        readBits = 0;

        if (pinLevel == VAN_LOGICAL_LOW)
        {
            // Normal detection: we've seen a series of VAN_LOGICAL_HIGH bits

            rxDesc->state = VAN_RX_SEARCHING;
            DEBUG_IFS(toState, VAN_RX_SEARCHING);

            if (nBits == 7 || nBits == 8) atBit = nBits; else atBit = 0;
            jitter = 0;

            pinLevelChangedDuringInterruptHandling = false;
        }
        else if (pinLevel == VAN_LOGICAL_HIGH)
        {
            if (nBits >= 2 && nBits <= 8)
            {
                // Late detection

                rxDesc->state = VAN_RX_SEARCHING;
                DEBUG_IFS(toState, VAN_RX_SEARCHING);

                atBit = nBits;
                if (nBits > 5) jitter = 0;
            } // if
        } // if

        RETURN;
    } // if

    // If the current head packet is already VAN_RX_DONE, the circular buffer is completely full
    if (state == VAN_RX_DONE)
    {
        VanBusRx._overrun = true;

        RETURN;
    } // if

    // During packet reception, the "Enhanced Manchester" encoding guarantees at most 5 bits are the same,
    // except during EOD when it can be 6.
    // However, sometimes the Manchester bit is missed. Let's be tolerant with that, and just pretend it
    // was there, by accepting up to 10 equal bits.
    if (nBits > 10)
    {
        jitter = 0;

        if (state == VAN_RX_SEARCHING)
        {
            readBits = 0;
            atBit = 0;
            rxDesc->size = 0;

            RETURN;
        } // if

        if (atBit == 9)
        {
            uint16_t currentByte = readBits << 1;
            uint8_t readByte = (currentByte >> 2 & 0xF0) | (currentByte >> 1 & 0x0F);
            rxDesc->bytes[rxDesc->size++] = readByte;
        } // if

        VanBusRx._AdvanceHead();

        RETURN;
    } // if

    // Experimental handling of special situations caused by a missed interrupt or a very late ISR invocation.
    // All cases were found by trial and error.
    if (nBits == 0)
    {
        if (state == VAN_RX_SEARCHING)
        {
            nBits = 1;
            DEBUG_ISR(nBits, 1);

            jitter = 0;
        }
        else if (atBit > 0)
        {
            const uint16_t prev = readBits;

            // Set or clear the last read bit
            readBits = pinLevel == VAN_LOGICAL_LOW ? readBits | 0x0001 : readBits & 0xFFFE;

            // If last bit was actually flipped, reset jitter
            //if (atBit > 0 && prev != readBits) jitter = 0;
            if (prev != readBits) jitter -= _min(jitter, CPU_CYCLES(157U));
        } // if
    }
    else if (samePinLevel)
    {
        if (nBits == 1)
        {
            flipBits = 0x0001;
        }
        else if (nBits == 2)
        {
            // Flip the last 'nBits' except the very last bit, e.g. flip the bits -- ---- --X-
            flipBits = 0x0002;
        }
        else if (nBits > 2)
        {
            // Flip the last 'nBits' except the very last bit, e.g. if nBits == 4 ==> flip the bits -- ---- XXX-
            flipBits = (1 << nBits) - 1 - 1;

            // If the interrupt was so late that the pin level has already changed again, then flip also the very
            // last bit
            if (jitter > CPU_CYCLES(280)) flipBits |= 0x0001;
        } // if

        if ((flipBits & 0x0001) == 0x0001) prevPinLevel = PIN_LEVEL_UNKNOWN; // next ISR, samePinLevel must always be false.
    } // if

    readBits <<= nBits;
    atBit += nBits;

    // Calculate the position of the last received bit (in order of reception: MSB first)
    int bitPosition = rxDesc->size * 8 + atBit;

    // Count only the "real" bits, not the Manchester bits
    if (atBit > 4) bitPosition--;
    if (atBit > 9) bitPosition--;

    if (pinLevel == VAN_LOGICAL_LOW)
    {
        // Just had a series of VAN_LOGICAL_HIGH bits
        uint16_t pattern = (1 << nBits) - 1;
        readBits |= pattern;
    } // if

    if (flipBits == 0 && nBits == 3 && (atBit == 5 || atBit == 10) && rxDesc->uncertainBit1 == NO_UNCERTAIN_BIT)
    {
        // 4-th or 8-th bit same as Manchester bit? Then mark that bit position as candidate for
        // later repair by the CheckCrcAndRepair(...) method.
        rxDesc->uncertainBit1 = bitPosition;  // Position 1 = MSB, bit 8 = LSB
    } // if

    if (flipBits != 0)
    {
        readBits ^= flipBits;

        if (nBits > 1 && rxDesc->uncertainBit1 == NO_UNCERTAIN_BIT)
        {
            // The last bit is very uncertain: mark the bit position as candidate for later repair by the
            // CheckCrcAndRepair(...) method
            rxDesc->uncertainBit1 = bitPosition;  // Position 1 = MSB, bit 8 = LSB

            // Note: the one-but-last bit is also very uncertain, but for now we mark only the last bit.
            // In a later version, more than one "uncertain bit" marking may be implemented.
        } // if
    } // if

    if (state == VAN_RX_SEARCHING)
    {
        // The bit timing is slightly different during SOF: apply alternative jitter calculations
        if (nBits == 3)
        {
            // Decrease jitter value by 168, but don't go below 0
            if (jitter > CPU_CYCLES(168)) jitter = jitter - CPU_CYCLES(168); else jitter = 0;
        }
        else if (atBit == 4)
        {
            if (nBits == 4)
            {
                // Timing seems to be around 2590 for the first 4-bit sequence ("----") during SOF
                // (normally it is around 2639)
                if (nCycles > CPU_CYCLES(2624)) jitter = nCycles - CPU_CYCLES(2624); else jitter = 0;
            }
        }
        else if (atBit == 7/* || atBit == 8*/)
        {
            if (nBits == 1)
            {
                // Decrease jitter value by 130, but don't go below 0
                if (jitter > CPU_CYCLES(130)) jitter = jitter - CPU_CYCLES(130); else jitter = 0;
            }
            else if (nBits == 2)
            {
                // Decrease jitter value by 168, but don't go below 0
                if (jitter > CPU_CYCLES(168)) jitter = jitter - CPU_CYCLES(168); else jitter = 0;
            }
            else if (nBits == 4)
            {
                // Timing seems to be around 2530 for the second 4-bit sequence ("1111") during SOF
                // (normally it is around 2639)
                if (nCycles > CPU_CYCLES(2514)) jitter = nCycles - CPU_CYCLES(2514); else jitter = 0;
            } // if
        } // if

        // Be flexible in SOF detection. All cases were found by trial and error.
        if ((atBit == 6 || atBit == 7 || atBit == 8) && (readBits & 0x00F) == 0x00D)  // e.g. 11 11-1, --- 11-1, -11 11-1, ---1 11-1, --11 11-1, ---- 11-1
        {
            atBit = 10;
        }
        else if ((atBit == 8 || atBit == 9) && (readBits & 0x00E) == 0x00A)  // e.g. -11 11-1 1, --11 11-1 1
        {
            atBit = 11;
        }
        else if (atBit == 9 && (readBits & 0x003) == 0x001)  // e.g. - --11 11-1, - ---- ---1, - ---- -1-1
        {
            atBit = 10;
        }
        else if (atBit == 10 && (readBits & 0x006) == 0x002) // e.g. - --11 11-1 -, - --11 11-1 1, - ---- -1-1 1
        {
            atBit = 11;
        } // if
        else if (atBit == 12 && (readBits & 0x018) == 0x008) // e.g. - ---- 11-1 111
        {
            atBit = 13;
        } // if
        else if (atBit == 13 && readBits == 0x1FF) // e.g. - ---1 1111 1111
        {
            // This is not a SOF pattern
            readBits = 0x000; // Force to state VAN_RX_VACANT, below
        } // if
        else if (atBit == 14 && readBits == 0x3FF) // e.g. -- --11 1111 1111
        {
            // This is not a SOF pattern
            readBits = 0x000; // Force to state VAN_RX_VACANT, below
        } // if
    } // if

    DEBUG_ISR(readBits, readBits);

    if (atBit >= 10)
    {
        atBit -= 10;

        // uint16_t, not uint8_t: we are reading 10 bits per byte ("Enhanced Manchester" encoding)
        uint16_t currentByte = readBits >> atBit;

        // Get ready for next byte
        readBits &= (1 << atBit) - 1;

        if (state == VAN_RX_SEARCHING)
        {
            // Ideally, the first 10 bits are 00 0011 1101 (0x03D) (SOF, Start Of Frame)
            if (currentByte != 0x03D

                // Accept also (found through trial and error):
                && currentByte != 0x009 // 00 0000 1001
                && currentByte != 0x01D // 00 0001 1101
                && currentByte != 0x039 // 00 0011 1001
                && currentByte != 0x03E // 00 0011 1110
                && currentByte != 0x019 // 00 0001 1001
                && currentByte != 0x03B // 00 0011 1011
                && currentByte != 0x03C // 00 0011 1100
                && currentByte != 0x01E // 00 0001 1110
                && currentByte != 0x00D // 00 0000 1101
                && currentByte != 0x005 // 00 0000 0101
                && currentByte != 0x001 // 00 0000 0001
                && currentByte != 0x03F // 00 0011 1111
                && currentByte != 0x3FD // 11 1111 1101
                && currentByte != 0x079 // 00 0111 1001
                && currentByte != 0x07D // 00 0111 1101
               )
            {
                rxDesc->state = VAN_RX_VACANT;
                DEBUG_IFS(toState, VAN_RX_VACANT);

                jitter = 0;

                RETURN;
            } // if

            currentByte = 0x03D;

            rxDesc->state = VAN_RX_LOADING;
            DEBUG_IFS(toState, VAN_RX_LOADING);
        } // if

        // Remove the 2 Manchester bits 'm'; the relevant 8 bits are 'X':
        //   9 8 7 6 5 4 3 2 1 0
        //   X X X X m X X X X m
        uint8_t readByte = (currentByte >> 2 & 0xF0) | (currentByte >> 1 & 0x0F);

        rxDesc->bytes[rxDesc->size++] = readByte;

        // EOD detected if last two bits are 0 followed by a 1, but never in bytes 0...4
        if ((currentByte & 0x003) == 0 && atBit == 0 && rxDesc->size >= 5

            // Experiment for 3 last "0"-bits: too short means it is not EOD
            && (nBits != 3 || nCycles > CPU_CYCLES(1963)))
        {
            rxDesc->state = VAN_RX_WAITING_ACK;
            DEBUG_IFS(toState, VAN_RX_WAITING_ACK);

            rxDesc->ack = VAN_NO_ACK;

            TRIGGER_ANALYSER_WAIT_ACK_ISR;

            // Set a timeout for the ACK bit

          #ifdef ARDUINO_ARCH_ESP32

           #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
            timerWrite(timer, 0);
            timerAlarm(timer, 40 * RX_TIMER_TICKS_PER_MICROSECOND, false, 0);  // 5 time slots = 5 * 8 us = 40 microseconds
           #else // ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
            timerWrite(timer, 0);
            timerAlarmEnable(timer);
           #endif // ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)

          #else // ! ARDUINO_ARCH_ESP32

            timer1_disable();
            timer1_attachInterrupt(WaitAckIsr);

            // Clock to timer (prescaler) is always 80 MHz, even if F_CPU is 160 MHz
            timer1_enable(TIMER_DIVIDER, TIM_EDGE, TIM_SINGLE);
            timer1_write(40 * TIMER_TICKS_PER_MICROSECOND); // 5 time slots = 5 * 8 us = 40 us = 200 ticks (0.2 microsecond/tick)

          #endif // ARDUINO_ARCH_ESP32

        }
        else if (rxDesc->size >= VAN_MAX_PACKET_SIZE)
        {
            rxDesc->result = VAN_RX_ERROR_MAX_PACKET;
            VanBusRx._AdvanceHead();

            jitter = 0;
        } // if
    } // if

    RETURN;
} // RxPinChangeIsr

// Initializes the VAN packet receiver
bool TVanPacketRxQueue::Setup(uint8_t rxPin, int queueSize)
{
    if (pin != VAN_NO_PIN_ASSIGNED) return false; // Already setup

  #if defined ANALYSE_WAIT_ACK_ISR || defined ANALYSE_RX_PIN_CHANGE_ISR
    pinMode(ANALYSER_PIN, OUTPUT);
    digitalWrite(ANALYSER_PIN, HIGH);
  #endif

    pinMode(rxPin, INPUT_PULLUP);

    size = queueSize;
    startDroppingPacketsAt = queueSize;
    pool = new TVanPacketRxDesc[queueSize];
    _head = pool;
    tail = pool;
    end = pool + queueSize;

  #ifdef VAN_RX_ISR_DEBUGGING
    _head->isrDebugPacket = isrDebugPacket;
  #endif // VAN_RX_ISR_DEBUGGING

    for (TVanPacketRxDesc* rxDesc = pool; rxDesc < end; rxDesc++) rxDesc->slot = rxDesc - pool;

  #ifdef ARDUINO_ARCH_ESP32

   #if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)

    timer = timerBegin(RX_TIMER_FREQ);
    timerAttachInterrupt(timer, &WaitAckIsr);
    timerAlarm(timer, 40 * RX_TIMER_TICKS_PER_MICROSECOND, false, 0);  // 5 time slots = 5 * 8 us = 40 microseconds

   #else // ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)

    // Clock to timer (prescaler) is always 80 MHz, even if F_CPU is 160 or 240 MHz. We want 0.2 microsecond resolution.
    timer = timerBegin(0, RX_TIMER_DIVIDER, true);
    timerAlarmDisable(timer);
    timerDetachInterrupt(timer);
    timerAttachInterrupt(timer, &WaitAckIsr, true);
    timerAlarmWrite(timer, 40 * RX_TIMER_TICKS_PER_MICROSECOND, false); // 5 time slots = 5 * 8 us = 40 us = 200 ticks (0.2 microsecond/tick)

   #endif // ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)

  #else // ! ARDUINO_ARCH_ESP32

    timer1_isr_init();
    timer1_disable();

  #endif // ARDUINO_ARCH_ESP32

    pin = rxPin;

    attachInterrupt(digitalPinToInterrupt(rxPin), RxPinChangeIsr, CHANGE);
    enabled = true;

    return true;
} // TVanPacketRxQueue::Setup

// If the TX pin is connected: make sure it does not affect the bus
void TVanPacketRxQueue::SetTxPinRecessive(uint8_t txPin)
{
    pinMode(txPin, OUTPUT);
    digitalWrite(txPin, VAN_BIT_RECESSIVE);  // Set bus state to 'recessive' (CANH and CANL: not driven)
} // TVanPacketRxQueue::SetTxPinRecessive

// Copy a VAN packet out of the receive queue, if available. Otherwise, returns false.
// If a valid pointer is passed to 'isQueueOverrun', will report then clear any queue overrun condition.
bool TVanPacketRxQueue::Receive(TVanPacketRxDesc& pkt, bool* isQueueOverrun)
{
    if (pin == VAN_NO_PIN_ASSIGNED) return false; // Call Setup first!

    if (! Available()) return false;

    // Copy the whole packet descriptor out (including the debug info)
    // Note:
    // Instead of copying out, we could also just pass the pointer to the descriptor. However, then we would have to
    // wait with freeing the descriptor, thus keeping one precious queue slot allocated. It is better to copy the
    // packet into the (usually stack-allocated) memory of 'pkt' and free the queue slot as soon as possible. The
    // caller can now keep the packet as long as needed.
    pkt = *tail;

    if (isQueueOverrun) *isQueueOverrun = IsQueueOverrun();

    // Indicate packet buffer is available for next packet
    tail->Init();

    AdvanceTail();

    return true;
} // TVanPacketRxQueue::Receive

// Disable VAN packet receiver
void TVanPacketRxQueue::Disable()
{
    if (pin == VAN_NO_PIN_ASSIGNED) return; // Call Setup first!

  #ifdef ARDUINO_ARCH_ESP8266
    timer1_disable();
  #endif // ARDUINO_ARCH_ESP8266

    detachInterrupt(digitalPinToInterrupt(VanBusRx.pin));
    enabled = false;
} // TVanPacketRxQueue::Disable

// Enable VAN packet receiver
void TVanPacketRxQueue::Enable()
{
    if (pin == VAN_NO_PIN_ASSIGNED) return; // Call Setup first!
    attachInterrupt(digitalPinToInterrupt(VanBusRx.pin), RxPinChangeIsr, CHANGE);
    enabled = true;
} // TVanPacketRxQueue::Enable

void TVanPacketRxQueue::SetDropPolicy(int startAt, bool (*isEssential)(const TVanPacketRxDesc& pkt) = 0)
{
    startDroppingPacketsAt = startAt;
    isEssentialPacket = isEssential;
} // TVanPacketRxQueue::SetDropPolicy

void IRAM_ATTR TVanPacketRxQueue::_AdvanceHead()
{
    _head->millis_ = millis();
    _head->state = VAN_RX_DONE;
    _head->seqNo = count++;

    // The last bit is always 0, no need to be uncertain about that
    if (_head->uncertainBit1 >= 8 * _head->size) _head->uncertainBit1 = NO_UNCERTAIN_BIT;

  #ifdef VAN_RX_ISR_DEBUGGING
    // Keep the ISR debug packet if CRC is wrong, otherwise just overwrite
    if (! _head->CheckCrc())
    {
        isrDebugPacket->wLock = false;  // Indicate this debug packet is free for reading

        // Move to the next debug packet, but skip it if it is currently being read
        do
        {
            isrDebugPacket++;
            if (isrDebugPacket == isrDebugPacketPool + N_ISR_DEBUG_PACKETS) isrDebugPacket = isrDebugPacketPool;
        } while (isrDebugPacket->rLock && isrDebugPacket != _head->isrDebugPacket);
    } // if
  #endif // VAN_RX_ISR_DEBUGGING

    // Implement simple drop policy
    if (nQueued <= startDroppingPacketsAt || (isEssentialPacket != 0 && (*isEssentialPacket)(*_head)))
    {
        // Move to next slot in queue
        _head = _head + 1;
        if (_head == end) _head = pool;  // Roll over if needed

        // Keep track of queue fill level
        nQueued = nQueued + 1;
        if (nQueued > maxQueued) maxQueued = nQueued;
    }
    else
    {
        // Drop just read packet; free current slot in queue
        _head->Init();
    } // if

  #ifdef VAN_RX_ISR_DEBUGGING
    isrDebugPacket->Init();
    _head->isrDebugPacket = isrDebugPacket;
  #endif // VAN_RX_ISR_DEBUGGING

  #ifdef VAN_RX_IFS_DEBUGGING
    _head->ifsDebugPacket.Init();
  #endif // VAN_RX_IFS_DEBUGGING
} // _AdvanceHead

// Simple function to generate a string representation of a float value.
// Note: passed buffer size must be (at least) MAX_FLOAT_SIZE bytes, e.g. declare like this:
//   char buffer[MAX_FLOAT_SIZE];
char* FloatToStr(char* buffer, float f, int prec)
{
    dtostrf(f, MAX_FLOAT_SIZE - 1, prec, buffer);

    // Strip leading spaces
    char* strippedStr = buffer;
    while (isspace(*strippedStr)) strippedStr++;

    return strippedStr;
} // FloatToStr

// Dumps packet statistics
void TVanPacketRxQueue::DumpStats(Stream& s, bool longForm) const
{
    uint32_t pktCount = GetCount();

    static const char PROGMEM formatter[] = "received pkts: %" PRIu32 ", corrupt: %" PRIu32 "/%" PRIu32 " (%s%%)";
    char floatBuf[MAX_FLOAT_SIZE];

    uint32_t overallCorrupt = nCorrupt - nRepaired;

    if (longForm)
    {
        // Long output format

        // Using shared buffer floatBuf, so only one invocation per printf
        s.printf_P(
            formatter,
            pktCount,
            nCorrupt,
            nCountedForRepair,
            nCountedForRepair == 0
                ? "-.---"
                : FloatToStr(floatBuf, 100.0 * nCorrupt / nCountedForRepair, 3));

        s.printf_P(
            PSTR(", repaired: %" PRIu32 " (%s%%)"),
            nRepaired,
            nCorrupt == 0
                ? "---" 
                : FloatToStr(floatBuf, 100.0 * nRepaired / nCorrupt, 0));

        s.printf_P(
            PSTR(" [DB:%" PRIu32 ", SB:%" PRIu32 ", DCB:%" PRIu32 ", DSB:%" PRIu32 "], UCB:%" PRIu32),
            nBitDeletionErrors,
            nOneBitErrors,
            nTwoConsecutiveBitErrors,
            nTwoSeparateBitErrors,
            nUncertainBitErrors);

        s.printf_P(
            PSTR(", overall: %" PRIu32 " (%s%%)"),
            overallCorrupt,
            nCountedForRepair == 0
                ? "-.---" 
                : FloatToStr(floatBuf, 100.0 * overallCorrupt / nCountedForRepair, 3));
    }
    else
    {
        // Short output format

        s.printf_P(
            formatter,
            pktCount,
            overallCorrupt,
            nCountedForRepair,
            nCountedForRepair == 0
                ? "-.---"
                : FloatToStr(floatBuf, 100.0 * overallCorrupt / nCountedForRepair, 3));
    } // if

    s.printf_P(PSTR(", addBitTime: %ld"), addToBitTime / CPU_F_FACTOR);

    s.printf_P(PSTR(", maxQueued: %d/%d\n"), GetMaxQueued(), QueueSize());
} // TVanPacketRxQueue::DumpStats

#ifdef VAN_RX_IFS_DEBUGGING

bool TIfsDebugPacket::IsAbnormal() const
{
    // Normally, a packet is recognized after 5 interrupts (pin level changes)
    // The 'pkt.getIfsDebugPacket().Dump()' output looks e.g. like this:
    //
    //   # nCycles -> nBits pinLVL       state
    //   0  >65535 ->  >255    "0"      VACANT
    //   1    2607 ->     4    "1"   SEARCHING
    //   2    2512 ->     4    "0"   SEARCHING
    //   3     729 ->     1    "1"   SEARCHING
    //   4     711 ->     1    "0"   SEARCHING
    //
    // Alternatively, this can also happen:
    //
    //   # nCycles -> nBits pinLVL       state
    //   0    1151 ->     2    "1"      VACANT
    //   1    7913 ->    12    "0"      VACANT
    //   2    2594 ->     4    "1"   SEARCHING
    //   3    2526 ->     4    "0"   SEARCHING
    //   4     714 ->     1    "1"   SEARCHING
    //   5     703 ->     1    "0"   SEARCHING

    bool normal = at <= 5 || (at == 6 && samples[0].pinLevel == 1);
    return ! normal;
} // TIfsDebugPacket::IsAbnormal

// Dump data found in the inter-frame space
void TIfsDebugPacket::Dump(Stream& s) const
{
    int i = 0;

    while (i < at)
    {
        const TIfsDebugData* ifsData = samples + i;
        if (i == 0) s.printf_P(PSTR("  # nCycles -> nBits pinLVL   fromState     toState\n"));

        s.printf("%3u", i);

        const uint32_t nCyclesMeasured = ifsData->nCyclesMeasured;
        if (nCyclesMeasured >= USHRT_MAX) s.printf("  >%5" PRIu32, USHRT_MAX); else s.printf(" %7" PRIu32, nCyclesMeasured);

        s.print(" -> ");

        const uint16_t nBits = ifsData->nBits;
        if (nBits >= UCHAR_MAX) s.printf(" >%3" PRIu16, UCHAR_MAX); else s.printf("%5" PRIu16, nBits);

        const uint16_t pinLevel = ifsData->pinLevel;
        s.printf("    \"%" PRIu16 "\"", pinLevel);

        s.printf(" %11.11s", TVanPacketRxDesc::StateStr(ifsData->fromState));
        s.printf(" %11.11s", TVanPacketRxDesc::StateStr(ifsData->toState));

        s.print(" ");

        if (nBits > 6)
        {
            s.print(pinLevel == VAN_LOGICAL_LOW ? F("1.....1") : F("-.....-"));
        }
        else
        {
            for (int i = 0; i < nBits; i++) s.print(pinLevel == VAN_LOGICAL_LOW ? "1" : "-");
            for (int i = nBits; i < 6; i++) s.print(" ");
        } // if

        s.print("\n");

        i++;
    } // while
} // TIfsDebugPacket::Dump

#endif // VAN_RX_IFS_DEBUGGING

#ifdef VAN_RX_ISR_DEBUGGING

// Print ISR debug data
void TIsrDebugPacket::Dump(Stream& s) const
{
    NO_INTERRUPTS;
    if (wLock)
    {
        // Packet has not (yet) been written to, or is currently being written into
        INTERRUPTS;
        return;
    } // if

    rLock = true;
    INTERRUPTS;

    uint16_t prevAtBit = 0;
    boolean eodSeen = false;
    int size = 0;
    int i = 0;

    #define reset() \
    { \
        eodSeen = false; \
        size = 0; \
    }

    while (at > 2 && i < at)
    {
        const TIsrDebugData* isrData = samples + i;
        if (i == 0)
        {
            // Print headings
            s.print(F("  # nCycles+jitt = nTotal -> nBits atBit (nLate) pinLVLs        fromState     toState data  flip byte\n"));
        } // if

        if (i <= 1) reset();

        s.printf("%3d", i);

        const uint32_t nCyclesMeasured = isrData->nCyclesMeasured;
        if (nCyclesMeasured >= USHRT_MAX) s.printf("  >%5" PRIu32, USHRT_MAX); else s.printf(" %7" PRIu32, nCyclesMeasured);

        const uint32_t jitter = isrData->fromJitter;
        if (jitter != 0)
        {
            s.printf("+%4" PRIu32, jitter);
            s.printf(" =%7" PRIu32, nCyclesMeasured + jitter);
        }
        else
        {
            s.print(F("              "));
        } // if

        s.print(" -> ");

        const uint16_t nBits = isrData->nBits;
        if (nBits >= UCHAR_MAX) s.printf(" >%3" PRIu16, UCHAR_MAX); else s.printf("%5" PRIu16, nBits);

        s.printf(" %5u", isrData->atBit);

        const uint32_t addedCycles = isrData->toJitter;
        if (addedCycles != 0)
        {
            #define MAX_UINT32_STR_SIZE 11
            static char buffer[MAX_UINT32_STR_SIZE];
            sprintf(buffer, "(%" PRIu32 ")", addedCycles);
            s.printf("%8s", buffer);
        }
        else
        {
            s.print(F("        "));
        } // if

        const uint16_t pinLevel = isrData->pinLevel;
        if (isrData->prevPinLevel == PIN_LEVEL_UNKNOWN)
        {
            s.print(" \"X\"");
        }
        else
        {
            s.printf(" \"%" PRIu16 "\"", isrData->prevPinLevel);
        }
        s.printf("->\"%" PRIu16 "\",\"%" PRIu16 "\"", pinLevel, isrData->pinLevelAtReturnFromIsr);

        s.printf(" %11.11s", TVanPacketRxDesc::StateStr(isrData->fromState));
        s.printf(" %11.11s ", TVanPacketRxDesc::StateStr(isrData->toState));

        if (nBits > 10)
        {
            // Show we just had a long series of 1's (shown as '1......') or 0's (shown as '-......')
            s.print(pinLevel == VAN_LOGICAL_LOW ? F("1......") : F("-......"));
            s.print("\n");

            reset();
            i++;
            continue;
        } // if

        // Print the read bits one by one, in a column of 6
        if (nBits > 6)
        {
            s.print(pinLevel == VAN_LOGICAL_LOW ? F("1.....1") : F("-.....-"));
        }
        else
        {
            for (int i = 0; i < nBits; i++)
            {
                s.print(pinLevel == VAN_LOGICAL_LOW ? "1" : "-");
                if (prevAtBit + i == 9) s.print("|");  // End of byte marker
            } // for
            for (int i = nBits; i < 6; i++) s.print(" ");
        } // if

        bool sofSeen = isrData->fromState == VAN_RX_SEARCHING && isrData->toState == VAN_RX_LOADING;
        if (sofSeen && prevAtBit + isrData->nBits < 10) s.print("|");  // End of SOF byte marker

        const uint16_t flipBits = isrData->flipBits;

        // TODO - print bit pattern, e.g. "1E" must be printed as "FFFF-" ("F" indicates bit to be flipped)
        if (flipBits == 0) s.print("    "); else s.printf(" %02" PRIX16 " ", flipBits);

        if (eodSeen)
        {
            if (pinLevel == VAN_LOGICAL_LOW && nBits == 1)
            {
                s.print(" ACK");
                reset();
            } // if
        }
        else if (sofSeen || prevAtBit + isrData->nBits >= 10)
        {
            int shift = prevAtBit + isrData->nBits;
            if (shift > 10) shift -= 10; else shift = 0;

            // uint16_t, not uint8_t: we are reading 10 bits per byte ("Enhanced Manchester" encoding)
            uint16_t currentByte = isrData->readBits >> shift;

            // Print each bit. Use small (superscript) characters for Manchester bits.
            for (int i = 9; i >= 6; i--) s.print(currentByte & 1 << i ? "1" : "-");
            s.print(currentByte & 1 << 5 ? "\u00b9" : "\u00b0");
            for (int i = 4; i >= 1; i--) s.print(currentByte & 1 << i ? "1" : "-");
            s.print(currentByte & 1 << 0 ? "\u00b9" : "\u00b0");

            // Remove the 2 Manchester bits 'm'; the relevant 8 bits are 'X':
            //   9 8 7 6 5 4 3 2 1 0
            //   X X X X m X X X X m
            const uint8_t readByte = (currentByte >> 2 & 0xF0) | (currentByte >> 1 & 0x0F);

            // Print the read byte and its position in the frame
            s.printf_P(PSTR(" --> 0x%02X '%c' (#%d)"),
                readByte,
                readByte >= 0x20 && readByte <= 0x7E ? readByte : '?',
                size + 1
            );
            size++;

            // EOD detected if last two bits are 0 followed by a 1, but never in bytes 0...4
            if ((currentByte & 0x003) == 0 && isrData->atBit == 0 && size >= 5)
            {
                eodSeen = true;
                s.print(" EOD");
            } // if
        } // if

        prevAtBit = isrData->atBit;

        s.print("\n");

        i++;
    } // while

    #undef reset

    rLock = false;  // Assumed to be atomic

} // TIsrDebugPacket::Dump

#endif // VAN_RX_ISR_DEBUGGING

TVanPacketRxQueue VanBusRx;
