/*
 * ir_DistanceWidthProtocol.hpp
 *
 * Contains only the decoder functions for universal pulse width or pulse distance protocols!
 * The send functions are used by almost all protocols and are therefore located in IRSend.hpp.
 *
 * If RAM is not more than 2k, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds
 * to save RAM space, otherwise it accepts durations up to 10 ms.
 *
 * This decoder tries to decode a pulse distance or pulse distance width with constant period (or pulse width - not enabled yet) protocol.
 * 1. Analyze all space and mark length
 * 2. Decide which protocol we have
 * 3. Try to decode with the mark and space data found in step 1
 * 4. Assume one start bit / header and one stop bit, since pulse distance data must have a stop bit!
 * No data and address decoding, only raw data as result.
 *
 * Pulse distance data can be sent with the generic function as in SendDemo example line 155:
 * https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L155
 * void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
 *            unsigned int aZeroSpaceMicros, uint32_t aData, uint8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit = false)
 * The header must be sent manually with:
 *          IrSender.mark(MarkMicros)
 *          IrSender.space(SpaceMicros);
 *
 * Or send it by filling a DecodedRawDataArray and with the sendPulseDistanceWidthFromArray() function as in SendDemo example line 175:
 * https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L175
 * sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, unsigned int aHeaderMarkMicros,
 *         unsigned int aHeaderSpaceMicros, unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
 *         unsigned int aZeroSpaceMicros, uint32_t *aDecodedRawDataArray, unsigned int aNumberOfBits, uint8_t aFlags,
 *         unsigned int aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats)
 *
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 ************************************************************************************
 * MIT License
 *
 * Copyright (c) 2022-2025 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_DISTANCE_WIDTH_HPP
#define _IR_DISTANCE_WIDTH_HPP

#if !defined(DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS)
#define DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS       100000 // 100 ms, bit it is just a guess
#endif

#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
//#define SHOW_DISTANCE_WIDTH_DECODER_ERRORS // Prints errors which prevents data to be decoded as distance width data

#if !defined(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE)
#  if (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF)
#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 50 // To save program space, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds
#  else
#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 200 // The decoder accepts mark or space durations up to 200 * 50 (MICROS_PER_TICK) = 10 milliseconds
#  endif
#endif

// Switch the decoding according to your needs
//#define USE_MSB_DECODING_FOR_DISTANCE_DECODER // If active, it resembles LG, otherwise LSB first as most other protocols e.g. NEC and Kaseikyo/Panasonic

/** \addtogroup Decoder Decoders and encoders for different protocols
 * @{
 */
//=====================================================================================
// DDD   III   SSS  TTTTTT   AA   N   N   CCC  EEEE     W     W  III  DDD  TTTTTT  H  H
// D  D   I   S       TT    A  A  NN  N  C     E        W     W   I   D  D   TT    H  H
// D  D   I    SSS    TT    AAAA  N N N  C     EEE      W  W  W   I   D  D   TT    HHHH
// D  D   I       S   TT    A  A  N  NN  C     E         W W W    I   D  D   TT    H  H
// DDD   III  SSSS    TT    A  A  N   N   CCC  EEEE       W W    III  DDD    TT    H  H
//=====================================================================================
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#Codings
/*
 Example output of UnitTest.ino for PulseWidth protocol:
 Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first
 Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 950, 550, 600, 300, 300, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
 rawData[66]:
 -1088600
 + 950,- 550
 + 600,- 300 + 300,- 300 + 350,- 250 + 350,- 250
 + 350,- 300 + 600,- 300 + 300,- 300 + 300,- 300
 + 650,- 250 + 650,- 250 + 300,- 300 + 350,- 250
 + 350,- 300 + 300,- 300 + 600,- 300 + 300,- 300
 + 600,- 300 + 350,- 250 + 600,- 300 + 350,- 250
 + 350,- 300 + 600,- 300 + 600,- 300 + 300,- 300
 + 600,- 300 + 600,- 300 + 600,- 300 + 300,- 300
 + 300,- 300 + 300,- 300 + 350,- 250 + 650
 Sum: 24500

 Example output of UnitTest.ino for PulseDistanceWidth protocol:
 Protocol=PulseDistance Raw-Data=0x76 7 bits LSB first
 Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 5950, 500, 550, 1450, 1550, 500, 0x76, 7, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
 rawData[18]:
 -1092450
 +5950,- 500
 +1500,- 500 + 500,-1450 + 550,-1450 +1550,- 450
 + 550,-1450 + 550,-1450 + 550,-1450 + 550
 Sum: 20950
 */

#if defined(LOCAL_DEBUG)
void printDurations(uint8_t aArray[], uint8_t aMaxIndex) {
    for (uint_fast8_t i = 0; i <= aMaxIndex; i++) {
        //Print index at the beginning of a new line
        if (i % 10 == 0) {
            if (i == 0) {
                Serial.print(' '); // indentation for the first index 0
            } else {
                Serial.println(); // new line for next indexes 10, 20 etc.
            }
            Serial.print(i);
            Serial.print(F(": "));
        }
        // Print number of values in array and duration if != 0
        Serial.print(aArray[i]);
        if (aArray[i] != 0) {
            Serial.print('x');
            Serial.print(i * (uint16_t) MICROS_PER_TICK);
        }
        Serial.print(F(" | "));
    }
    Serial.println();
}
#endif

/*
 * We count all consecutive (allow only one gap between) durations and compute the average.
 * @return false if more than 2 distinct duration values found
 */
bool aggregateArrayCounts(uint8_t aArray[], uint8_t aMaxIndex, uint8_t *aShortIndex, uint8_t *aLongIndex) {
    uint8_t tSum = 0;
    uint16_t tWeightedSum = 0;
    uint8_t tGapCount = 0;
    for (uint_fast8_t i = 0; i <= aMaxIndex; i++) {
        uint8_t tCurrentDurations = aArray[i];
        if (tCurrentDurations != 0) {
            // Add it to sum and remove array content
            tSum += tCurrentDurations;
            tWeightedSum += (tCurrentDurations * i);
            aArray[i] = 0;
            tGapCount = 0;
        } else {
            tGapCount++;
        }
        if (tSum != 0 && (i == aMaxIndex || tGapCount > 1)) {
            /*
             * Here we have a sum AND last element OR more than 1 consecutive gap
             */
            uint8_t tAggregateIndex = (tWeightedSum + (tSum / 2)) / tSum; // with rounding
            aArray[tAggregateIndex] = tSum; // disabling this line increases code size by 2 - unbelievable!
            // store aggregate for later decoding
            if (*aShortIndex == 0) {
                *aShortIndex = tAggregateIndex;
            } else if (*aLongIndex == 0) {
                *aLongIndex = tAggregateIndex;
            } else {
                // we have 3 bins => this is likely no pulse width or distance protocol. e.g. it can be RC5.
                return false;
            }
            // initialize for next aggregation
            tSum = 0;
            tWeightedSum = 0;
        }
    }
    return true;
}

/*
 * Try to decode a pulse distance or pulse width protocol.
 * 1. Analyze all space and mark length
 * 2. Decide if we have an pulse width or distance protocol
 * 3. Try to decode with the mark and space data found in step 1
 * No data and address decoding, only raw data as result.
 *
 * Restrictions:
 * Only protocols with at least 7 bits (+ start and trailing stop bit) are accepted.
 * Pulse or pause duration must be below 2500 us (depends on DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE).
 *
 * calloc() version is 700 bytes larger :-(
 */
bool IRrecv::decodeDistanceWidth() {
    /*
     * Array for up to 49 ticks / 2500 us (or 199  ticks / 10 ms us if RAM > 2k)
     * tick array index 0 covers mark or space durations from 0 to 49 us, and index 49 from 2450 to 2499 us
     */
    uint8_t tDurationArray[DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE];

    /*
     * Only protocols with at least 7 bits are accepted
     */
    if (decodedIRData.rawlen < (2 * 7) + 4) {
#if defined(DEBUG) || defined(SHOW_DISTANCE_WIDTH_DECODER_ERRORS)
        Serial.print(F("PULSE_DISTANCE_WIDTH: "));
        Serial.print(F("Data length="));
        Serial.print(decodedIRData.rawlen);
        Serial.println(F(" is less than 18"));
#endif
        return false;
    }

    // Reset duration array
    memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE);

    uint8_t tIndexOfMaxDuration = 0;
    /*
     * Count number of mark durations. Skip leading start and trailing stop bit.
     */
    for (IRRawlenType i = 3; i < decodedIRData.rawlen - 2; i += 2) {
        auto tDurationTicks = irparams.rawbuf[i];
        if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) {
            tDurationArray[tDurationTicks]++; // count duration if less than DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE
            if (tIndexOfMaxDuration < tDurationTicks) {
                tIndexOfMaxDuration = tDurationTicks;
            }
        } else {
#if defined(LOCAL_DEBUG) || defined(SHOW_DISTANCE_WIDTH_DECODER_ERRORS)
            Serial.print(F("PULSE_DISTANCE_WIDTH: "));
            Serial.print(F("Mark "));
            Serial.print(tDurationTicks * MICROS_PER_TICK);
            Serial.print(F(" is longer than maximum "));
            Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK);
            Serial.print(F(" us. Index="));
            Serial.println(i);
#endif
            return false;
        }
    }

    /*
     * Aggregate mark counts to one duration bin
     */
    uint8_t tMarkTicksShort = 0;
    uint8_t tMarkTicksLong = 0;
    bool tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tMarkTicksShort, &tMarkTicksLong);
#if defined(LOCAL_DEBUG)
    Serial.println(F("Mark:"));
    printDurations(tDurationArray, tIndexOfMaxDuration);
#endif

    if (!tSuccess) {
#if defined(LOCAL_DEBUG) || defined(SHOW_DISTANCE_WIDTH_DECODER_ERRORS)
        Serial.print(F("PULSE_DISTANCE_WIDTH: "));
        Serial.println(F("Mark aggregation failed, more than 2 distinct mark duration values found"));
#endif
        return false;
    }

    // Reset duration array
    memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE);

    /*
     * Count number of space durations. Skip leading start and trailing stop bit.
     */
    tIndexOfMaxDuration = 0;
    for (IRRawlenType i = 4; i < decodedIRData.rawlen - 2; i += 2) {
        auto tDurationTicks = irparams.rawbuf[i];
        if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) {
            tDurationArray[tDurationTicks]++;
            if (tIndexOfMaxDuration < tDurationTicks) {
                tIndexOfMaxDuration = tDurationTicks;
            }
        } else {
#if defined(LOCAL_DEBUG) || defined(SHOW_DISTANCE_WIDTH_DECODER_ERRORS)
            Serial.print(F("PULSE_DISTANCE_WIDTH: "));
            Serial.print(F("Space "));
            Serial.print(tDurationTicks * MICROS_PER_TICK);
            Serial.print(F(" is longer than maximum "));
            Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK);
            Serial.print(F(" us. Index="));
            Serial.println(i);
#endif
            return false;
        }
    }

    /*
     * Aggregate space counts to one duration bin
     */
    uint8_t tSpaceTicksShort = 0;
    uint8_t tSpaceTicksLong = 0;
    tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tSpaceTicksShort, &tSpaceTicksLong);
#if defined(LOCAL_DEBUG)
    Serial.println(F("Space:"));
    printDurations(tDurationArray, tIndexOfMaxDuration);
#endif

    if (!tSuccess) {
#if defined(LOCAL_DEBUG) || defined(SHOW_DISTANCE_WIDTH_DECODER_ERRORS)
        Serial.print(F("PULSE_DISTANCE_WIDTH: "));
        Serial.println(F("Space aggregation failed, more than 2 distinct space duration values found"));
#endif
        return false;
    }

#if RAW_BUFFER_LENGTH <= (512 -4)
    uint_fast8_t tNumberOfBits;
#else
    uint16_t tNumberOfBits;
#endif
    tNumberOfBits = (decodedIRData.rawlen / 2) - 1;
    if (tSpaceTicksLong > 0) {
        // For PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- a stop bit is mandatory, for PULSE_WIDTH it is not required!
        tNumberOfBits--; // Correct for PULSE_DISTANCE stop bit
    }
    decodedIRData.numberOfBits = tNumberOfBits;

    /*
     * Print characteristics of this protocol. Durations are in (50 us) ticks.
     * Number of bits, start bit, start pause, long mark, long space, short mark, short space
     *
     * NEC:         32, 180, 90,  11, 34, 11, 11
     * Samsung32:   32,  90, 90,  11, 34, 11, 11
     * LG:          28, 180, 84,  10, 32, 10, 11
     * JVC:         16, 168, 84,  10, 32, 10, 10
     * Kaseikyo:    48.  69, 35,  9, 26,  9,  9
     * Sony:  12|15|20,  48, 12, 24,  12, 12, 12 // the only known pulse width protocol
     * Disney monorail
     *   model:      7, 120, 10, 30, 30, 10, 10 // PulseDistanceWidth. Can be seen as direct conversion of a 7 bit serial timing at 250 baud with a 6 ms start bit.
     */
#if defined(LOCAL_DEBUG)
    Serial.print(F("DistanceWidthTimingInfoStruct: "));
    Serial.print(tNumberOfBits);
    Serial.print(F(", "));
    Serial.print(irparams.rawbuf[1] * MICROS_PER_TICK);
    Serial.print(F(", "));
    Serial.print(irparams.rawbuf[2] * MICROS_PER_TICK);
    Serial.print(F(", "));
    if(tMarkTicksLong == 0) {
        Serial.print(tMarkTicksShort * MICROS_PER_TICK);
    } else {
        Serial.print(tMarkTicksLong * MICROS_PER_TICK);
    }
    Serial.print(F(", "));
    if(tSpaceTicksLong == 0) {
        Serial.print(tSpaceTicksShort * MICROS_PER_TICK);
    } else {
        Serial.print(tSpaceTicksLong * MICROS_PER_TICK);
    }
    Serial.print(F(", "));
    Serial.print(tMarkTicksShort * MICROS_PER_TICK);
    Serial.print(F(", "));
    Serial.println(tSpaceTicksShort * MICROS_PER_TICK);
#endif

    uint8_t tNumberOfAdditionalArrayValues = (tNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE;

    /*
     * We can have the following protocol timings
     * PULSE_DISTANCE:       Pause/spaces have different length and determine the bit value, longer space is 1. Pulses/marks can be constant, like NEC.
     * PULSE_WIDTH:          Pulses/marks have different length and determine the bit value, longer mark is 1. Pause/spaces can be constant, like Sony.
     * PULSE_DISTANCE_WIDTH: Pulses/marks and pause/spaces have different length, often the bit length is constant, like MagiQuest. Can be decoded by PULSE_DISTANCE approach.
     */

    if (tMarkTicksLong == 0 && tSpaceTicksLong == 0) {
#if defined(LOCAL_DEBUG)
        Serial.print(F("PULSE_DISTANCE: "));
        Serial.println(F("Cannot decode, because there is only one distinct duration value for each space and mark"));
#endif
        return false;
    }
    unsigned int tSpaceMicrosShort;
#if defined(LOCAL_DEBUG)
        if(tMarkTicksLong > 0 && tSpaceTicksLong > 0) {
            Serial.println(F("PULSE_DISTANCE_WIDTH signal found"));
        }
#endif
    tSpaceMicrosShort = tSpaceTicksShort * MICROS_PER_TICK;
    unsigned int tMarkMicrosShort = tMarkTicksShort * MICROS_PER_TICK;
    unsigned int tMarkMicrosLong = tMarkTicksLong * MICROS_PER_TICK;
    unsigned int tSpaceMicrosLong = tSpaceTicksLong * MICROS_PER_TICK;
    IRRawlenType tStartIndex = 3;  // skip leading start bit for decoding.

    for (uint_fast8_t i = 0; i <= tNumberOfAdditionalArrayValues; ++i) {
        uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
        /*
         * Decode in 32/64 bit chunks. Only the last chunk can contain less than 32/64 bits
         */
        if (tNumberOfBitsForOneDecode > BITS_IN_RAW_DATA_TYPE) {
            tNumberOfBitsForOneDecode = BITS_IN_RAW_DATA_TYPE;
        }
        if (tSpaceTicksLong > 0) {
            /*
             * Here short and long space durations found. So we have PULSE_DISTANCE or PULSE_DISTANCE_WIDTH.
             * PULSE_DISTANCE_WIDTH can successfully be decoded by only using the timings of space :-).
             */
            decodedIRData.protocol = PULSE_DISTANCE; // NEC etc. + PULSE_DISTANCE_WIDTH
#if defined(USE_THRESHOLD_DECODER)
            decodeWithThresholdPulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex,
                    ((tSpaceMicrosLong + tSpaceMicrosShort) / 2) + MARK_EXCESS_MICROS, // MARK_EXCESS_MICROS is 0 here if not explicitly specified by user
#else
            decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tSpaceMicrosLong,
#endif
                    PROTOCOL_IS_PULSE_DISTANCE,
#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
                    PROTOCOL_IS_MSB_FIRST
#else
                    PROTOCOL_IS_LSB_FIRST
#endif
                    );

        } else {
            /*
             * Here no long space duration but short and long mark durations found. So we have PULSE_WIDTH.
             * Use timing of marks for decoding here.
             * This else case will most likely never be used, but it only requires 12 bytes additional programming space :-)
             */
            decodedIRData.protocol = PULSE_WIDTH; // Sony
#if defined(USE_THRESHOLD_DECODER)
            decodeWithThresholdPulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex,
                    ((tMarkMicrosLong+ tMarkMicrosShort) / 2) - MARK_EXCESS_MICROS, // MARK_EXCESS_MICROS is 0 here if not explicitly specified by user
#else
            decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkMicrosLong,
#endif
                    PROTOCOL_IS_PULSE_WIDTH,
#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
                    PROTOCOL_IS_MSB_FIRST
#else
                    PROTOCOL_IS_LSB_FIRST
#endif
                    );

        }
#if defined(LOCAL_DEBUG)
        Serial.print(F("PULSE_WIDTH: "));
        Serial.print(F("decodedRawData=0x"));
        Serial.println(decodedIRData.decodedRawData, HEX);
#endif
        // fill array with decoded data
        decodedIRData.decodedRawDataArray[i] = decodedIRData.decodedRawData;
        tStartIndex += (2 * BITS_IN_RAW_DATA_TYPE);
        tNumberOfBits -= BITS_IN_RAW_DATA_TYPE;
    }

#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
    decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
#endif

    // Check for repeat. Check also for equality of last DecodedRawData.
    if (decodedIRData.initialGapTicks < DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS / MICROS_PER_TICK
            && decodedIRData.decodedRawDataArray[tNumberOfAdditionalArrayValues] == lastDecodedRawData) {
        decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
    }
    lastDecodedRawData = decodedIRData.decodedRawData;

    /*
     * Store timing data to reproduce frame for sending
     */
    decodedIRData.DistanceWidthTimingInfo.HeaderMarkMicros = (irparams.rawbuf[1] * MICROS_PER_TICK);
    decodedIRData.DistanceWidthTimingInfo.HeaderSpaceMicros = (irparams.rawbuf[2] * MICROS_PER_TICK);
    decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosShort;
    decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosShort;
    if (tMarkMicrosLong != 0) {
        if (tSpaceMicrosLong == 0) {
            // PULSE_DISTANCE, Sony
            decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong;
            decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort;
        } else {
            // PULSE_DISTANCE_WIDTH, we have 4 distinct values here
            // Assume long space for a one when we have PulseDistanceWidth like for RS232, where a long inactive period (high) is a 1
            decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong;
            decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort;
            decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosLong;
//            // Assume long mark for a one when we have PulseDistanceWidth
//            decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort;
//            decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosLong;
//            decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong;
        }
    } else {
        // PULSE_WIDTH, NEC etc.
        // Here tMarkMicrosLong is 0 => tSpaceMicrosLong != 0
        decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort;
        decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong;
    }

#if defined(LOCAL_DEBUG)
    Serial.print(F("DistanceWidthTimingInfo="));
    IrReceiver.printDistanceWidthTimingInfo(&Serial, &decodedIRData.DistanceWidthTimingInfo);
    Serial.println();
#endif
    return true;
}

/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_DISTANCE_WIDTH_HPP
