/* IRLibDecodeBase.cpp * Part of IRLib Library for Arduino receiving, decoding, and sending * infrared signals. See COPYRIGHT.txt and LICENSE.txt for more information. */ /* * This module contains the base classes for decoding. You will not create instances * of these classes, rather you will use them as base classes in creating derived * protocol specific decoders. */ #include "IRLibDecodeBase.h" #include "IRLibHardware.h" IRdecodeBase::IRdecodeBase(void) { recvGlobal.decoderWantsData=false; //turned on by enableIRIn. recvGlobal.decodeBuffer=recvGlobal.recvBuffer;//default buffer ignoreHeader=false; resetDecoder(); }; /* * Reinitialize the decoder clearing out previous data. */ void IRdecodeBase::resetDecoder(void) { protocolNum= UNKNOWN; value=0; address=0; bits=0; }; void IRdecodeBase::dumpResults(bool verbose) { int i;uint32_t Extent;int interval; if((protocolNum>89) || (protocolNum<=LAST_PROTOCOL)) { Serial.print(F("Decoded ")); Serial.print(Pnames(protocolNum)); Serial.print(F("(")); Serial.print(protocolNum,DEC); Serial.print(F("): Value:")); Serial.print(value, HEX); Serial.print(F(" Adrs:" )); Serial.print(address, HEX); }; Serial.print(F(" (")); Serial.print(bits, DEC); Serial.print(F(" bits) ")); if(recvGlobal.didAutoResume) Serial.print(F("Auto Resumed")); Serial.println(); if(!verbose) return; Serial.print(F("Raw samples(")); Serial.print(recvGlobal.decodeLength, DEC); Serial.print(F("): Gap:")); Serial.println(recvGlobal.decodeBuffer[0], DEC); Serial.print(F(" Head: m")); Serial.print(recvGlobal.decodeBuffer[1], DEC); Serial.print(F(" s")); Serial.println(recvGlobal.decodeBuffer[2], DEC); int LowSpace= 32767; int LowMark= 32767; int HiSpace=0; int HiMark= 0; Extent=recvGlobal.decodeBuffer[1]+recvGlobal.decodeBuffer[2]; for (i = 3; i < recvGlobal.decodeLength; i++) { Extent+=(interval= recvGlobal.decodeBuffer[i]); if (i % 2) { LowMark=min(LowMark, interval); HiMark=max(HiMark, interval); Serial.print(i/2-1,DEC); Serial.print(F(":m")); } else { if(interval>0)LowSpace=min(LowSpace, interval); HiSpace=max (HiSpace, interval); Serial.print(F(" s")); } Serial.print(interval, DEC); int j=i-1; if ((j % 2)==1)Serial.print(F("\t")); if ((j % 4)==1)Serial.print(F("\t ")); if ((j % 8)==1)Serial.println(); if ((j % 32)==1)Serial.println(); } Serial.println(); Serial.print(F("Extent=")); Serial.println(Extent,DEC); Serial.print(F("Mark min:")); Serial.print(LowMark,DEC);Serial.print(F("\t max:")); Serial.println(HiMark,DEC); Serial.print(F("Space min:")); Serial.print(LowSpace,DEC);Serial.print(F("\t max:")); Serial.println(HiSpace,DEC); Serial.println(); } /* We use a generic routine because most protocols have the same basic structure. * Previous versions of this method would handle protocols with variable marks * or variable spaces. However we have discovered that only Sony protocol uses * variable marks so we have stripped out that portion of the code. This changes * the number of necessary parameters. We no longer need markOne and markZero * because they are both the same which we will pass in markData. Note this new * version will handle up to 48 bits putting the most significant 16 bits in * "this.address" in the least significant 32 bits in "this.data". We could have * allowed for 64 bit but we have not seen generic protocols that large. */ bool IRdecodeBase::decodeGeneric(uint8_t expectedLength, uint16_t headMark, uint16_t headSpace, uint16_t markData, uint16_t spaceOne, uint16_t spaceZero) { resetDecoder();//This used to be in the receiver getResults. // If "expectedLenght" or "headMark" or "headSpace" are zero or if "ignoreHeader" // is true then don't perform these tests. This is because some protocols need // to do their own custom header work. uint64_t data = 0; bufIndex_t Max=recvGlobal.decodeLength-1; if (expectedLength) { if (recvGlobal.decodeLength != expectedLength) return RAW_COUNT_ERROR; } if(!ignoreHeader) { if (headMark) { if (!MATCH(recvGlobal.decodeBuffer[1],headMark)) return HEADER_MARK_ERROR(headMark); } } if (headSpace) { if (!MATCH(recvGlobal.decodeBuffer[2],headSpace)) return HEADER_SPACE_ERROR(headSpace); } offset=3;//skip initial gap plus two header items while (offset < Max) { if (!MATCH (recvGlobal.decodeBuffer[offset],markData)) return DATA_MARK_ERROR(markData); offset++; if (MATCH(recvGlobal.decodeBuffer[offset],spaceOne)) { data = (data << 1) | 1; } else if (MATCH (recvGlobal.decodeBuffer[offset],spaceZero)) { data <<= 1; } else return DATA_SPACE_ERROR(spaceZero); offset++; } bits = (offset - 1) / 2 -1;//didn't encode stop bit // Success value = (uint32_t)data; //low order 32 bits address = (uint16_t) (data>>32); //high order 16 bits return true; } /* * These MATCH methods used to be macros but we saved nearly * 800 bytes of program space by making them actual methods. */ bool IRdecodeBase::MATCH(int16_t val,int16_t expected){ #ifdef IRLIB_USE_PERCENT return (val >= (uint16_t)(expected*(1.0-PERCENT_TOLERANCE/100.0))) && (val <= (uint16_t)(expected*(1.0+PERCENT_TOLERANCE/100.0))); #else return ABS_MATCH(val,expected,DEFAULT_ABS_TOLERANCE); #endif } bool IRdecodeBase::ABS_MATCH(int16_t val,int16_t expected,int16_t tolerance){ return (val >= (expected-tolerance)) && (val <= (expected+tolerance)); } /* * The RC5 and RC6 and similar protocols used phase encoding and leave a * routine to extract zeros and ones. This routine gets one undecoded * level at a time from the raw buffer. personally 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 getRClevel will return MARK, MARK, SPACE. * The variables "offset" and "used" are updated to keep track of the * current position. The variable "t1" is the time interval for a single * bit in microseconds. Returns ERROR if the measured time interval is * not a multiple of "t1". */ IRdecodeRC::RCLevel IRdecodeRC::getRClevel(uint8_t *used, const uint16_t t1) { if (offset >= recvGlobal.decodeLength) { // After end of recorded buffer, assume SPACE. return SPACE; } uint16_t width = recvGlobal.decodeBuffer[offset]; IRdecodeRC::RCLevel val; if ((offset) % 2) val=MARK; else val=SPACE; uint8_t avail; if (MATCH(width, t1)) { avail = 1; } else if (MATCH(width, 2*t1)) { avail = 2; } else if (MATCH(width, 3*t1)) { avail = 3; } else { if((ignoreHeader) && (offset==1) && (width= avail) { *used = 0; (offset)++; } return val; }