/*
 * IRReceive.hpp
 * This file is exclusively included by IRremote.h to enable easy configuration of library switches
 *
 *  Contains all IRrecv class functions as well as other receiver related functions.
 *
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 ************************************************************************************
 * MIT License
 *
 * Copyright (c) 2009-2022 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 ************************************************************************************
 */
#ifndef _IR_RECEIVE_HPP
#define _IR_RECEIVE_HPP

#include "IRremoteInt.h"


/** \addtogroup Receiving Receiving IR data for multiple protocols
 * @{
 */
/**
 * The receiver instance
 */

#ifdef NOTHERE
IRrecv IrReceiver;
#endif

/*
 * The control structure instance
 */
struct irparams_struct irparams; // the irparams instance

/**
 * Instantiate the IRrecv class. Multiple instantiation is not supported.
 * @param IRReceivePin Arduino pin to use. No sanity check is made.
 */
IRrecv::IRrecv() {
    decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc.
    setReceivePin(0);
#if !defined(NO_LED_FEEDBACK_CODE)
    setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK);
#endif
}

IRrecv::IRrecv(uint_fast8_t aReceivePin) {
    decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc.
    setReceivePin(aReceivePin);
#if !defined(NO_LED_FEEDBACK_CODE)
    setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK);
#endif
}

/**
 * Instantiate the IRrecv class. Multiple instantiation is not supported.
 * @param aReceivePin Arduino pin to use, where a demodulating IR receiver is connected.
 * @param aFeedbackLEDPin if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
 */
IRrecv::IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin) {
    decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc.
    setReceivePin(aReceivePin);
#if !defined(NO_LED_FEEDBACK_CODE)
    setLEDFeedback(aFeedbackLEDPin, DO_NOT_ENABLE_LED_FEEDBACK);
#else
    (void) aFeedbackLEDPin;
#endif
}

/**********************************************************************************************************************
 * Stream like API
 **********************************************************************************************************************/
/**
 * Initializes the receive and feedback pin
 * @param aReceivePin The Arduino pin number, where a demodulating IR receiver is connected.
 * @param aEnableLEDFeedback if true / ENABLE_LED_FEEDBACK, then let the feedback led blink on receiving IR signal
 * @param aFeedbackLEDPin if 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
 */
void IRrecv::begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {

    setReceivePin(aReceivePin);
#if !defined(NO_LED_FEEDBACK_CODE)
    bool tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
    if (aEnableLEDFeedback) {
        tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_RECEIVE;
    }
    setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
#else
    (void) aEnableLEDFeedback;
    (void) aFeedbackLEDPin;
#endif
    // Set pin mode once
    pinModeFast(irparams.IRReceivePin, INPUT);

#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
    pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
    start();
}

/**
 * Sets / changes the receiver pin number
 */
void IRrecv::setReceivePin(uint_fast8_t aReceivePinNumber) {
    irparams.IRReceivePin = aReceivePinNumber;
#if defined(__AVR__)
    irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber);
    irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber));
#endif
}

/**
 * Configures the timer and the state machine for IR reception.
 */
void IRrecv::start() {

    // Setup for cyclic 50 us interrupt
    timerConfigForReceive(); // no interrupts enabled here!

    // Initialize state machine state
    resume();

    // Timer interrupt is enabled after state machine reset
    TIMER_ENABLE_RECEIVE_INTR;
}
/**
 * Alias for start().
 */
void IRrecv::enableIRIn() {
    start();
}

/**
 * Configures the timer and the state machine for IR reception.
 * @param aMicrosecondsToAddToGapCounter To compensate for the amount of microseconds the timer was stopped / disabled.
 */
void IRrecv::start(uint32_t aMicrosecondsToAddToGapCounter) {
    start();
    noInterrupts();
    irparams.TickCounterForISR += aMicrosecondsToAddToGapCounter / MICROS_PER_TICK;
    interrupts();
}

/**
 * Restarts receiver after send. Is a NOP if sending does not require a timer
 */
void IRrecv::restartAfterSend() {
#if defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER)
    start();
#endif
}

/**
 * Disables the timer for IR reception.
 */
void IRrecv::stop() {
    TIMER_DISABLE_RECEIVE_INTR;
}
/**
 * Alias for stop().
 */
void IRrecv::disableIRIn() {
    stop();
}
/**
 * Alias for stop().
 */
void IRrecv::end() {
    stop();
}

/**
 * Returns status of reception
 * @return true if no reception is on-going.
 */
bool IRrecv::isIdle() {
    return (irparams.StateForISR == IR_REC_STATE_IDLE || irparams.StateForISR == IR_REC_STATE_STOP) ? true : false;
}

/**
 * Restart the ISR state machine
 * Enable receiving of the next value
 */
void IRrecv::resume() {
    // check allows to call resume at arbitrary places or more than once
    if (irparams.StateForISR == IR_REC_STATE_STOP) {
        irparams.StateForISR = IR_REC_STATE_IDLE;
    }
#if defined(SEND_PWM_BY_TIMER)
//    TIMER_ENABLE_RECEIVE_INTR;  // normally it is stopped by send()
#endif
}

/**
 * Is internally called by decode before calling decoders.
 * Must be used to setup data, if you call decoders manually.
 */
void IRrecv::initDecodedIRData() {

    if (irparams.OverflowFlag) {
        // Copy overflow flag to decodedIRData.flags and reset it
        irparams.OverflowFlag = false;
        irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call
        decodedIRData.flags = IRDATA_FLAGS_WAS_OVERFLOW;
        IR_DEBUG_PRINTLN(F("Overflow happened"));

    } else {
        decodedIRData.flags = IRDATA_FLAGS_EMPTY;
        // save last protocol, command and address for repeat handling (where the are copied back :-))
        lastDecodedProtocol = decodedIRData.protocol; // repeat patterns can be equal between protocols (e.g. NEC and LG), so we must keep the original one
        lastDecodedCommand = decodedIRData.command;
        lastDecodedAddress = decodedIRData.address;

    }
    decodedIRData.protocol = UNKNOWN;
    decodedIRData.command = 0;
    decodedIRData.address = 0;
    decodedIRData.decodedRawData = 0;
    decodedIRData.numberOfBits = 0;
}

/**
 * Returns true if IR receiver data is available.
 */
bool IRrecv::available() {
    return (irparams.StateForISR == IR_REC_STATE_STOP);
}

/**
 * If IR receiver data is available, returns pointer to IrReceiver.decodedIRData, else NULL.
 */
IRData* IRrecv::read() {
    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return NULL;
    }
    if (decode()) {
        return &decodedIRData;
    } else {
        return NULL;
    }
}

/**
 * The main decode function, attempts to decode the recently receive IR signal.
 * The set of decoders used is determined by active definitions of the DECODE_<PROTOCOL> macros.
 * @return false if no IR receiver data available, true if data available. Results of decoding are stored in IrReceiver.decodedIRData.
 */
bool IRrecv::decode() {
    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return false;
    }

    initDecodedIRData(); // sets IRDATA_FLAGS_WAS_OVERFLOW

    if (decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) {
        /*
         * Set OverflowFlag flag and return true here, to let the loop call resume or print raw data.
         */
        decodedIRData.protocol = UNKNOWN;
        return true;
    }

#if defined(DECODE_NEC)
    IR_TRACE_PRINTLN(F("Attempting NEC decode"));
    if (decodeNEC()) {
        return true;
    }
#endif

#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO)
    IR_TRACE_PRINTLN(F("Attempting Panasonic/Kaseikyo decode"));
    if (decodeKaseikyo()) {
        return true;
    }
#endif

#if defined(DECODE_DENON)
    IR_TRACE_PRINTLN(F("Attempting Denon/Sharp decode"));
    if (decodeDenon()) {
        return true;
    }
#endif

#if defined(DECODE_SONY)
    IR_TRACE_PRINTLN(F("Attempting Sony decode"));
    if (decodeSony()) {
        return true;
    }
#endif

#if defined(DECODE_RC5)
    IR_TRACE_PRINTLN(F("Attempting RC5 decode"));
    if (decodeRC5()) {
        return true;
    }
#endif

#if defined(DECODE_RC6)
    IR_TRACE_PRINTLN(F("Attempting RC6 decode"));
    if (decodeRC6()) {
        return true;
    }
#endif

#if defined(DECODE_LG)
    IR_TRACE_PRINTLN(F("Attempting LG decode"));
    if (decodeLG()) {
        return true;
    }
#endif

#if defined(DECODE_JVC)
    IR_TRACE_PRINTLN(F("Attempting JVC decode"));
    if (decodeJVC()) {
        return true;
    }
#endif

#if defined(DECODE_SAMSUNG)
    IR_TRACE_PRINTLN(F("Attempting Samsung decode"));
    if (decodeSamsung()) {
        return true;
    }
#endif
    /*
     * Start of the exotic protocols
     */

#if defined(DECODE_BEO)
    IR_TRACE_PRINTLN(F("Attempting Bang & Olufsen decode"));
    if (decodeBangOlufsen()) {
        return true;
    }
#endif

#if defined(DECODE_WHYNTER)
    IR_TRACE_PRINTLN(F("Attempting Whynter decode"));
    if (decodeWhynter()) {
        return true;
    }
#endif

#if defined(DECODE_LEGO_PF)
    IR_TRACE_PRINTLN(F("Attempting Lego Power Functions"));
    if (decodeLegoPowerFunctions()) {
        return true;
    }
#endif

#if defined(DECODE_BOSEWAVE)
    IR_TRACE_PRINTLN(F("Attempting Bosewave  decode"));
    if (decodeBoseWave()) {
        return true;
    }
#endif

#if defined(DECODE_MAGIQUEST)
    IR_TRACE_PRINTLN(F("Attempting MagiQuest decode"));
    if (decodeMagiQuest()) {
        return true;
    }
#endif

    /*
     * Try the universal decoder for pulse distance protocols
     */
#if defined(DECODE_DISTANCE)
    IR_TRACE_PRINTLN(F("Attempting universal Distance decode"));
    if (decodeDistance()) {
        return true;
    }
#endif

    /*
     * Last resort is the universal hash decode which always return true
     */
#if defined(DECODE_HASH)
    IR_TRACE_PRINTLN(F("Hash decode"));
    // decodeHash returns a hash on any input.
    // Thus, it needs to be last in the list.
    // If you add any decodes, add them before this.
    if (decodeHash()) {
        return true;
    }
#endif

    /*
     * Return true here, to let the loop decide to call resume or to print raw data.
     */
    return true;
}

/**********************************************************************************************************************
 * Common decode functions
 **********************************************************************************************************************/
/**
 * Decode pulse width protocols. Currently only used for sony protocol, which is LSB first.
 * The space (pause) has constant length, the length of the mark determines the bit value.
 *      Each bit looks like: MARK_1 + SPACE -> 1 or : MARK_0 + SPACE -> 0
 *
 * Input is     IrReceiver.decodedIRData.rawDataPtr->rawbuf[]
 * Output is    IrReceiver.decodedIRData.decodedRawData
 *
 * @param aStartOffset must point to a mark
 * @return true if decoding was successful
 */
bool IRrecv::decodePulseWidthData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aOneMarkMicros,
        unsigned int aZeroMarkMicros, unsigned int aBitSpaceMicros, bool aMSBfirst) {

    unsigned int *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset];
    uint32_t tDecodedData = 0;

    if (aMSBfirst) {
        /*
         * MSB first is currently optimized out by the compiler, since it is never used.
         */
        for (uint_fast8_t i = 0; i < aNumberOfBits; i++) {
            // Check for variable length mark indicating a 0 or 1
            if (matchMark(*tRawBufPointer, aOneMarkMicros)) {
                tDecodedData = (tDecodedData << 1) | 1;
                IR_TRACE_PRINT('1');
            } else if (matchMark(*tRawBufPointer, aZeroMarkMicros)) {
                tDecodedData = (tDecodedData << 1) | 0;
                IR_TRACE_PRINT('0');
            } else {
                IR_DEBUG_PRINT(F("Mark="));
                IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
                IR_DEBUG_PRINT(F(" is not "));
                IR_DEBUG_PRINT(aOneMarkMicros);
                IR_DEBUG_PRINT(F(" or "));
                IR_DEBUG_PRINT(aZeroMarkMicros);
                IR_DEBUG_PRINT(' ');
                return false;
            }
            tRawBufPointer++;

            // If we have no stop bit, assume that last space, which is not recorded, is correct, since we can not check it
            if (tRawBufPointer < &decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->rawlen]) {
                // Check for constant length space
                if (!matchSpace(*tRawBufPointer, aBitSpaceMicros)) {
                    IR_DEBUG_PRINT(F("Space="));
                    IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
                    IR_DEBUG_PRINT(F(" is not "));
                    IR_DEBUG_PRINT(aBitSpaceMicros);
                    IR_DEBUG_PRINT(' ');
                    return false;
                }
                tRawBufPointer++;
            }
        }
        IR_TRACE_PRINTLN(F(""));
    } else {
        // LSB first
        for (uint32_t tMask = 1UL; aNumberOfBits > 0; tMask <<= 1, aNumberOfBits--) {

            // Check for variable length mark indicating a 0 or 1
            if (matchMark(*tRawBufPointer, aOneMarkMicros)) {
                tDecodedData |= tMask; // set the bit
                IR_TRACE_PRINT('1');
            } else if (matchMark(*tRawBufPointer, aZeroMarkMicros)) {
                // do not set the bit
                IR_TRACE_PRINT('0');
            } else {
                IR_DEBUG_PRINT(F("Mark="));
                IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
                IR_DEBUG_PRINT(F(" is not "));
                IR_DEBUG_PRINT(aOneMarkMicros);
                IR_DEBUG_PRINT(F(" or "));
                IR_DEBUG_PRINT(aZeroMarkMicros);
                IR_DEBUG_PRINT(' ');
                return false;
            }
            tRawBufPointer++;

            // If we have no stop bit, assume that last space, which is not recorded, is correct, since we can not check it
            if (tRawBufPointer < &decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->rawlen]) {
                // Check for constant length space here
                if (!matchSpace(*tRawBufPointer, aBitSpaceMicros)) {
                    IR_DEBUG_PRINT(F("Space="));
                    IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
                    IR_DEBUG_PRINT(F(" is not "));
                    IR_DEBUG_PRINT(aBitSpaceMicros);
                    IR_DEBUG_PRINT(' ');
                    return false;
                }
                tRawBufPointer++;
            }
        }
        IR_TRACE_PRINTLN(F(""));
    }
    decodedIRData.decodedRawData = tDecodedData;
    return true;
}

/**
 * Decode pulse distance protocols.
 * The mark (pulse) has constant length, the length of the space determines the bit value.
 * Each bit looks like: MARK + SPACE_1 -> 1
 *                 or : MARK + SPACE_0 -> 0
 *
 * Input is     IrReceiver.decodedIRData.rawDataPtr->rawbuf[]
 * Output is    IrReceiver.decodedIRData.decodedRawData
 *
 * @param   aStartOffset must point to a mark
 * @return  true if decoding was successful
 */
bool IRrecv::decodePulseDistanceData(PulsePauseWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits,
        uint_fast8_t aStartOffset) {

    return decodePulseDistanceData(aNumberOfBits, aStartOffset, aProtocolConstants->OneMarkMicros,
            aProtocolConstants->OneSpaceMicros, aProtocolConstants->ZeroSpaceMicros, aProtocolConstants->isMSBFirst);
}

/**
 * Decode pulse distance protocols.
 * The mark (pulse) has constant length, the length of the space determines the bit value.
 * Each bit looks like: MARK + SPACE_1 -> 1
 *                 or : MARK + SPACE_0 -> 0
 *
 * Input is     IrReceiver.decodedIRData.rawDataPtr->rawbuf[]
 * Output is    IrReceiver.decodedIRData.decodedRawData
 *
 * @param   aStartOffset must point to a mark
 * @return  true if decoding was successful
 */
bool IRrecv::decodePulseDistanceData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aBitMarkMicros,
        unsigned int aOneSpaceMicros, unsigned int aZeroSpaceMicros, bool aMSBfirst) {

    unsigned int *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset];
    uint32_t tDecodedData = 0;

    uint32_t tMask = 1UL;
    for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {
        // Check for constant length mark
        if (!matchMark(*tRawBufPointer, aBitMarkMicros)) {
            IR_DEBUG_PRINT(F("Mark="));
            IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
            IR_DEBUG_PRINT(F(" is not "));
            IR_DEBUG_PRINT(aBitMarkMicros);
            IR_DEBUG_PRINT(' ');
            return false;
        }
        tRawBufPointer++;

        if (aMSBfirst) {
            tDecodedData <<= 1;
        }
        // Check for variable length space indicating a 0 or 1
        if (matchSpace(*tRawBufPointer, aOneSpaceMicros)) {
            // set the bit
            if (aMSBfirst) {
                tDecodedData |= 1;
            } else {
                tDecodedData |= tMask;
            }
            IR_TRACE_PRINT('1');
        } else if (matchSpace(*tRawBufPointer, aZeroSpaceMicros)) {
            // do not set the bit
            IR_TRACE_PRINT('0');
        } else {
            IR_DEBUG_PRINT(F("Space="));
            IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK);
            IR_DEBUG_PRINT(F(" is not "));
            IR_DEBUG_PRINT(aOneSpaceMicros);
            IR_DEBUG_PRINT(F(" or "));
            IR_DEBUG_PRINT(aZeroSpaceMicros);
            IR_DEBUG_PRINT(' ');
            return false;
        }
        tRawBufPointer++;
        tMask <<= 1;
    }
    IR_TRACE_PRINTLN(F(""));

    decodedIRData.decodedRawData = tDecodedData;
    return true;
}

/*
 * Static variables for the getBiphaselevel function
 */
uint_fast8_t sBiphaseDecodeRawbuffOffset; // Index into raw timing array
unsigned int sBiphaseCurrentTimingIntervals; // 1, 2 or 3. Number of aBiphaseTimeUnit intervals of the current rawbuf[sBiphaseDecodeRawbuffOffset] timing.
uint_fast8_t sBiphaseUsedTimingIntervals;       // Number of already used intervals of sCurrentTimingIntervals.
unsigned int sBiphaseTimeUnit;

void IRrecv::initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, unsigned int aBiphaseTimeUnit) {
    sBiphaseDecodeRawbuffOffset = aRCDecodeRawbuffOffset;
    sBiphaseTimeUnit = aBiphaseTimeUnit;
    sBiphaseUsedTimingIntervals = 0;
}

/**
 * Gets the level of one time interval (aBiphaseTimeUnit) at a time from the raw buffer.
 * The RC5/6 decoding is easier if the data is broken into time intervals.
 * E.g. if the buffer has mark for 2 time intervals and space for 1,
 * successive calls to getBiphaselevel will return 1, 1, 0.
 *
 *               _   _   _   _   _   _   _   _   _   _   _   _   _
 *         _____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
 *                ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^   ^    Significant clock edge
 *               _     _   _   ___   _     ___     ___   _   - Mark
 * Data    _____| |___| |_| |_|   |_| |___|   |___|   |_| |  - Data starts with a mark->space bit
 *                1   0   0   0   1   1   0   1   0   1   1  - Space
 * A mark to space at a significant clock edge results in a 1
 * A space to mark at a significant clock edge results in a 0 (for RC6)
 * Returns current level [MARK or SPACE] or -1 for error (measured time interval is not a multiple of sBiphaseTimeUnit).
 */
uint_fast8_t IRrecv::getBiphaselevel() {
    uint_fast8_t tLevelOfCurrentInterval; // 0 (SPACE) or 1 (MARK)

    if (sBiphaseDecodeRawbuffOffset >= decodedIRData.rawDataPtr->rawlen) {
        return SPACE;  // After end of recorded buffer, assume space.
    }

    tLevelOfCurrentInterval = (sBiphaseDecodeRawbuffOffset) & 1; // on odd rawbuf offsets we have mark timings

    /*
     * Setup data if sUsedTimingIntervals is 0
     */
    if (sBiphaseUsedTimingIntervals == 0) {
        unsigned int tCurrentTimingWith = decodedIRData.rawDataPtr->rawbuf[sBiphaseDecodeRawbuffOffset];
        unsigned int tMarkExcessCorrection = (tLevelOfCurrentInterval == MARK) ? MARK_EXCESS_MICROS : -MARK_EXCESS_MICROS;

        if (matchTicks(tCurrentTimingWith, (sBiphaseTimeUnit) + tMarkExcessCorrection)) {
            sBiphaseCurrentTimingIntervals = 1;
        } else if (matchTicks(tCurrentTimingWith, (2 * sBiphaseTimeUnit) + tMarkExcessCorrection)) {
            sBiphaseCurrentTimingIntervals = 2;
        } else if (matchTicks(tCurrentTimingWith, (3 * sBiphaseTimeUnit) + tMarkExcessCorrection)) {
            sBiphaseCurrentTimingIntervals = 3;
        } else {
            return -1;
        }
    }

// We use another interval from tCurrentTimingIntervals
    sBiphaseUsedTimingIntervals++;

// keep track of current timing offset
    if (sBiphaseUsedTimingIntervals >= sBiphaseCurrentTimingIntervals) {
        // we have used all intervals of current timing, switch to next timing value
        sBiphaseUsedTimingIntervals = 0;
        sBiphaseDecodeRawbuffOffset++;
    }

    IR_TRACE_PRINTLN(tLevelOfCurrentInterval);

    return tLevelOfCurrentInterval;
}

#if defined(DECODE_HASH)
/**********************************************************************************************************************
 * Internal Hash decode function
 **********************************************************************************************************************/
/**
 * Compare two (tick) values for Hash decoder
 * Use a tolerance of 20% to enable e.g. 500 and 600 (NEC timing) to be equal
 * @return  0 if newval is shorter, 1 if newval is equal, and 2 if newval is longer
 */
uint_fast8_t IRrecv::compare(unsigned int oldval, unsigned int newval) {
    if (newval * 10 < oldval * 8) {
        return 0;
    }
    if (oldval * 10 < newval * 8) {
        return 2;
    }
    return 1;
}

#define FNV_PRIME_32 16777619   ///< used for decodeHash()
#define FNV_BASIS_32 2166136261 ///< used for decodeHash()

/**
 * decodeHash - decode an arbitrary IR code.
 * Instead of decoding using a standard encoding scheme
 * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value.
 *
 * The algorithm: look at the sequence of MARK signals, and see if each one
 * is shorter (0), the same length (1), or longer (2) than the previous.
 * Do the same with the SPACE signals.  Hash the resulting sequence of 0's,
 * 1's, and 2's to a 32-bit value.  This will give a unique value for each
 * different code (probably), for most code systems.
 *
 * Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
 * Converts the raw code values into a 32-bit hash code.
 * Hopefully this code is unique for each button.
 * This isn't a "real" decoding, just an arbitrary value.
 *
 * see: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html
 */
bool IRrecv::decodeHash() {
    unsigned long hash = FNV_BASIS_32; // the result is the same no matter if we use a long or unsigned long variable

// Require at least 6 samples to prevent triggering on noise
    if (decodedIRData.rawDataPtr->rawlen < 6) {
        return false;
    }
#if RAW_BUFFER_LENGTH <= 254        // saves around 75 bytes program memory and speeds up ISR
    uint_fast8_t i;
#else
    unsigned int i;
#endif
    for (i = 1; (i + 2) < decodedIRData.rawDataPtr->rawlen; i++) {
        uint_fast8_t value = compare(decodedIRData.rawDataPtr->rawbuf[i], decodedIRData.rawDataPtr->rawbuf[i + 2]);
        // Add value into the hash
        hash = (hash * FNV_PRIME_32) ^ value;
    }

    decodedIRData.decodedRawData = hash;
    decodedIRData.numberOfBits = 32;
    decodedIRData.protocol = UNKNOWN;

    return true;
}

bool IRrecv::decodeHashOld(decode_results *aResults) {
    unsigned long hash = FNV_BASIS_32;

// Require at least 6 samples to prevent triggering on noise
    if (aResults->rawlen < 6) {
        return false;
    }

    for (uint8_t i = 3; i < aResults->rawlen; i++) {
        uint_fast8_t value = compare(aResults->rawbuf[i - 2], aResults->rawbuf[i]);
        // Add value into the hash
        hash = (hash * FNV_PRIME_32) ^ value;
    }

    aResults->value = hash;
    aResults->bits = 32;
    aResults->decode_type = UNKNOWN;
    decodedIRData.protocol = UNKNOWN;

    return true;
}
#endif // DECODE_HASH

/**********************************************************************************************************************
 * Match functions
 **********************************************************************************************************************/

/*
 * returns true if values do match
 */
bool IRrecv::checkHeader(PulsePauseWidthProtocolConstants *aProtocolConstants) {
// Check header "mark" and "space"
    if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], aProtocolConstants->HeaderMarkMicros)) {
        IR_DEBUG_PRINT(::getProtocolString(aProtocolConstants->ProtocolIndex));
        IR_DEBUG_PRINTLN(F(": Header mark length is wrong"));
        return false;
    }
    if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], aProtocolConstants->HeaderSpaceMicros)) {
        IR_DEBUG_PRINT(::getProtocolString(aProtocolConstants->ProtocolIndex));
        IR_DEBUG_PRINTLN(F(": Header space length is wrong"));
        return false;
    }
    return true;
}

void IRrecv::checkForRepeatSpaceAndSetFlag(unsigned int aMediumRepeatSpaceMillis) {
    if (decodedIRData.rawDataPtr->rawbuf[0]
            < ((aMediumRepeatSpaceMillis + (aMediumRepeatSpaceMillis / 4)) * (1000 / MICROS_PER_TICK))) {
        decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
    }
}

/**
 * Match function without compensating for marks exceeded or spaces shortened by demodulator hardware
 * Currently not used
 */
bool matchTicks(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) {
#if defined(TRACE)
    Serial.print(F("Testing: "));
    Serial.print(TICKS_LOW(aMatchValueMicros), DEC);
    Serial.print(F(" <= "));
    Serial.print(aMeasuredTicks, DEC);
    Serial.print(F(" <= "));
    Serial.print(TICKS_HIGH(aMatchValueMicros), DEC);
#endif
    bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros)) && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros)));
#if defined(TRACE)
    if (passed) {
        Serial.println(F("?; passed"));
    } else {
        Serial.println(F("?; FAILED"));
    }
#endif
    return passed;
}

bool MATCH(unsigned int measured_ticks, unsigned int desired_us) {
    return matchTicks(measured_ticks, desired_us);
}

/**
 * Compensate for marks exceeded by demodulator hardware
 */
bool matchMark(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) {
#if defined(TRACE)
    Serial.print(F("Testing mark (actual vs desired): "));
    Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC);
    Serial.print(F("us vs "));
    Serial.print(aMatchValueMicros, DEC);
    Serial.print(F("us: "));
    Serial.print(TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC);
    Serial.print(F(" <= "));
    Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC);
    Serial.print(F(" <= "));
    Serial.print(TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC);
#endif
// compensate for marks exceeded by demodulator hardware
    bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS))
            && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS)));
#if defined(TRACE)
    if (passed) {
        Serial.println(F("?; passed"));
    } else {
        Serial.println(F("?; FAILED"));
    }
#endif
    return passed;
}

bool MATCH_MARK(unsigned int measured_ticks, unsigned int desired_us) {
    return matchMark(measured_ticks, desired_us);
}

/**
 * Compensate for spaces shortened by demodulator hardware
 */
bool matchSpace(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) {
#if defined(TRACE)
    Serial.print(F("Testing space (actual vs desired): "));
    Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC);
    Serial.print(F("us vs "));
    Serial.print(aMatchValueMicros, DEC);
    Serial.print(F("us: "));
    Serial.print(TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC);
    Serial.print(F(" <= "));
    Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC);
    Serial.print(F(" <= "));
    Serial.print(TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC);
#endif
// compensate for spaces shortened by demodulator hardware
    bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS))
            && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS)));
#if defined(TRACE)
    if (passed) {
        Serial.println(F("?; passed"));
    } else {
        Serial.println(F("?; FAILED"));
    }
#endif
    return passed;
}

bool MATCH_SPACE(unsigned int measured_ticks, unsigned int desired_us) {
    return matchSpace(measured_ticks, desired_us);
}

/**
 * Getter function for MARK_EXCESS_MICROS
 */
int getMarkExcessMicros() {
    return MARK_EXCESS_MICROS;
}

#ifdef NOTHERE
/*
 * Check if protocol is not detected and detected space between two transmissions
 * is smaller than known value for protocols (Sony with around 24 ms)
 */
void checkForRecordGapsMicros(Print *aSerial, IRData *aIRDataPtr) {
    /*
     * Check if protocol is not detected and detected space between two transmissions
     * is smaller than known value for protocols (Sony with around 24 ms)
     */
    if (aIRDataPtr->protocol <= PULSE_DISTANCE
            && aIRDataPtr->rawDataPtr->rawbuf[0] < (RECORD_GAP_MICROS_WARNING_THRESHOLD / MICROS_PER_TICK)) {
        aSerial->println();
        aSerial->print(F("Space of "));
        aSerial->print(aIRDataPtr->rawDataPtr->rawbuf[0] * MICROS_PER_TICK);
        aSerial->print(F(" us between two detected transmission is smaller than the minimal gap of "));
        aSerial->print(RECORD_GAP_MICROS_WARNING_THRESHOLD);
        aSerial->println(F(" us known for a protocol."));
        aSerial->println(F("If you get unexpected results, try to increase the RECORD_GAP_MICROS in IRremote.h."));
        aSerial->println();
    }
}


/**********************************************************************************************************************
 * Print functions
 * Since a library should not allocate the "Serial" object, all functions require a pointer to a Print object.
 **********************************************************************************************************************/
void IRrecv::printActiveIRProtocols(Print *aSerial) {
// call no class function with same name
    ::printActiveIRProtocols(aSerial);
}
void printActiveIRProtocols(Print *aSerial) {
#if defined(DECODE_NEC)
    aSerial->print(F("NEC/NEC2/Onkyo/Apple, "));
#endif
#if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO)
    aSerial->print(F("Panasonic/Kaseikyo, "));
#endif
#if defined(DECODE_DENON)
    aSerial->print(F("Denon/Sharp, "));
#endif
#if defined(DECODE_SONY)
    aSerial->print(F("Sony, "));
#endif
#if defined(DECODE_RC5)
    aSerial->print(F("RC5, "));
#endif
#if defined(DECODE_RC6)
    aSerial->print(F("RC6, "));
#endif
#if defined(DECODE_LG)
    aSerial->print(F("LG, "));
#endif
#if defined(DECODE_JVC)
    aSerial->print(F("JVC, "));
#endif
#if defined(DECODE_SAMSUNG)
    aSerial->print(F("Samsung, "));
#endif
    /*
     * Start of the exotic protocols
     */
#if defined(DECODE_BEO)
    aSerial->print(F("Bang & Olufsen, "));
#endif
#if defined(DECODE_WHYNTER)
    aSerial->print(F("Whynter, "));
#endif
#if defined(DECODE_LEGO_PF)
    aSerial->print(F("Lego Power Functions, "));
#endif
#if defined(DECODE_BOSEWAVE)
    aSerial->print(F("Bosewave , "));
#endif
#if defined(DECODE_MAGIQUEST)
    aSerial->print(F("MagiQuest, "));
#endif
#if defined(DECODE_DISTANCE)
#  if defined(SUPPORT_PULSE_WIDTH_DECODING) // The only known pulse width protocol is Sony
    aSerial->print(F("Universal Distance, "));
#  else
    aSerial->print(F("Pulse Distance, "));
#  endif
#endif
#if defined(DECODE_HASH)
    aSerial->print(F("Hash "));
#endif
#if defined(NO_DECODER) // for sending raw only
    (void)aSerial; // to avoid compiler warnings
#endif
}

/**
 * Function to print values and flags of IrReceiver.decodedIRData in one line.
 * Ends with println().
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRResultShort(Print *aSerial) {
// call no class function with same name
    ::printIRResultShort(aSerial, &decodedIRData, true);
}

/**
 * Internal function to print decoded result and flags in one line.
 * Ends with println().
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 * @param aIRDataPtr        Pointer to the data to be printed.
 * @param aPrintRepeatGap   If true also print the gap before repeats.
 */
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintRepeatGap) {
    aSerial->print(F("Protocol="));
    aSerial->print(getProtocolString(aIRDataPtr->protocol));
    if (aIRDataPtr->protocol == UNKNOWN) {
#if defined(DECODE_HASH)
        aSerial->print(F(" Hash=0x"));
        aSerial->print(aIRDataPtr->decodedRawData, HEX);
#endif
        aSerial->print(' ');
        aSerial->print((aIRDataPtr->rawDataPtr->rawlen + 1) / 2, DEC);
        aSerial->println(F(" bits (incl. gap and start) received"));
    } else {
#if defined(DECODE_DISTANCE)
        if(aIRDataPtr->protocol != PULSE_DISTANCE) {
#endif
        /*
         * New decoders have address and command
         */
        aSerial->print(F(" Address=0x"));
        aSerial->print(aIRDataPtr->address, HEX);

        aSerial->print(F(" Command=0x"));
        aSerial->print(aIRDataPtr->command, HEX);

        if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) {
            aSerial->print(F(" Extra=0x"));
            aSerial->print(aIRDataPtr->extra, HEX);
        }

        if (aIRDataPtr->flags & IRDATA_FLAGS_PARITY_FAILED) {
            aSerial->print(F(" Parity fail"));
        }

        if (aIRDataPtr->flags & IRDATA_TOGGLE_BIT_MASK) {
            if (aIRDataPtr->protocol == NEC) {
                aSerial->print(F(" Special repeat"));
            } else {
                aSerial->print(F(" Toggle=1"));
            }
        }
#if defined(DECODE_DISTANCE)
        }
#endif
        if (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
            aSerial->print(' ');
            if (aIRDataPtr->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) {
                aSerial->print(F("Auto-"));
            }
            aSerial->print(F("Repeat"));
            if (aPrintRepeatGap) {
                aSerial->print(F(" gap="));
                aSerial->print((uint32_t) aIRDataPtr->rawDataPtr->rawbuf[0] * MICROS_PER_TICK);
                aSerial->print(F("us"));
            }
        }

        /*
         * Print raw data
         */
        if (!(aIRDataPtr->flags & IRDATA_FLAGS_IS_REPEAT) || aIRDataPtr->decodedRawData != 0) {
            aSerial->print(F(" Raw-Data=0x"));
            aSerial->print(aIRDataPtr->decodedRawData, HEX);

            /*
             * Print number of bits processed
             */
            aSerial->print(' ');
            aSerial->print(aIRDataPtr->numberOfBits, DEC);
            aSerial->print(F(" bits"));

            if (aIRDataPtr->flags & IRDATA_FLAGS_IS_MSB_FIRST) {
                aSerial->println(F(" MSB first"));
            } else {
                aSerial->println(F(" LSB first"));
            }

        } else {
            aSerial->println();
        }

        checkForRecordGapsMicros(aSerial, aIRDataPtr);
    }
}

/**
 * Function to print values and flags of IrReceiver.decodedIRData in one line.
 * Ends with println().
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRSendUsage(Print *aSerial) {
// call no class function with same name
    ::printIRSendUsage(aSerial, &decodedIRData);
}

void printIRSendUsage(Print *aSerial, IRData *aIRDataPtr) {
    if (aIRDataPtr->protocol != UNKNOWN && (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) == 0x00) {
#if defined(DECODE_DISTANCE)
        aSerial->print(F("Send with:"));
        if (aIRDataPtr->protocol == PULSE_DISTANCE) {
            aSerial->println();
            aSerial->print(F("    uint32_t tRawData[]={0x"));
            uint_fast8_t tNumberOf32BitChunks = ((aIRDataPtr->numberOfBits - 1) / 32) + 1;
            for (uint_fast8_t i = 0; i < tNumberOf32BitChunks; ++i) {
                aSerial->print(aIRDataPtr->decodedRawDataArray[i], HEX);
                if (i != tNumberOf32BitChunks - 1) {
                    aSerial->print(F(", 0x"));
                }
            }
            aSerial->println(F("};"));
            aSerial->print(F("   "));
        }
        aSerial->print(F(" IrSender.send"));
#else
        aSerial->print(F("Send with: IrSender.send"));
#endif

#if defined(DECODE_DISTANCE)
        if (aIRDataPtr->protocol != PULSE_DISTANCE) {
#endif
            aSerial->print(getProtocolString(aIRDataPtr->protocol));
            aSerial->print(F("(0x"));
#if defined(DECODE_MAGIQUEST)
            if (aIRDataPtr->protocol == MAGIQUEST) {
                aSerial->print(aIRDataPtr->decodedRawData, HEX);
            } else {
                aSerial->print(aIRDataPtr->address, HEX);
            }
#else
            /*
             * New decoders have address and command
             */
            aSerial->print(aIRDataPtr->address, HEX);
#endif

            aSerial->print(F(", 0x"));
            aSerial->print(aIRDataPtr->command, HEX);
            aSerial->print(F(", <numberOfRepeats>"));

            if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) {
                aSerial->print(F(", 0x"));
                aSerial->print(aIRDataPtr->extra, HEX);
            }
#if defined(DECODE_DISTANCE)
        } else {
            aSerial->print("PulseDistanceWidthFromArray(38, ");
            aSerial->print((aIRDataPtr->extra >> 8) * MICROS_PER_TICK); // aHeaderMarkMicros
            aSerial->print(F(", "));
            aSerial->print((aIRDataPtr->extra & 0xFF) * MICROS_PER_TICK); // aHeaderSpaceMicros
            aSerial->print(F(", "));
            aSerial->print((aIRDataPtr->address >> 8) * MICROS_PER_TICK); // aOneMarkMicros
            aSerial->print(F(", "));
            aSerial->print((aIRDataPtr->address & 0xFF) * MICROS_PER_TICK); // aOneSpaceMicros
            aSerial->print(F(", "));
            aSerial->print((aIRDataPtr->command >> 8) * MICROS_PER_TICK); // aZeroMarkMicros
            aSerial->print(F(", "));
            aSerial->print((aIRDataPtr->command & 0xFF) * MICROS_PER_TICK); // aZeroSpaceMicros
            aSerial->print(F(", &tRawData[0], "));
            aSerial->print(aIRDataPtr->numberOfBits); // aNumberOfBits
#if defined(DISTANCE_DO_MSB_DECODING)
            aSerial->print(F(", PROTOCOL_IS_MSB_FIRST"));
#else
            aSerial->print(F(", PROTOCOL_IS_LSB_FIRST"));
#endif
            aSerial->print(F(", SEND_STOP_BIT"));
            aSerial->print(F(", <millisofRepeatPeriod>, <numberOfRepeats>"));
        }
#endif
        aSerial->println(F(");"));
    }
}

/**
 * Function to print protocol number, address, command, raw data and repeat flag of IrReceiver.decodedIRData in one short line.
 * Does not print a Newline / does not end with println().
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRResultMinimal(Print *aSerial) {
    aSerial->print(F("P="));
    aSerial->print(decodedIRData.protocol);
    if (decodedIRData.protocol == UNKNOWN) {
#if defined(DECODE_HASH)
        aSerial->print(F(" #=0x"));
        aSerial->print(decodedIRData.decodedRawData, HEX);
#endif
        aSerial->print(' ');
        aSerial->print((decodedIRData.rawDataPtr->rawlen + 1) / 2, DEC);
        aSerial->println(F(" bits received"));
    } else {
        /*
         * New decoders have address and command
         */
        aSerial->print(F(" A=0x"));
        aSerial->print(decodedIRData.address, HEX);

        aSerial->print(F(" C=0x"));
        aSerial->print(decodedIRData.command, HEX);

        aSerial->print(F(" Raw=0x"));
        aSerial->print(decodedIRData.decodedRawData, HEX);

        if (decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) {
            aSerial->print(F(" R"));
        }
    }
}

/**
 * Dump out the timings in IrReceiver.decodedIRData.rawDataPtr->rawbuf[] array 8 values per line.
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability.
 */
void IRrecv::printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) {
// Print Raw data
    aSerial->print(F("rawData["));
    aSerial->print(decodedIRData.rawDataPtr->rawlen, DEC);
    aSerial->println(F("]: "));

    /*
     * Print initial gap
     */
    aSerial->print(F(" -"));
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->println((uint32_t) decodedIRData.rawDataPtr->rawbuf[0] * MICROS_PER_TICK, DEC);
    } else {
        aSerial->println(decodedIRData.rawDataPtr->rawbuf[0], DEC);
    }
#if RAW_BUFFER_LENGTH <= 254        // saves around 75 bytes program memory and speeds up ISR
    uint_fast8_t i;
#else
    unsigned int  i;
#endif

// Newline is printed every 8. value, if tCounterForNewline % 8 == 0
    uint_fast8_t tCounterForNewline = 6; // first newline is after the 2 values of the start bit

// check if we have a protocol with no or 8 start bits
#if defined(DECODE_DENON) || defined(DECODE_MAGIQUEST)
    if (
#if defined(DECODE_DENON)
            decodedIRData.protocol == DENON || decodedIRData.protocol == SHARP ||
#endif
#if defined(DECODE_MAGIQUEST)
            decodedIRData.protocol == MAGIQUEST ||
#endif
             false) {
        tCounterForNewline = 0; // no or 8 start bits
    }
#endif

    uint32_t tDuration;
    uint16_t tSumOfDurationTicks = 0;
    for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
        auto tCurrentTicks = decodedIRData.rawDataPtr->rawbuf[i];
        if (aOutputMicrosecondsInsteadOfTicks) {
            tDuration = tCurrentTicks * MICROS_PER_TICK;
        } else {
            tDuration = tCurrentTicks;
        }
        tSumOfDurationTicks += tCurrentTicks; // compute length of protocol frame

        if (!(i & 1)) {  // even
            aSerial->print('-');
        } else {  // odd
            aSerial->print(F(" +"));
        }

        // padding only for big values
        if (aOutputMicrosecondsInsteadOfTicks && tDuration < 1000) {
            aSerial->print(' ');
        }
        if (aOutputMicrosecondsInsteadOfTicks && tDuration < 100) {
            aSerial->print(' ');
        }
        if (tDuration < 10) {
            aSerial->print(' ');
        }
        aSerial->print(tDuration, DEC);

        if ((i & 1) && (i + 1) < decodedIRData.rawDataPtr->rawlen) {
            aSerial->print(','); //',' not required for last one
        }

        tCounterForNewline++;
        if ((tCounterForNewline % 8) == 0) {
            aSerial->println();
        }
    }

    aSerial->println();
    aSerial->print("Sum: ");
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->println((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK, DEC);
    } else {
        aSerial->println(tSumOfDurationTicks, DEC);
    }
}

/**
 * Dump out the IrReceiver.decodedIRData.rawDataPtr->rawbuf[] to be used as C definition for sendRaw().
 *
 * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
 * Print ticks in 8 bit format to save space.
 * Maximum is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence.
 * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms).
 *
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability.
 */
void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) {
// Start declaration
    if (aOutputMicrosecondsInsteadOfTicks) {
        aSerial->print(F("uint16_t rawData["));         // variable type, array name
    } else {
        aSerial->print(F("uint8_t rawTicks["));          // variable type, array name
    }

    aSerial->print(decodedIRData.rawDataPtr->rawlen - 1, DEC);    // array size
    aSerial->print(F("] = {"));    // Start declaration

// Dump data
#if RAW_BUFFER_LENGTH <= 254        // saves around 75 bytes program memory and speeds up ISR
    uint_fast8_t i;
#else
    unsigned int  i;
#endif
    for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
        uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK;

        if (i & 1) {
            // Mark
            tDuration -= MARK_EXCESS_MICROS;
        } else {
            tDuration += MARK_EXCESS_MICROS;
        }

        if (aOutputMicrosecondsInsteadOfTicks) {
            aSerial->print(tDuration);
        } else {
            unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK;
            tTicks = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // uint8_t rawTicks above are 8 bit
            aSerial->print(tTicks);
        }
        if (i + 1 < decodedIRData.rawDataPtr->rawlen)
            aSerial->print(',');                // ',' not required on last one
        if (!(i & 1))
            aSerial->print(' ');
    }

// End declaration
    aSerial->print(F("};"));                //

// Comment
    aSerial->print(F("  // "));
    printIRResultShort(aSerial);

// Newline
    aSerial->println("");
}
#endif


/**
 * Store the decodedIRData to be used for sendRaw().
 *
 * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding and store it in an array provided.
 *
 * Maximum for uint8_t is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence.
 * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms).
 * @param aArrayPtr Address of an array provided by the caller.
 */
void IRrecv::compensateAndStoreIRResultInArray(uint8_t *aArrayPtr) {

// Store data, skip leading space#
#if RAW_BUFFER_LENGTH <= 254        // saves around 75 bytes program memory and speeds up ISR
    uint_fast8_t i;
#else
    unsigned int  i;
#endif
    for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) {
        uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK;
        if (i & 1) {
            // Mark
            tDuration -= MARK_EXCESS_MICROS;
        } else {
            tDuration += MARK_EXCESS_MICROS;
        }

        unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK;
        *aArrayPtr = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // we store it in an 8 bit array
        aArrayPtr++;
    }
}


#ifdef NOTHERE
/**
 * Print results as C variables to be used for sendXXX()
 * @param aSerial The Print object on which to write, for Arduino you can use &Serial.
 */
void IRrecv::printIRResultAsCVariables(Print *aSerial) {
// Now dump "known" codes
    if (decodedIRData.protocol != UNKNOWN) {

        /*
         * New decoders have address and command
         */
        aSerial->print(F("uint16_t"));
        aSerial->print(F(" address = 0x"));
        aSerial->print(decodedIRData.address, HEX);
        aSerial->println(';');

        aSerial->print(F("uint16_t"));
        aSerial->print(F(" command = 0x"));
        aSerial->print(decodedIRData.command, HEX);
        aSerial->println(';');

        // All protocols have data
        aSerial->print(F("uint32_t data = 0x"));
        aSerial->print(decodedIRData.decodedRawData, HEX);
        aSerial->println(';');
        aSerial->println();
    }
}

#endif

/*
 * !!Must be the same order as in decode_type_t in IRProtocol.h!!!
 */
const char * const ProtocolNames[] PROGMEM =
{
    string_Unknown,
#if defined(SUPPORT_PULSE_WIDTH_DECODING) // The only known pulse width protocol is Sony
    string_PulseWidth,
#endif
    string_PulseDistance,
    string_Apple,
    string_Denon,
    string_JVC,
    string_LG,
    string_LG2,
    string_NEC,
    string_NEC2,
    string_Onkyo,
    string_Panasonic,
    string_Kaseikyo,
    string_Kaseikyo_Denon,
    string_Kaseikyo_Sharp,
    string_Kaseikyo_JVC,
    string_Kaseikyo_Mitsubishi,
    string_RC5,
    string_RC6,
    string_Samsung,
    string_SamsungLG,
    string_Sharp,
    string_Sony
#if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
    , string_BangOlufsen,
    string_BoseWave,
    string_Lego,
    string_MagiQuest,
    string_Whynter
#endif
};

#if defined(__AVR__)
const __FlashStringHelper* IRrecv::getProtocolString() {
// call no class function with same name
    return ::getProtocolString(decodedIRData.protocol);
}

const __FlashStringHelper* getProtocolString(decode_type_t aProtocol) {
    const char* tProtocolStringPtr = (char*) pgm_read_word(&ProtocolNames[aProtocol]);
    return((__FlashStringHelper *) (tProtocolStringPtr));
}
#else
const char* IRrecv::getProtocolString() {
// call no class function with same name
    return ::getProtocolString(decodedIRData.protocol);
}

const char* getProtocolString(decode_type_t aProtocol) {
    return ProtocolNames[aProtocol];
}
#endif

/**********************************************************************************************************************
 * Interrupt Service Routine - Called every 50 us
 *
 * Duration in ticks of 50 us of alternating SPACE, MARK are recorded in irparams.rawbuf array.
 * 'rawlen' counts the number of entries recorded so far.
 * First entry is the SPACE between transmissions.
 *
 * As soon as one SPACE entry gets longer than RECORD_GAP_TICKS, state switches to STOP (frame received). Timing of SPACE continues.
 * A call of resume() switches from STOP to IDLE.
 * As soon as first MARK arrives in IDLE, gap width is recorded and new logging starts.
 *
 * With digitalRead and Feedback LED
 * 15 pushs, 1 in, 1 eor before start of code = 2 us @16MHz + * 7.2 us computation time (6us idle time) + * pop + reti = 2.25 us @16MHz => 10.3 to 11.5 us @16MHz
 * With portInputRegister and mask and Feedback LED code commented
 * 9 pushs, 1 in, 1 eor before start of code = 1.25 us @16MHz + * 2.25 us computation time + * pop + reti = 1.5 us @16MHz => 5 us @16MHz
 * => Minimal CPU frequency is 4 MHz
 *
 **********************************************************************************************************************/
//#define _IR_MEASURE_TIMING
//#define _IR_TIMING_TEST_PIN 7 // do not forget to execute: "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" if activated by line above
#if defined(TIMER_INTR_NAME)
ISR (TIMER_INTR_NAME) // for ISR definitions
#else
ISR () // for functions definitions which are called by separate (board specific) ISR
#endif
{
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
    digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
// 7 - 8.5 us for ISR body (without pushes and pops) for ATmega328 @16MHz

    TIMER_RESET_INTR_PENDING;// reset TickCounterForISR interrupt flag if required (currently only for Teensy and ATmega4809)

// Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on]
#if defined(__AVR__)
    uint8_t tIRInputLevel = *irparams.IRReceivePinPortInputRegister & irparams.IRReceivePinMask;
#else
    uint_fast8_t tIRInputLevel = (uint_fast8_t) digitalReadFast(irparams.IRReceivePin);
#endif

    /*
     * Increase TickCounter and clip it at maximum 0xFFFF / 3.2 seconds at 50 us ticks
     */
    if (irparams.TickCounterForISR < UINT16_MAX) {
        irparams.TickCounterForISR++;  // One more 50uS tick
    }

    /*
     * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32
     * So we change the code to if / else if
     */
//    switch (irparams.StateForISR) {
//......................................................................
    if (irparams.StateForISR == IR_REC_STATE_IDLE) { // In the middle of a gap or just resumed (and maybe in the middle of a transmission
        if (tIRInputLevel == INPUT_MARK) {
            // check if we did not start in the middle of a transmission by checking the minimum length of leading space
            if (irparams.TickCounterForISR > RECORD_GAP_TICKS) {
                // Gap just ended; Record gap duration + start recording transmission
                // Initialize all state machine variables
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//                digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
                irparams.OverflowFlag = false;
                irparams.rawbuf[0] = irparams.TickCounterForISR;
                irparams.rawlen = 1;
                irparams.StateForISR = IR_REC_STATE_MARK;
            } // otherwise stay in idle state
            irparams.TickCounterForISR = 0;// reset counter in both cases
        }

    } else if (irparams.StateForISR == IR_REC_STATE_MARK) {  // Timing mark
        if (tIRInputLevel != INPUT_MARK) {   // Mark ended; Record time
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//            digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
            irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR;
            irparams.StateForISR = IR_REC_STATE_SPACE;
            irparams.TickCounterForISR = 0;
        }

    } else if (irparams.StateForISR == IR_REC_STATE_SPACE) {  // Timing space
        if (tIRInputLevel == INPUT_MARK) {  // Space just ended; Record time
            if (irparams.rawlen >= RAW_BUFFER_LENGTH) {
                // Flag up a read OverflowFlag; Stop the state machine
                irparams.OverflowFlag = true;
                irparams.StateForISR = IR_REC_STATE_STOP;
            } else {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//                digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
                irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR;
                irparams.StateForISR = IR_REC_STATE_MARK;
            }
            irparams.TickCounterForISR = 0;

        } else if (irparams.TickCounterForISR > RECORD_GAP_TICKS) {
            /*
             * Current code is ready for processing!
             * We received a long space, which indicates gap between codes.
             * Switch to IR_REC_STATE_STOP
             * Don't reset TickCounterForISR; keep counting width of next leading space
             */
            irparams.StateForISR = IR_REC_STATE_STOP;
        }
    } else if (irparams.StateForISR == IR_REC_STATE_STOP) {
        /*
         * Complete command received
         * stay here until resume() is called, which switches state to IR_REC_STATE_IDLE
         */
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
//        digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
        if (tIRInputLevel == INPUT_MARK) {
            // Reset gap TickCounterForISR, to prepare for detection if we are in the middle of a transmission after call of resume()
            irparams.TickCounterForISR = 0;
        }
    }

#if !defined(NO_LED_FEEDBACK_CODE)
    if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_RECEIVE) {
        setFeedbackLED(tIRInputLevel == INPUT_MARK);
    }
#endif

#ifdef _IR_MEASURE_TIMING
    digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
#endif
}

/**********************************************************************************************************************
 * Function to bit reverse OLD MSB values of e.g. NEC.
 **********************************************************************************************************************/
uint8_t bitreverseOneByte(uint8_t aValue) {
//    uint8_t tReversedValue;
//    return __builtin_avr_insert_bits(0x01234567, aValue, tReversedValue);
    // 76543210
    aValue = (aValue >> 4) | (aValue << 4); // Swap in groups of 4
    // 32107654
    aValue = ((aValue & 0xcc) >> 2) | ((aValue & 0x33) << 2); // Swap in groups of 2
    // 10325476
    aValue = ((aValue & 0xaa) >> 1) | ((aValue & 0x55) << 1); // Swap bit pairs
    // 01234567
    return aValue;
}

uint32_t bitreverse32Bit(uint32_t aInput) {
//    __builtin_avr_insert_bits();
    LongUnion tValue;
    tValue.UByte.HighByte = bitreverseOneByte(aInput);
    tValue.UByte.MidHighByte = bitreverseOneByte(aInput >> 8);
    tValue.UByte.MidLowByte = bitreverseOneByte(aInput >> 16);
    tValue.UByte.LowByte = bitreverseOneByte(aInput >> 24);
    return tValue.ULong;
}
/**********************************************************************************************************************
 * The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials
 * This function calls the old MSB first decoders and fills only the 3 variables:
 * aResults->value
 * aResults->bits
 * aResults->decode_type
 **********************************************************************************************************************/
bool IRrecv::decode(decode_results *aResults) {
    static bool sDeprecationMessageSent = false;

    if (irparams.StateForISR != IR_REC_STATE_STOP) {
        return false;
    }

    if (!sDeprecationMessageSent) {
#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
//        Serial.println(
//                "The function decode(&results)) is deprecated and may not work as expected! Just use decode() without a parameter and IrReceiver.decodedIRData.<fieldname> .");
#endif
        sDeprecationMessageSent = true;
    }

// copy for usage by legacy programs
    aResults->rawbuf = irparams.rawbuf;
    aResults->rawlen = irparams.rawlen;
    if (irparams.OverflowFlag) {
        // Copy overflow flag to decodedIRData.flags
        irparams.OverflowFlag = false;
        irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call
        IR_DEBUG_PRINTLN(F("Overflow happened"));
    }
    aResults->overflow = irparams.OverflowFlag;
    aResults->value = 0;

    decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; // for print

#if defined(DECODE_NEC)
    IR_DEBUG_PRINTLN(F("Attempting old NEC decode"));
    if (decodeNECMSB(aResults)) {
        return true ;
    }
#endif

#if defined(DECODE_SONY)
    IR_DEBUG_PRINTLN(F("Attempting old Sony decode"));
    if (decodeSonyMSB(aResults))  {
        return true ;
    }
#endif

#if defined(DECODE_RC5)
    IR_DEBUG_PRINTLN(F("Attempting RC5 decode"));
    if (decodeRC5()) {
        aResults->bits = decodedIRData.numberOfBits;
        aResults->value = decodedIRData.decodedRawData;
        aResults->decode_type = RC5;

        return true ;
    }
#endif

#if defined(DECODE_RC6)
    IR_DEBUG_PRINTLN(F("Attempting RC6 decode"));
    if (decodeRC6())  {
        aResults->bits = decodedIRData.numberOfBits;
        aResults->value = decodedIRData.decodedRawData;
        aResults->decode_type = RC6;
        return true ;
    }
#endif

//    Removed bool IRrecv::decodePanasonicMSB(decode_results *aResults) since  implementations was wrong (wrong length), and nobody recognized it

#if defined(DECODE_LG)
    IR_DEBUG_PRINTLN(F("Attempting old LG decode"));
    if (decodeLGMSB(aResults)) { return true ;}
#endif

#if defined(DECODE_JVC)
    IR_DEBUG_PRINTLN(F("Attempting old JVC decode"));
    if (decodeJVCMSB(aResults)) {
        return true ;
    }
#endif

#if defined(DECODE_SAMSUNG)
    IR_DEBUG_PRINTLN(F("Attempting old SAMSUNG decode"));
    if (decodeSAMSUNG(aResults)) {
        return true ;
    }
#endif

#if defined(DECODE_DENON)
    IR_DEBUG_PRINTLN(F("Attempting old Denon decode"));
    if (decodeDenonOld(aResults)) {
        return true ;
    }
#endif

// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
// If you add any decodes, add them before this.
    if (decodeHashOld(aResults)) {
        return true;
    }
// Throw away and start over
    resume();
    return false;
}

/** @}*/
#endif // _IR_RECEIVE_HPP
