File: RC6RC5Decoder

Details

File: RC6RC5Decoder.cs
Dir: http://www.ofitselfso.com/IRSky/IRSky.php
Date: Mon, Dec 15, 2014
using System;
using System.Threading;
using BBBCSIO;

/// +------------------------------------------------------------------------------------------------------------------------------+
/// |                                                   TERMS OF USE: MIT License                                                  |
/// +------------------------------------------------------------------------------------------------------------------------------|
/// |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.                         |
/// +------------------------------------------------------------------------------------------------------------------------------+

namespace IRSkyBBB
{
    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
    /// <summary>
    /// 
    /// RC6RC5Decoder - Provides signal decoding functionality for SKY+ IR Remotes 
    ///                    but should be adaptable for any IR Remote using the RC5 or 
    ///                    RC6 protocol
    /// 
    /// Some notes on SKY+ remotes
    /// 
    /// SKY+ remotes operate in two modes: SKY mode and TV mode. SKY Mode is toggled on
    /// when the SKY button is pressed and most buttons will then emit IR codes which
    /// use the RC6-6-20 protocol. The TV mode is toggled on when the TV button is 
    /// pressed. The IR codes emitted in this mode are intended to control a TV, not
    /// the Sky box, and since TV's use a wide variety of IR protocols the codes output
    /// are dependent on the current TV setting of the SKY remote. If the SKY remote
    /// is configured to a Phillips TV then the output codes will be standard
    /// RC5. It is up to the user to configure the remote to the type of TV (see below)
    /// 
    /// There are specific exceptions. Some buttons (Volume+/- and Mute) always emit
    /// the configured TV codes even in SKY mode. Some buttons do not send any codes in
    /// TV mode (Play, Pause) and some (TV Guide, Box Office) always toggle SKY mode 
    /// back on and then send a SKY mode code. 
    /// 
    /// NOTE: In order for this code to properly interpret all buttons on the SKY+ remote
    /// the SKY + remote must be set to a Phillips TV. This makes the buttons send RC5
    /// IR remote codes in TV Mode. To set a SKY+ remote into Phillips RC5 TV mode
    /// have a look at the link below
    /// 
    /// http://www.skyremotecodes.co.uk/Philips-sky-remote-codes
    /// 
    /// This signal interpretation is designed to be both accurate and lossy. What this 
    /// means is that it may not get every signal the IR remote sends but for the IR codes 
    /// it does receive, the processing will be complete and reasonably error checked. The 
    /// reception and processing of every IR code is not usually expected when using a remote  
    /// - they repeat the codes as long as the button is pressed in order to ensure something  
    /// makes it through. However, if a code is received, it should be accurate. 
    /// 
    /// Some links which may help you understand the RC5, RC6 and RC6 Mode 6 protocols
    /// 
    /// RC6 PROTOCOL
    /// http://www.sbprojects.com/knowledge/ir/rc6.php
    /// http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/
    /// 
    /// RC6 MODE 6A PROTOCOL
    /// http://www.guiott.com/wrc/RC6-6.html
    /// 
    /// RC5 PROTOCOL
    /// http://www.sbprojects.com/knowledge/ir/rc5.php
    /// 
    /// Ultimately every IR code received is placed in the latestProcessedIRSignalBytes 
    /// int variable. This is overwritten as new ones come in and are processed. The
    /// processing is done in the interrupt of the last pulse of the signal. It is
    /// up to the external code to poll this value often enough to get each value
    /// if that is what it needs.
    /// 
    /// The data output is the complete information in the signal including SMODE
    /// CONTROL, TOGGLE and INFO fields as appropriate to the protocol. Also included
    /// is a field which indicates if the signal came in as RC6 or RC5. All of this
    /// data is bundled up and stuffed into an Int32 for easy transport. See the 
    /// comments below on OUTPUT DATA for the structure of this Int32
    /// 
    /// NOTE: the SKY+ remote does not seem to send a toggle bit in RC6 - it is
    /// always 0. In RC5 mode the toggle bit will change as expected
    /// 
    /// This code is written in C#, for a Beaglebone Black and uses the BBBCSIO Library
    /// 
    /// Tested with a Sparkfun IR Receiver Breakout SEN-08554 and a SKY+ Remote Rev4
    ///    Also tested with a SKY+ Remote Rev6
    /// 
    /// </summary>
    /// <history>
    ///   20 Jan 14  Cynic - originally written
    /// </history>
    public class RC6RC5Decoder
    {
        // this is the interrupt on which the IR Receiver places its pulses
        public InterruptPortMM edgeInterrupt = null;
        
        // this is the decoder for the IR pulses
        public ManchesterDecoder manchesterDecoder = null;
        
        // this is the number of 1T time periods we expect to see in a RC6-6-20 signal
        // NOTE: if the last bit is a 0 we will get a H->L transition to mark it.
        // If it is a 1, manchester encoding is already low so it does not go high again
        // this means we do not get a 60th trailing pulse to mark the end of the last
        // odd bit. We could add this ourselves from the timing but as you will see 
        // when you read the comments in the ProcessPulsesReceivedRC6_6_20 function 
        // we can safely ignore its non-presence
        private const int SKYPLUS_RC6_6_20_PULSECOUNT = 59;
        // this is the number of 1T time periods we expect to see in a RC5 signal
        private const int SKYPLUS_RC5_PULSECOUNT = 52;

        // used to embed the protocol type in the output databytes
        private const int RC5_PROTOCOLFLAG = 1;
        private const int RC6_PROTOCOLFLAG = 2;

        // this is the number of complete and valid IR codes we have processed
        private int processedIRCodeCount = 0;
        // this is the number of processed IR Codes that were consumed by an 
        // external object calling GetData();
        private int consumedIRCodeCount = 0;
        // if true, data is ready. If false - no new data.
        // the GetData() call will reset this to false
        private bool dataReady=false;

        // OUTPUT DATA
        // latestProcessedIRSignalBytes is the data which we have processed out of 
        // the IR pulse stream we represent it as an int (4 bytes) because that is 
        // such an handy way of passing information around. It is a value type - not 
        // an object and so we do not have the object creation overhead and subsequent
        // garbage collection etc.

        // The data will be returned as nibbles and bytes. The meaning is dependent
        // on the protocol being interpreted. All diagrams are MSB to LSB each [] is 
        // a byte

        // FOR RC6-6-20 (most keys on SKY+ remotes in SKY mode) 
        // [protocol|modeField][controlField][trailerField|sField][infoField]
        // where 
        //  protocol     - 4 bit nibble hardcoded to RC6_PROTOCOLFLAG
        //  modefield    - 4 bit nibble with the mode number. will be 6 for RC6-6
        //  controlField - 8 bits of the control field information
        //  trailerField - 4 bit nibble which will be either 1 or 0 
        //  sField       - 4 bit nibble containing the sField info 0x0c for SKY+ remotes
        //  infoField    - 8 bits of information from the information field

        // FOR RC5 (most keys on SKY+ remotes in TV mode with Phillips TV configured) 
        // [protocol|0000][controlField][trailerField|0000][infoField]
        // where 
        //  protocol     - 4 bit nibble hardcoded to RC5_PROTOCOLFLAG
        //  controlField - 8 bits of the control field information
        //  trailerField - 4 bit nibble which will be either 1 or 0 
        //  infoField    - 8 bits of information from the information field

        private int latestProcessedIRSignalBytes = 0;
        // used to sync access to the returning data
        private object lockObject = new object();
        // for diagnostics
        public int lastProcessPulsesResult = 0;

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="eventInterruptModeIn">The interrupt mode</param>
        /// <param name="gpioIn">The GPIO this interrupt is on.
       /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public RC6RC5Decoder(GpioEnum gpioIn, InterruptMode eventInterruptModeIn)
        {
            // build the interrupt, note we are trapping both rising and falling interrupts here
            edgeInterrupt = new InterruptPortMM(gpioIn, eventInterruptModeIn);
            // Set the interrupt handler. 
            edgeInterrupt.OnInterrupt += new InterruptEventHandlerMM(HandleEdgeInterrupt);
 
            // we will not get any data if it is not enabled
            edgeInterrupt.EnableInterrupt();

            // build the decoder class
            manchesterDecoder = new ManchesterDecoder();
            manchesterDecoder.ResetForNewInput(11);
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Handle an edge interrupt. This is the function that will get the incoming
        /// interrupts from the class edgeInterrupt.
        /// 
        /// The basic operation is to call the decoder for every pulse. When the decoder
        /// is done we see how many pulses we have. If everything looks right for an
        /// RC5 or RC6 signal we process it into bits and place it in the 
        /// latestProcessedIRSignalByte variable for consumption by some process that polls 
        /// for it. The update and read of latestProcessedIRSignalBytes variable is done
        /// in a lock.
        /// 
        /// SPEED matters here - you are in an interrupt - do not faff around
        /// 
        /// </summary>
        /// <param name="evGpio">The gpio the event is configured on</param>
        /// <param name="evState">The event state (1 or 0)</param>
        /// <param name="evTime">The event datetime</param>
        /// <param name="evData">The event data structure</param>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public void HandleEdgeInterrupt(GpioEnum evGpio, bool evState, DateTime evDateTime, EventData evData)
        {            

            uint data2 ;
            // should never happen
            if (evData == null) return;
            // should never happen
            if (evData.PortObject == null) return;

            // set the data2 and time variables so we can be consistent with the standard IRSky Implementation
            if (evData.EvValue == 0) data2 = 0;
            else data2 = 1;

            // we differentiate based on rising or falling pulses
            if (data2 == 1)
            {
                manchesterDecoder.RecordPulseEvent(PulseTypeEnum.PULSE_FALLING, evDateTime);
            }
            else
            {
                manchesterDecoder.RecordPulseEvent(PulseTypeEnum.PULSE_RISING, evDateTime);
            }

            // by the time we get here we have added the signal content to the pulseArray
            // in the decoder and that array will contain a sequence of 1T pulse information
            // see the comments in that class for more details

            // certain byte counts are triggers to tell us process data
            if ((manchesterDecoder.RC6Detected==false) && (manchesterDecoder.PulsesReceived == SKYPLUS_RC5_PULSECOUNT))
            {
                // we may well have an even RC5 command from the sky+ IR remote
                lastProcessPulsesResult = ProcessPulsesReceivedRC5();
                // always reset - we are done here
                manchesterDecoder.ResetForNewInput(22);
            }
            else if ((manchesterDecoder.RC6Detected==true) && (manchesterDecoder.PulsesReceived == SKYPLUS_RC6_6_20_PULSECOUNT))
            {
                // we may well have an even RC6-6-20 command from the sky+ IR remote
                lastProcessPulsesResult = ProcessPulsesReceivedRC6_6_20();
                // always reset - we are done here
                manchesterDecoder.ResetForNewInput(23);
            }

            // make sure we clear
            evData.PortObject.ClearInterrupt();
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Processes the pulses in the signal decoder as if it were an RC5 signal
        /// 
        /// NOTE: we are still in the interrupt when this is called. SPEED is of the essence!
        /// 
        /// </summary>
        /// <returns>z success - ir code processed, nz fail code provides reason</returns>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        private int ProcessPulsesReceivedRC5()
        {
            int pulseArray = manchesterDecoder.PulsesReceived;
            int trailerField = 0;
            int infoField = 0;
            int controlField = 0;
            
            // there are an assumed two -2 1T pulses here which will not be recorded by the 
            // decoder. We begin processing on the rising part of the first pulse

            // we require the first 2 values to be "+2" this indicates that the first start 
            // pulse happened. 
            if (manchesterDecoder.pulseArray[0] != 2) return 10;
            // we can assume the following and do not need to test - see notes in the decoder
            // if (manchesterDecoder.pulseArray[1] != 2) return false;

            // we require the next two values to be "-2" this indicates that the low
            // part of the second start pulse happened. 
            if (manchesterDecoder.pulseArray[2] != -2) return 11;
            // the line below can be assumed
            // if (manchesterDecoder.pulseArray[3] != -2) return false;

            // we require the next two values to be "+2" or +4 this indicates that the high
            // part of the second start pulse happened. 
            if (((manchesterDecoder.pulseArray[4] == 2) || (manchesterDecoder.pulseArray[4] == 4))==false) return 12;
            // the line below can be assumed
            // if (manchesterDecoder.pulseArray[5] != 2 ...or... 4) return false;

            // A NOTE on how we decode the pulses into bits. Once we get past the first start
            // pulses with its missing leading low, each bit is represented by four sequential 
            // values in the manchesterDecoder.pulseArray array putting those in there is pretty  
            // much the entire purpose of the manchesterDecoder - review the comments there

            // These pulses will either be LowLow then HighHigh represented by a -ve-ve+ve+ve
            // which in RC5 is a 1. Or a HighHigh then LowLow represented by a +ve+ve-ve-ve
            // which in RC5 is a 0. No other permutations permitted by the protocol.

            // If manchesterDecoder.pulseArray[10] is < 0 and manchesterDecoder.pulseArray[12]>0
            // we have a 1 bit. Of course this assumes we know to consider [10] and [12] as the bit
            // and not [11],[13] Or [12],[14]. However, at this point we are pretty sure we know where 
            // we are in the signal so as long as we did not miss the start pulses we are pretty
            // sure we are interpreting correctly

            // So the upshot is that for any one bit we do not have to look at all 1T values that
            // make it up. Remember we are optimizing for speed here. All we need to do is look at
            // the even numbered array entries. If this is -ve we have a 0, if it is +ve we have a 1
            // this saves time.

            // the next 4 slots will be the Trailer - as discussed above all we need to do is look at 
            // the first slot
            if (manchesterDecoder.pulseArray[6] < 0) trailerField = 0;
            else if (manchesterDecoder.pulseArray[6] > 0) trailerField = 1;
            else return 13;

            // now we parse the control field. This begins at array entry [10] 
            // or 10T if we are counting that way, We build the control byte 
            // from the ones and zeros
            if (manchesterDecoder.pulseArray[10] < 0) controlField++;     // msb
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[14] < 0) controlField++;
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[18] < 0) controlField++;
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[22] < 0) controlField++;
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[26] < 0) controlField++;     // lsb

            // now we parse the command data. This begins at array entry [30] 
            // or 30T if we are counting that way, We build the info byte 
            // from the ones and zeros
            if (manchesterDecoder.pulseArray[30] < 0) infoField++;     // msb
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[34] < 0) infoField++;
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[38] < 0) infoField++;
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[42] < 0) infoField++;
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[46] < 0) infoField++;
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[50] < 0) infoField++;     // lsb

            // set the output data, we do this inside a lock
            lock (lockObject)
            {
                // build our output int - in the format is below [is a byte] pipes "|" are nibble separators
                // [protocol|0000][controlField][trailerField|0000][infoField]
                latestProcessedIRSignalBytes = (RC5_PROTOCOLFLAG << 28) + (controlField << 16) + (trailerField << 12) + infoField;
                processedIRCodeCount++;
                dataReady = true;
            }

            // it is all good
            return 0;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Processes the pulses in the signal decoder as if it were an RC6-6-20 signal
        /// 
        /// NOTE: we are still in the interrupt when this is called. SPEED is of the essence!
        /// 
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        private int ProcessPulsesReceivedRC6_6_20()
        {
            int pulseArray = manchesterDecoder.PulsesReceived;
            int modeField =0;
            int trailerField = 0;
            int infoField = 0;
            int controlField = 0;
            int sField = 0;

            // we require the first 6 values to be "+6" this indicates that the 6T starting 
            // pulse happened. 
            if (manchesterDecoder.pulseArray[0] != 6) return 100;
            // we can assume the following and do not need to test - see notes in the decoder
            // if (manchesterDecoder.pulseArray[1] != 6) return false;
            // if (manchesterDecoder.pulseArray[2] != 6) return false;
            // if (manchesterDecoder.pulseArray[3] != 6) return false;
            // if (manchesterDecoder.pulseArray[4] != 6) return false;
            // if (manchesterDecoder.pulseArray[5] != 6) return false;

            // we require the next two values to be "-2" this indicates that the 2T low
            // part of the start pulse happened. 
            if (manchesterDecoder.pulseArray[6] != -2) return 200;
            // the line below can be assumed
            // if (manchesterDecoder.pulseArray[7] != -2) return false;

            // A NOTE on how we decode the pulses into bits. Once we get past the leader
            // (and other than the trailer bit special case at positions 16-19) each bit
            // is represented by two sequential values in the manchesterDecoder.pulseArray array
            // that is pretty much the entire purpose of the manchesterDecoder - review the comments there

            // These pulses will either be a High to Low (H->L) transition represented by a +ve and
            // a subsequent -ve. Or a Low to High (L->H) represented by a -ve and a subsequent
            // +ve. Thats it. There is never a case of +ve,+ve or -ve,-ve outside of the leader
            // and trailer. This is not permitted by the protocol and if it occurred would 
            // immediately reset the decoder software so it will never appear in there.

            // so if manchesterDecoder.pulseArray[10] is > 0 and manchesterDecoder.pulseArray[11]<0
            // we have a 1 bit. Of course this assumes we know to consider [10] and [11] as the bit
            // and not [11],[12]. We know to start on the even entries because (once again) the
            // decoder has busted the signal up on 1T boundaries and all pulses, even the special cases
            // take an even number of 1T time periods to complete - the protocol specifies this. 

            // So the upshot is that for regular single bit transitions, we do not have to look at
            // both values. Remember we are optimizing for speed here. All we need to do is look at
            // the even numbered array entries. If this is -ve we have a 0, if it is +ve we have a one
            // this saves time.

            // check the start bit is 1 (+ve,-ve) - the protocol requires this
            if (manchesterDecoder.pulseArray[8] <=0) return 300;

            // now the mode bits in slots [10], [12] and [14]
            if (manchesterDecoder.pulseArray[10] > 0) modeField = modeField + 4;  // msb of 3 mode bits
            if (manchesterDecoder.pulseArray[12] > 0) modeField = modeField + 2;  // middle of 3 mode bits
            if (manchesterDecoder.pulseArray[14] > 0) modeField = modeField + 1;  // lsb of 3 mode bits

            // the trailer bit is a special case and will be the next 4 slots. These this will 
            // either be a -2-2+2+2 which signals a 0 bit (L->H) over two 2T periods) or a 
            // +2+2-2-2 which signals a 1 bit (H->L) Either way we can figure out what it is
            // just by looking at the first value. We do both tests because we want
            // to check for a +/- 2 and fail if that is not there
            if (manchesterDecoder.pulseArray[16] < 0) trailerField = 0;
            else if (manchesterDecoder.pulseArray[16] > 0) trailerField = 1;
            else return 400;

            // now we parse the control field. This begins at array entry [20] 
            // or 20T if we are counting that way, We build the control byte 
            // from the ones and zeros
            if (manchesterDecoder.pulseArray[20] > 0) controlField++;     // msb
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[22] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[24] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[26] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[28] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[30] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[32] > 0) controlField++;     
            controlField <<= 1;
            if (manchesterDecoder.pulseArray[34] > 0) controlField++;     // lsb

            // now we parse the S field. This begins at array entry [36] 
            // or 36T if we are counting that way, We build the s nibble 
            // from the ones and zeros
            if (manchesterDecoder.pulseArray[36] > 0) sField++;     // msb
            sField <<= 1;
            if (manchesterDecoder.pulseArray[38] > 0) sField++;
            sField <<= 1;
            if (manchesterDecoder.pulseArray[40] > 0) sField++;
            sField <<= 1;
            if (manchesterDecoder.pulseArray[42] > 0) sField++;     // lsb

            // now we parse the information data. This begins at array entry [44] 
            // or 44T if we are counting that way, We build the info byte 
            // from the ones and zeros
            if (manchesterDecoder.pulseArray[44] > 0) infoField++;     // msb
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[46] > 0) infoField++;     
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[48] > 0) infoField++;     
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[50] > 0) infoField++;     
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[52] > 0) infoField++;     
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[54] > 0) infoField++;    
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[56] > 0) infoField++;     
            infoField <<= 1;
            if (manchesterDecoder.pulseArray[58] > 0) infoField++;     // lsb

            // set the output data, we do this inside a lock
            lock (lockObject)
            {
                // build our output int - the format is below [is a byte], pipes "|" are nibble separators
                // [protocol|modeField][controlField][trailerField|sField][infoField]
                latestProcessedIRSignalBytes = (RC6_PROTOCOLFLAG << 28) + (modeField << 24) + (controlField << 16) + (trailerField << 12)+ (sField << 8) + infoField;
                processedIRCodeCount++;
                dataReady = true;
            }

            // it is all good
            return 0;
        }
 
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Detects if data is available
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public bool IsDataAvailable()
        {
            // no need to lock() here. it is either ready or it isn't
            return dataReady;
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the 4 data bytes as an int. The meaning of those bytes is dependent on
        /// the protocol interpreted. See the comments. If this is called when 
        /// DataReady != true it will just return whatever happens to be in the 
        /// latestProcessedIRSignalBytes value at that time. It does not check.
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public int GetData()
        {
            // we do this inside a lock
            lock (lockObject)
            {
                dataReady=false;
                consumedIRCodeCount++;
                return latestProcessedIRSignalBytes;
            }
        }   

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the number of times we processed a complete IR code. Does not indicate
        /// if it was actually consumed by a GetData() call
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public int ProcessedIRCodeCount
        {
            get
            {
                return processedIRCodeCount;
            }
        }   

       /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the number of times we processed a complete IR code. This is the number
        /// actually consumed by a GetData() call
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public int ConsumedIRCodeCount
        {
            get
            {
                return consumedIRCodeCount;
            }
        }   

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Gets the number of times we reset for a new code. This value includes
        /// corrupted streams that were discarded as well as complete IR codes that
        /// were processed and/or consumed
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public int ResetCount
        {
            get
            {
                return manchesterDecoder.PulseArrayResetCount;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Returns a marker code indicating the reason for the last reset
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public int LastResetReason
        {
            get
            {
                // this the marker indicating the reason for the last reset
                return manchesterDecoder.LastResetReason;
            }
        }

        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Forces a reset on the manchesterDecoder object
        /// </summary>
        /// <history>
        ///   20 Jan 14  Cynic - originally written
        /// </history>
        public void ForceReset()
        {
            // just reset it
            manchesterDecoder.ResetForNewInput(99);
        }           
    }
}
HTML Code Generated by CoDocker v00.90 Beta