From 57e2ef233f17bdb5a24e5dcd343a2e95fa7ff82f Mon Sep 17 00:00:00 2001 From: David Conran Date: Sun, 28 May 2017 17:38:09 +1000 Subject: [PATCH] Unit tests for Coolix protocol. (#224) - Unit tests - Change the way decoding is done so that it supports 64 bit sizes. - Update status of the decoder from ALPHA to BETA. --- .travis.yml | 1 + src/ir_Coolix.cpp | 42 +++--- test/Makefile | 11 +- test/ir_Coolix_test.cpp | 290 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+), 20 deletions(-) create mode 100644 test/ir_Coolix_test.cpp diff --git a/.travis.yml b/.travis.yml index 4721f6bc1..7f2841fbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,6 +52,7 @@ script: - test/ir_Panasonic_test - test/ir_Dish_test - test/ir_Whynter_test + - test/ir_Coolix_test - test/ir_Aiwa_test - test/ir_Denon_test - test/ir_Sanyo_test diff --git a/src/ir_Coolix.cpp b/src/ir_Coolix.cpp index 9d8127900..f687b3c11 100644 --- a/src/ir_Coolix.cpp +++ b/src/ir_Coolix.cpp @@ -82,7 +82,7 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / untested. +// Status: BETA / Probably working. bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits, bool strict) { // The protocol sends the data normal + inverted, alternating on @@ -95,9 +95,10 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits, return false; uint64_t data = 0; + uint64_t inverted = 0; uint16_t offset = OFFSET_START; - if (nbits * 2 > sizeof(data) * 8) + if (nbits > sizeof(data) * 8) return false; // We can't possibly capture a Coolix packet that big. // Header @@ -109,37 +110,40 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits, // 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)) return false; - if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], COOLIX_ZERO_SPACE)) - data <<= 1; // 0 - else + if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE)) { // 1 + if (flip) + inverted = (inverted << 1) | 1; + else + data = (data << 1) | 1; + } else if (matchSpace(results->rawbuf[offset], COOLIX_ZERO_SPACE)) { // 0 + if (flip) + inverted <<= 1; + else + data <<= 1; + } else { return false; + } } // Footer if (!matchMark(results->rawbuf[offset], COOLIX_BIT_MARK)) return false; - // Data should now be (MSB to LSB) byte1,!byte1,byte2,!byte2,byte3,!byte3 - // Decode, and verify if needed. - uint64_t result = 0; // Build a new result for we destroy the existing data. - for (uint16_t i = 0; i < nbits; i += 8) { - uint8_t inverted = (data & 0xFF) ^ 0xFF; // Un-invert the byte. - data >>= 8; - uint8_t normal = data & 0xFF; - data >>= 8; - if (strict && data != inverted) // Compliance - return false; // The message doesn't verify. - result |= (normal << i); // Add the byte in front of the previous result. + // Compliance + uint64_t orig = data; // Save a copy of the data. + if (strict) { + for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8) + if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF)) + return false; } // Success results->decode_type = COOLIX; results->bits = nbits; - results->value = result; + results->value = orig; results->address = 0; results->command = 0; return true; diff --git a/test/Makefile b/test/Makefile index aabb49d77..1ea18c1f7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -29,7 +29,7 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Sherwood_test ir_Sony_test ir_Samsung_test ir_Kelvinator_test \ ir_JVC_test ir_RCMM_test ir_LG_test ir_Mitsubishi_test ir_Sharp_test \ ir_RC5_RC6_test ir_Panasonic_test ir_Dish_test ir_Whynter_test \ - ir_Aiwa_test ir_Denon_test ir_Sanyo_test ir_Daikin_test + ir_Aiwa_test ir_Denon_test ir_Sanyo_test ir_Daikin_test ir_Coolix_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -230,6 +230,15 @@ ir_Whynter_test.o : ir_Whynter_test.cpp $(USER_DIR)/IRremoteESP8266.h $(USER_DIR ir_Whynter_test : IRrecv.o IRsend.o IRtimer.o IRutils.o ir_GlobalCache.o ir_Whynter_test.o ir_Whynter.o gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +ir_Coolix.o : $(USER_DIR)/IRremoteESP8266.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h $(USER_DIR)/ir_Coolix.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + +ir_Coolix_test.o : ir_Coolix_test.cpp $(USER_DIR)/IRremoteESP8266.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Coolix_test.cpp + +ir_Coolix_test : IRrecv.o IRsend.o IRtimer.o IRutils.o ir_GlobalCache.o ir_Coolix_test.o ir_Coolix.o gtest_main.a + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_Aiwa.o : $(USER_DIR)/IRremoteESP8266.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h $(USER_DIR)/ir_Aiwa.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp diff --git a/test/ir_Coolix_test.cpp b/test/ir_Coolix_test.cpp new file mode 100644 index 000000000..3c44cffdd --- /dev/null +++ b/test/ir_Coolix_test.cpp @@ -0,0 +1,290 @@ +// Copyright 2017 David Conran + +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendCOOLIX(). + +// Test sending typical data only. +TEST(TestSendCoolix, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendCOOLIX(0x0); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040", irsend.outputStr()); + + irsend.reset(); + irsend.sendCOOLIX(0xAA55AA); + EXPECT_EQ( + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040", irsend.outputStr()); + + irsend.reset(); + irsend.sendCOOLIX(0xFFFFFF); + EXPECT_EQ( + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5040", irsend.outputStr()); +} + +// Test sending with different repeats. +TEST(TestSendCoolix, SendWithRepeats) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendCOOLIX(0xAA55AA, COOLIX_BITS, 1); // 1 repeat. + EXPECT_EQ( + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040", irsend.outputStr()); + irsend.sendCOOLIX(0xAA55AA, COOLIX_BITS, 2); // 2 repeats. + EXPECT_EQ( + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040", irsend.outputStr()); +} + +// Test sending an atypical data size. +TEST(TestSendCoolix, SendUsualSize) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendCOOLIX(0x0, 8); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040", irsend.outputStr()); + + irsend.reset(); + irsend.sendCOOLIX(0x1234567890ABCDEF, 64); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560m560s1680" + "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s5040", irsend.outputStr()); + + // Bit sizes must be a multiple of 8. + irsend.reset(); + irsend.sendCOOLIX(0x0, 17); + EXPECT_EQ("" , irsend.outputStr()); +} + +// Tests for decodeCOOLIX(). + +// Decode normal Coolix messages. +TEST(TestDecodeCoolix, NormalDecodeWithStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Coolix 24-bit message. + irsend.reset(); + irsend.sendCOOLIX(0x123456); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Normal Coolix 24-bit message. + irsend.reset(); + irsend.sendCOOLIX(0x0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Normal Coolix 24-bit message. + irsend.reset(); + irsend.sendCOOLIX(0xFFFFFF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0xFFFFFF, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Decode normal repeated Coolix messages. +TEST(TestDecodeCoolix, NormalDecodeWithRepeatAndStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Coolix 16-bit message with 2 repeats. + irsend.reset(); + irsend.sendCOOLIX(0x123456, COOLIX_BITS, 2); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.makeDecodeResult(4 * COOLIX_BITS + 4); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); + + irsend.makeDecodeResult(2 * (4 * COOLIX_BITS + 4)); + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); +} + +// Decode unsupported Coolix messages. +TEST(TestDecodeCoolix, DecodeWithNonStrictSizes) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + irsend.sendCOOLIX(0x12, 8); // Illegal value Coolix 8-bit message. + irsend.makeDecodeResult(); + // Should fail with strict on. + ASSERT_FALSE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + // Should pass if strict off. + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, 8, false)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(8, irsend.capture.bits); + EXPECT_EQ(0x12, irsend.capture.value); + + irsend.reset(); + irsend.sendCOOLIX(0x12345678, 32); // Illegal value Coolix 32-bit message. + irsend.makeDecodeResult(); + // Should pass with strict when we ask for less bits than we got. + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, true)); + + irsend.makeDecodeResult(); + // Should fail with strict when we ask for the wrong bit size. + ASSERT_FALSE(irrecv.decodeCOOLIX(&irsend.capture, 32, true)); + // Should pass if strict off. + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, 32, false)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0x12345678, irsend.capture.value); + + // Decode should fail if asked to decode non-multiples of 8 bits. + irsend.reset(); + irsend.sendCOOLIX(0x123456, COOLIX_BITS, 2); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decodeCOOLIX(&irsend.capture, 9, false)); +} + +// Decode (non-standard) 64-bit messages. +TEST(TestDecodeCoolix, Decode64BitMessages) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Illegal size Coolix 64-bit message. + irsend.sendCOOLIX(0xFFFFFFFFFFFFFFFF, 64); + irsend.makeDecodeResult(); + // Should work with a 'normal' match (not strict) + ASSERT_TRUE(irrecv.decodeCOOLIX(&irsend.capture, 64, false)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(64, irsend.capture.bits); + EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value); +} + +// Fail to decode a non-Coolix example via GlobalCache +TEST(TestDecodeCoolix, FailToDecodeNonCoolixExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Modified a few entries to unexpected values, based on previous test case. + uint16_t gc_test[39] = {38000, 1, 1, 322, 162, 20, 61, 20, 61, 20, 20, 20, 20, + 20, 20, 20, 127, 20, 61, 9, 20, 20, 61, 20, 20, 20, + 61, 20, 61, 20, 61, 20, 20, 20, 20, 20, 20, 20, 884}; + irsend.sendGC(gc_test, 39); + irsend.makeDecodeResult(); + + ASSERT_FALSE(irrecv.decodeCOOLIX(&irsend.capture)); + ASSERT_FALSE(irrecv.decodeCOOLIX(&irsend.capture, COOLIX_BITS, false)); +}