Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add auto-calibrating to most decoders #268

Merged
merged 14 commits into from
Jul 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,58 @@ bool IRrecv::decodeHash(decode_results *results) {
results->decode_type = UNKNOWN;
return true;
}

// Match & decode the typical data section of an IR message.
// The data value constructed as the Most Significant Bit first.
//
// Args:
// data_ptr: A pointer to where we are at in the capture buffer.
// nbits: Nr. of data bits we expect.
// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit.
// onespace: Nr. of uSeconds in an expected space signal for a '1' bit.
// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit.
// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit.
// Returns:
// A match_result_t structure containing the success (or not), the data value,
// and how many buffer entries were used.
match_result_t IRrecv::matchData(volatile uint16_t *data_ptr, uint16_t nbits,
uint16_t onemark, uint32_t onespace,
uint16_t zeromark, uint32_t zerospace) {
match_result_t result;
result.success = false;
result.data = 0;
if (onemark == zeromark) { // Is this space encoded data format?
for (result.used = 0;
result.used < nbits * 2;
result.used += 2, data_ptr++) {
if (!matchMark(*data_ptr, onemark))
return result; // Fail
data_ptr++;
if (matchSpace(*data_ptr, onespace))
result.data = (result.data << 1) | 1;
else if (matchSpace(*data_ptr, zerospace))
result.data <<= 1;
else
return result; // Fail
}
result.success = true;
} else if (onespace == zerospace) { // Is this mark encoded data format?
for (result.used = 0;
result.used < nbits * 2;
result.used += 2, data_ptr++) {
if (matchMark(*data_ptr, onemark))
result.data = (result.data << 1) | 1;
else if (matchMark(*data_ptr, zeromark))
result.data <<= 1;
else
return result; // Fail
data_ptr++;
if (!matchSpace(*data_ptr, onespace))
return result; // Fail
}
result.success = true;
}
return result;
}

// End of IRrecv class -------------------
10 changes: 10 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ typedef struct {
uint8_t overflow; // Buffer overflow indicator.
} irparams_t;

// results from a data match
typedef struct {
bool success; // Was the match successful?
uint64_t data; // The data found.
uint16_t used; // How many buffer positions were used.
} match_result_t;

// Classes

// Results returned from the decoder
Expand Down Expand Up @@ -91,6 +98,9 @@ class IRrecv {
uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS);
bool matchSpace(uint32_t measured_ticks, uint32_t desired_us,
uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS);
match_result_t matchData(volatile uint16_t *data_ptr, uint16_t nbits,
uint16_t onemark, uint32_t onespace,
uint16_t zeromark, uint32_t zerospace);
bool decodeHash(decode_results *results);
#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO)
bool decodeNEC(decode_results *results, uint16_t nbits = NEC_BITS,
Expand Down
12 changes: 12 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#include "IRrecv.h"

// Reverse the order of the requested least significant nr. of bits.
// Args:
Expand Down Expand Up @@ -56,3 +57,14 @@ void serialPrintUint64(uint64_t input, uint8_t base) {
Serial.print(str);
#endif
}

// Calculate the tick time in uSeconds based on the input time & factor.
//
// Args:
// input: Nr. of capture ticks.
// factor: Nr. of multiples of the common tick divisor the input should be.
// Returns:
// A uint32_t containing the common tick time in uSeconds.
uint32_t calcTickTime(uint16_t input, uint16_t factor) {
return (input * USECPERTICK) / factor;
}
1 change: 1 addition & 0 deletions src/IRutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@

uint64_t reverseBits(uint64_t input, uint16_t nbits);
void serialPrintUint64(uint64_t input, uint8_t base);
uint32_t calcTickTime(uint16_t input, uint16_t factor);

#endif // IRUTILS_H_
44 changes: 29 additions & 15 deletions src/ir_Coolix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtimer.h"
#include "IRutils.h"

// CCCCC OOOOO OOOOO LL IIIII XX XX
// CC C OO OO OO OO LL III XX XX
Expand All @@ -17,12 +18,20 @@
// Pulse parms are *50-100 for the Mark and *50+100 for the space
// First MARK is the one after the long gap
// pulse parameters in usec
#define COOLIX_BIT_MARK 560U // Approximately 21 cycles at 38kHz
#define COOLIX_ONE_SPACE COOLIX_BIT_MARK * 3U
#define COOLIX_ZERO_SPACE COOLIX_BIT_MARK * 1U
#define COOLIX_HDR_MARK COOLIX_BIT_MARK * 8U
#define COOLIX_HDR_SPACE COOLIX_BIT_MARK * 8U
#define COOLIX_MIN_GAP COOLIX_HDR_SPACE + COOLIX_ZERO_SPACE
#define COOLIX_TICK 560U // Approximately 21 cycles at 38kHz
#define COOLIX_BIT_MARK_TICKS 1U
#define COOLIX_BIT_MARK (COOLIX_BIT_MARK_TICKS * COOLIX_TICK)
#define COOLIX_ONE_SPACE_TICKS 3U
#define COOLIX_ONE_SPACE (COOLIX_ONE_SPACE_TICKS * COOLIX_TICK)
#define COOLIX_ZERO_SPACE_TICKS 1U
#define COOLIX_ZERO_SPACE (COOLIX_ZERO_SPACE_TICKS * COOLIX_TICK)
#define COOLIX_HDR_MARK_TICKS 8U
#define COOLIX_HDR_MARK (COOLIX_HDR_MARK_TICKS * COOLIX_TICK)
#define COOLIX_HDR_SPACE_TICKS 8U
#define COOLIX_HDR_SPACE (COOLIX_HDR_SPACE_TICKS * COOLIX_TICK)
#define COOLIX_MIN_GAP_TICKS (COOLIX_HDR_MARK_TICKS + \
COOLIX_ZERO_SPACE_TICKS)
#define COOLIX_MIN_GAP (COOLIX_MIN_GAP_TICKS * COOLIX_TICK)

#if SEND_COOLIX
// Send a Coolix message
Expand Down Expand Up @@ -102,23 +111,28 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
return false; // We can't possibly capture a Coolix packet that big.

// Header
if (!matchMark(results->rawbuf[offset++], COOLIX_HDR_MARK))
return false;
if (!matchSpace(results->rawbuf[offset++], COOLIX_HDR_SPACE))
return false;
if (!matchMark(results->rawbuf[offset], COOLIX_HDR_MARK)) return false;
// Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = calcTickTime(results->rawbuf[offset++],
COOLIX_HDR_MARK_TICKS);
if (!matchSpace(results->rawbuf[offset], COOLIX_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space.
uint32_t s_tick = calcTickTime(results->rawbuf[offset++],
COOLIX_HDR_SPACE_TICKS);

// Data
// Twice as many bits as there are normal plus inverted bits.
for (uint16_t i = 0; i < nbits * 2; i++, offset++) {
bool flip = (i / 8) % 2;
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK))
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
return false;
if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE)) { // 1
if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE_TICKS * s_tick)) {
if (flip)
inverted = (inverted << 1) | 1;
else
data = (data << 1) | 1;
} else if (matchSpace(results->rawbuf[offset], COOLIX_ZERO_SPACE)) { // 0
} else if (matchSpace(results->rawbuf[offset],
COOLIX_ZERO_SPACE_TICKS * s_tick)) {
if (flip)
inverted <<= 1;
else
Expand All @@ -129,10 +143,10 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
}

// Footer
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK))
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], COOLIX_MIN_GAP))
!matchAtLeast(results->rawbuf[offset], COOLIX_MIN_GAP_TICKS * s_tick))
return false;

// Compliance
Expand Down
62 changes: 37 additions & 25 deletions src/ir_Denon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtimer.h"
#include "IRutils.h"

// DDDD EEEEE N N OOO N N
// D D E NN N O O NN N
Expand All @@ -18,15 +19,24 @@
// Constants
// Ref:
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
#define DENON_HDR_MARK 263U // The length of the Header:Mark
#define DENON_HDR_SPACE 789U // The length of the Header:Space
#define DENON_BIT_MARK 263U // The length of a Bit:Mark
#define DENON_ONE_SPACE 1842U // The length of a Bit:Space for 1's
#define DENON_ZERO_SPACE 789U // The length of a Bit:Space for 0's
#define DENON_MIN_COMMAND_LENGTH 134052UL
#define DENON_MIN_GAP DENON_MIN_COMMAND_LENGTH - \
(DENON_HDR_MARK + DENON_HDR_SPACE + DENON_BITS * \
(DENON_BIT_MARK + DENON_ONE_SPACE) + DENON_BIT_MARK)
#define DENON_TICK 263U
#define DENON_HDR_MARK_TICKS 1U
#define DENON_HDR_MARK (DENON_HDR_MARK_TICKS * DENON_TICK)
#define DENON_HDR_SPACE_TICKS 3U
#define DENON_HDR_SPACE (DENON_HDR_SPACE_TICKS * DENON_TICK)
#define DENON_BIT_MARK_TICKS 1U
#define DENON_BIT_MARK (DENON_BIT_MARK_TICKS * DENON_TICK)
#define DENON_ONE_SPACE_TICKS 7U
#define DENON_ONE_SPACE (DENON_ONE_SPACE_TICKS * DENON_TICK)
#define DENON_ZERO_SPACE_TICKS 3U
#define DENON_ZERO_SPACE (DENON_ZERO_SPACE_TICKS * DENON_TICK)
#define DENON_MIN_COMMAND_LENGTH_TICKS 510U
#define DENON_MIN_COMMAND_LENGTH (DENON_MIN_COMMAND_LENGTH_TICKS * DENON_TICK)
#define DENON_MIN_GAP_TICKS (DENON_MIN_COMMAND_LENGTH_TICKS - \
(DENON_HDR_MARK_TICKS + DENON_HDR_SPACE_TICKS + \
DENON_BITS * (DENON_BIT_MARK_TICKS + DENON_ONE_SPACE_TICKS) + \
DENON_BIT_MARK_TICKS))
#define DENON_MIN_GAP (DENON_MIN_GAP_TICKS * DENON_TICK)
#define DENON_MANUFACTURER 0x2A4CULL

#if SEND_DENON
Expand Down Expand Up @@ -92,7 +102,7 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
if (!decodeSharp(results, nbits, true, false) &&
!decodePanasonic(results, nbits, true, DENON_MANUFACTURER)) {
// We couldn't decode it as expected, so try the old legacy method.
// NOTE: I don't this following protocol actually exists.
// NOTE: I don't think this following protocol actually exists.
// Looks like a partial version of the Sharp protocol.
// Check we have enough data
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
Expand All @@ -104,24 +114,26 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
uint16_t offset = OFFSET_START;

// Header
if (!matchMark(results->rawbuf[offset++], DENON_HDR_MARK))
return false;
if (!matchSpace(results->rawbuf[offset++], DENON_HDR_SPACE))
return false;
if (!matchMark(results->rawbuf[offset], DENON_HDR_MARK)) return false;
// Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = calcTickTime(results->rawbuf[offset++],
DENON_HDR_MARK_TICKS);
if (!matchSpace(results->rawbuf[offset], DENON_HDR_SPACE)) return false;
uint32_t s_tick = calcTickTime(results->rawbuf[offset++],
DENON_HDR_SPACE_TICKS);

// Data
for (uint16_t i = 0; i < nbits; i++, offset++) {
if (!matchMark(results->rawbuf[offset++], DENON_BIT_MARK))
return false;
if (matchSpace(results->rawbuf[offset], DENON_ONE_SPACE))
data = (data << 1) | 1; // 1
else if (matchSpace(results->rawbuf[offset], DENON_ZERO_SPACE))
data = (data << 1); // 0
else
return false;
}
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
DENON_BIT_MARK_TICKS * m_tick,
DENON_ONE_SPACE_TICKS * s_tick,
DENON_BIT_MARK_TICKS * m_tick,
DENON_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false;
data = data_result.data;
offset += data_result.used;

// Footer
if (!matchMark(results->rawbuf[offset++], DENON_BIT_MARK))
if (!matchMark(results->rawbuf[offset++], DENON_BIT_MARK_TICKS * m_tick))
return false;

// Success
Expand Down
59 changes: 33 additions & 26 deletions src/ir_Dish.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtimer.h"
#include "IRutils.h"

// DDDD IIIII SSSS H H
// D D I S H H
Expand All @@ -18,12 +19,19 @@
// Ref:
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
// http://www.hifi-remote.com/wiki/index.php?title=Dish
#define DISH_HDR_MARK 400U
#define DISH_HDR_SPACE 6100U
#define DISH_BIT_MARK 400U
#define DISH_ONE_SPACE 1700U
#define DISH_ZERO_SPACE 2800U
#define DISH_RPT_SPACE DISH_HDR_SPACE
#define DISH_TICK 100U
#define DISH_HDR_MARK_TICKS 4U
#define DISH_HDR_MARK (DISH_HDR_MARK_TICKS * DISH_TICK)
#define DISH_HDR_SPACE_TICKS 61U
#define DISH_HDR_SPACE (DISH_HDR_SPACE_TICKS * DISH_TICK)
#define DISH_BIT_MARK_TICKS 4U
#define DISH_BIT_MARK (DISH_BIT_MARK_TICKS * DISH_TICK)
#define DISH_ONE_SPACE_TICKS 17U
#define DISH_ONE_SPACE (DISH_ONE_SPACE_TICKS * DISH_TICK)
#define DISH_ZERO_SPACE_TICKS 28U
#define DISH_ZERO_SPACE (DISH_ZERO_SPACE_TICKS * DISH_TICK)
#define DISH_RPT_SPACE_TICKS DISH_HDR_SPACE_TICKS
#define DISH_RPT_SPACE (DISH_RPT_SPACE_TICKS * DISH_TICK)

#if SEND_DISH
// Send an IR command to a DISH NETWORK device.
Expand Down Expand Up @@ -95,42 +103,41 @@ bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) {
uint16_t offset = OFFSET_START;

// Header
if (!match(results->rawbuf[offset++], DISH_HDR_MARK))
return false;
if (!matchSpace(results->rawbuf[offset++], DISH_HDR_SPACE))
return false;
if (!match(results->rawbuf[offset], DISH_HDR_MARK)) return false;
// Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = calcTickTime(results->rawbuf[offset++],
DISH_HDR_MARK_TICKS);
if (!matchSpace(results->rawbuf[offset], DISH_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space.
uint32_t s_tick = calcTickTime(results->rawbuf[offset++],
DISH_HDR_SPACE_TICKS);

// Data
uint16_t actual_bits;
for (actual_bits = 0; offset < results->rawlen; actual_bits++, offset++) {
if (!match(results->rawbuf[offset++], DISH_BIT_MARK))
return false;
if (matchSpace(results->rawbuf[offset], DISH_ONE_SPACE))
data = (data << 1) | 1; // 1
else if (matchSpace(results->rawbuf[offset], DISH_ZERO_SPACE))
data = data << 1; // 0
else
break;
}
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits,
DISH_BIT_MARK_TICKS * m_tick,
DISH_ONE_SPACE_TICKS * s_tick,
DISH_BIT_MARK_TICKS * m_tick,
DISH_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false;
data = data_result.data;
offset += data_result.used;

// Footer
if (!match(results->rawbuf[offset - 1], DISH_BIT_MARK))
if (!matchMark(results->rawbuf[offset++], DISH_BIT_MARK_TICKS * m_tick))
return false;

// Compliance
if (strict) {
// The DISH protocol calls for a repeated message, so strictly speaking
// there should be a code following this. Only require it if we are set to
// strict matching.
if (!matchSpace(results->rawbuf[offset], DISH_RPT_SPACE))
if (!matchSpace(results->rawbuf[offset], DISH_RPT_SPACE_TICKS * s_tick))
return false;
if (actual_bits != nbits)
return false; // We didn't get the same number of bits we asked for.
}

// Success
results->decode_type = DISH;
results->bits = actual_bits;
results->bits = nbits;
results->value = data;
results->address = 0;
results->command = 0;
Expand Down
Loading