You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.8 KiB

/* 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<t1)){
avail =1;
} else {
return ERROR;
}
}
(*used)++;
if (*used >= avail) {
*used = 0;
(offset)++;
}
return val;
}