From a0bd0b742bda5c60e56027b61e7c6e6b871ffb92 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 29 Oct 2020 17:33:58 +1000 Subject: [PATCH] Add basic support for Panasonic A/C 64 bits. * `sendPanasonicAC64()` & `decodePanasonicAC64()` added * Unit tests for the above. * LSBF order determined by Temperature ranges. For #1307 --- src/IRrecv.cpp | 4 + src/IRrecv.h | 8 +- src/IRremoteESP8266.h | 11 ++- src/IRsend.cpp | 8 +- src/IRsend.h | 7 +- src/IRtext.cpp | 1 + src/ir_Panasonic.cpp | 151 +++++++++++++++++++++++++++++++------ src/ir_Panasonic.h | 2 + src/locale/defaults.h | 3 + test/ir_Panasonic_test.cpp | 148 ++++++++++++++++++++++++++++++++---- 10 files changed, 303 insertions(+), 40 deletions(-) diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index def5359b0..c52416012 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -884,6 +884,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting EliteScreens decode"); if (decodeElitescreens(results, offset)) return true; #endif // DECODE_ELITESCREENS +#if DECODE_PANASONIC_AC64 + DPRINTLN("Attempting Panasonic AC (64bit) decode"); + if (decodePanasonicAC64(results, offset)) return true; +#endif // DECODE_PANASONIC_AC64 // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index 928b45ce7..3d970fee1 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -596,7 +596,13 @@ class IRrecv { uint16_t offset = kStartOffset, const uint16_t nbits = kPanasonicAcBits, const bool strict = true); -#endif +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC64 + bool decodePanasonicAC64(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicAc64Bits, + const bool strict = true); +#endif // DECODE_PANASONIC_AC64 #if DECODE_PIONEER bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kPioneerBits, diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 614197ef7..1bf998eda 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -502,6 +502,13 @@ #define SEND_PANASONIC_AC _IR_ENABLE_DEFAULT_ #endif // SEND_PANASONIC_AC +#ifndef DECODE_PANASONIC_AC64 +#define DECODE_PANASONIC_AC64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC_AC64 +#ifndef SEND_PANASONIC_AC64 +#define SEND_PANASONIC_AC64 _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC_AC64 + #ifndef DECODE_MWM #define DECODE_MWM _IR_ENABLE_DEFAULT_ #endif // DECODE_MWM @@ -859,8 +866,9 @@ enum decode_type_t { TECHNIBEL_AC, MIRAGE, ELITESCREENS, // 95 + PANASONIC_AC64, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = ELITESCREENS, + kLastDecodeType = PANASONIC_AC64, }; // Message lengths & required repeat values @@ -1022,6 +1030,7 @@ const uint16_t kPanasonicAcStateShortLength = 16; const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; +const uint16_t kPanasonicAc64Bits = 64; const uint16_t kPioneerBits = 64; const uint16_t kProntoMinLength = 6; const uint16_t kRC5RawBits = 14; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 4ab30839a..b34bdd307 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -667,6 +667,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case AMCOR: case CARRIER_AC64: case DELONGHI_AC: + case PANASONIC_AC64: case PIONEER: return 64; case ARGO: @@ -926,7 +927,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, case PANASONIC: sendPanasonic64(data, nbits, min_repeat); break; -#endif +#endif // SEND_PANASONIC +#if SEND_PANASONIC_AC64 + case PANASONIC_AC64: + sendPanasonicAC64(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC_AC64 #if SEND_PIONEER case PIONEER: sendPioneer(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index a7e34557b..e531e9bfc 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -573,7 +573,12 @@ class IRsend { void sendPanasonicAC(const unsigned char data[], const uint16_t nbytes = kPanasonicAcStateLength, const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC64 + void sendPanasonicAC64(const uint64_t data, + const uint16_t nbits = kPanasonicAc64Bits, + const uint16_t repeat = kPanasonicAcDefaultRepeat); +#endif // SEND_PANASONIC_AC64 #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, const uint16_t repeat = kNoRepeat); diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 539e257b4..47891aea3 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -274,5 +274,6 @@ const PROGMEM char *kAllProtocolNamesStr = D_STR_TECHNIBEL_AC "\x0" D_STR_MIRAGE "\x0" D_STR_ELITESCREENS "\x0" + D_STR_PANASONIC_AC64 "\x0" ///< New protocol strings should be added just above this line. "\x0"; ///< This string requires double null termination. diff --git a/src/ir_Panasonic.cpp b/src/ir_Panasonic.cpp index 6c38090c5..c09a2b59c 100644 --- a/src/ir_Panasonic.cpp +++ b/src/ir_Panasonic.cpp @@ -25,32 +25,28 @@ // Constants /// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 -const uint16_t kPanasonicTick = 432; -const uint16_t kPanasonicHdrMarkTicks = 8; -const uint16_t kPanasonicHdrMark = kPanasonicHdrMarkTicks * kPanasonicTick; -const uint16_t kPanasonicHdrSpaceTicks = 4; -const uint16_t kPanasonicHdrSpace = kPanasonicHdrSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicBitMarkTicks = 1; -const uint16_t kPanasonicBitMark = kPanasonicBitMarkTicks * kPanasonicTick; -const uint16_t kPanasonicOneSpaceTicks = 3; -const uint16_t kPanasonicOneSpace = kPanasonicOneSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicZeroSpaceTicks = 1; -const uint16_t kPanasonicZeroSpace = kPanasonicZeroSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicMinCommandLengthTicks = 378; -const uint32_t kPanasonicMinCommandLength = - kPanasonicMinCommandLengthTicks * kPanasonicTick; -const uint16_t kPanasonicEndGap = 5000; // See issue #245 -const uint16_t kPanasonicMinGapTicks = - kPanasonicMinCommandLengthTicks - - (kPanasonicHdrMarkTicks + kPanasonicHdrSpaceTicks + - kPanasonicBits * (kPanasonicBitMarkTicks + kPanasonicOneSpaceTicks) + - kPanasonicBitMarkTicks); -const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick; - -const uint16_t kPanasonicAcSectionGap = 10000; +const uint16_t kPanasonicHdrMark = 3456; ///< uSeconds. +const uint16_t kPanasonicHdrSpace = 1728; ///< uSeconds. +const uint16_t kPanasonicBitMark = 432; ///< uSeconds. +const uint16_t kPanasonicOneSpace = 1296; ///< uSeconds. +const uint16_t kPanasonicZeroSpace = 432; ///< uSeconds. +const uint32_t kPanasonicMinCommandLength = 163296; ///< uSeconds. +const uint16_t kPanasonicEndGap = 5000; ///< uSeconds. See issue #245 +const uint32_t kPanasonicMinGap = 74736; ///< uSeconds. + +const uint16_t kPanasonicAcSectionGap = 10000; ///< uSeconds. const uint16_t kPanasonicAcSection1Length = 8; const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. +const uint16_t kPanasonicAc64HdrMark = 3543; ///< uSeconds. +const uint16_t kPanasonicAc64BitMark = 920; ///< uSeconds. +const uint16_t kPanasonicAc64HdrSpace = 3450; ///< uSeconds. +const uint16_t kPanasonicAc64OneSpace = 2575; ///< uSeconds. +const uint16_t kPanasonicAc64ZeroSpace = 828; ///< uSeconds. +const uint16_t kPanasonicAc64SectionGap = 13946; ///< uSeconds. +const uint8_t kPanasonicAc64Sections = 2; +const uint8_t kPanasonicAc64BlocksPerSection = 2; + using irutils::addBoolToString; using irutils::addFanToString; using irutils::addIntToString; @@ -926,3 +922,112 @@ bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t offset, return true; } #endif // DECODE_PANASONIC_AC + +#if SEND_PANASONIC_AC64 +/// Send a Panasonic AC 64bit formatted message. +/// Status: BETA / Probably works. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kPanasonicAc64Bits +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 +void IRsend::sendPanasonicAC64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + const uint16_t kSectionBits = nbits / kPanasonicAc64Sections; + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t section = 0; section < kPanasonicAc64Sections; section++) { + // Two data blocks per section (i.e. 1 + a repeat) + sendGeneric(kPanasonicAc64HdrMark, kPanasonicAc64HdrSpace, // Header + kPanasonicAc64BitMark, kPanasonicAc64OneSpace, // Data + kPanasonicAc64BitMark, kPanasonicAc64ZeroSpace, + 0, 0, // No Footer + GETBITS64( + data, + kSectionBits * (kPanasonicAc64Sections - section - 1), + kSectionBits), + kSectionBits, kPanasonicFreq, false, + kPanasonicAc64BlocksPerSection - 1, // Repeat + 50); + // Section Footer + sendGeneric(kPanasonicAc64HdrMark, kPanasonicAc64HdrSpace, // Header + 0, 0, 0, 0, // No Data + kPanasonicAc64BitMark, kPanasonicAc64SectionGap, // Footer + data, 0, // No data (bits) + kPanasonicFreq, true, 0, 50); + } + } +} +#endif // SEND_PANASONIC_AC64 + +#if DECODE_PANASONIC_AC64 +/// Decode the supplied Panasonic AC 64bit message. +/// Status: BETA / Probably works. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 +bool IRrecv::decodePanasonicAC64(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (2 * (nbits + kHeader) + kFooter) - 1 + offset) + return false; // Can't possibly be a valid message. + if (strict && nbits != kPanasonicAc64Bits) + return false; // Not strictly a message. + + const uint16_t kSectionBits = nbits / kPanasonicAc64Sections; + uint64_t data = 0; + uint64_t section_data = 0; + uint32_t prev_section_data; + + for (uint16_t block = 0; + block < kPanasonicAc64Sections * kPanasonicAc64BlocksPerSection; + block++) { + uint16_t used = matchGeneric(results->rawbuf + offset, §ion_data, + results->rawlen - offset, kSectionBits, + kPanasonicAc64HdrMark, kPanasonicAc64HdrSpace, + kPanasonicAc64BitMark, kPanasonicAc64OneSpace, + kPanasonicAc64BitMark, kPanasonicAc64ZeroSpace, + 0, 0, // No Footer + false, kUseDefTol, kMarkExcess, false); + if (!used) return false; + offset += used; + // Is it the first block of the section? + if (block % kPanasonicAc64BlocksPerSection == 0) { + prev_section_data = section_data; + // Keep the data from the first of the block pairs. + data = (data << kSectionBits) | section_data; + } else { + // Compliance + if (strict) + // Compare the data from the blocks in pairs. + if (section_data != prev_section_data) return false; + // Look for the section footer at the end of the blocks. + if ((block + 1) % kPanasonicAc64BlocksPerSection == 0) { + uint64_t junk; + used = matchGeneric(results->rawbuf + offset, &junk, + results->rawlen - offset, 0, + // Header + kPanasonicAc64HdrMark, kPanasonicAc64HdrSpace, + // No Data + 0, 0, + 0, 0, + // Footer + kPanasonicAc64BitMark, kPanasonicAc64SectionGap, + true); + if (!used) return false; + offset += used; + } + } + } + + // Success + results->value = data; + results->decode_type = decode_type_t::PANASONIC_AC64; + results->bits = nbits; + results->address = GETBITS64(data, kSectionBits, kSectionBits); + results->command = GETBITS64(data, 0, kSectionBits);; + return true; +} +#endif // DECODE_PANASONIC_AC64 diff --git a/src/ir_Panasonic.h b/src/ir_Panasonic.h index 4098993f9..a189a2ed2 100644 --- a/src/ir_Panasonic.h +++ b/src/ir_Panasonic.h @@ -23,6 +23,8 @@ // Brand: Panasonic, Model: A75C2616-1 remote (PANASONIC_AC DKE/3) // Brand: Panasonic, Model: A75C3704 remote (PANASONIC_AC DKE/3) // Brand: Panasonic, Model: A75C3747 remote (PANASONIC_AC JKE/4) +// Brand: Panasonic, Model: CS-E9CKP series A/C (PANASONIC_AC64) +// Brand: Panasonic, Model: A75C2295 remote (PANASONIC_AC64) #ifndef IR_PANASONIC_H_ #define IR_PANASONIC_H_ diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 9c44eed58..e1b9d4b67 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -670,6 +670,9 @@ #ifndef D_STR_PANASONIC_AC #define D_STR_PANASONIC_AC "PANASONIC_AC" #endif // D_STR_PANASONIC_AC +#ifndef D_STR_PANASONIC_AC64 +#define D_STR_PANASONIC_AC64 D_STR_PANASONIC_AC"64" +#endif // D_STR_PANASONIC_AC64 #ifndef D_STR_PIONEER #define D_STR_PIONEER "PIONEER" #endif // D_STR_PIONEER diff --git a/test/ir_Panasonic_test.cpp b/test/ir_Panasonic_test.cpp index 8008fbc14..9e46026e2 100644 --- a/test/ir_Panasonic_test.cpp +++ b/test/ir_Panasonic_test.cpp @@ -497,7 +497,7 @@ TEST(TestDecodePanasonic, DecodeIssue245) { // Test sending typical data only. TEST(TestSendPanasonicAC, SendDataOnly) { - IRsendTest irsend(0); + IRsendTest irsend(kGpioUnused); irsend.begin(); irsend.reset(); @@ -873,8 +873,8 @@ TEST(TestDecodePanasonicAC, RealExample) { // Decode synthetic Panasonic AC message. TEST(TestDecodePanasonicAC, SyntheticExample) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); // Data from Issue #525 @@ -913,8 +913,8 @@ TEST(TestGeneralPanasonic, typeToString) { // Decode a problematic Panasonic AC message TEST(TestDecodePanasonicAC, Issue540) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); // Data from Issue #540 @@ -1063,8 +1063,8 @@ TEST(TestIRPanasonicAcClass, TimersAndClock) { // Decode a real short Panasonic AC message TEST(TestDecodePanasonicAC, RealExampleOfShortMessage) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); // Data from Issue #544 (Odour Wash) @@ -1108,8 +1108,8 @@ TEST(TestDecodePanasonicAC, RealExampleOfShortMessage) { // Create and decode a short Panasonic AC message TEST(TestDecodePanasonicAC, SyntheticShortMessage) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); uint8_t odourWash[kPanasonicAcStateShortLength] = { @@ -1127,8 +1127,8 @@ TEST(TestDecodePanasonicAC, SyntheticShortMessage) { // // Test for CKP model / see issue #544 TEST(TestDecodePanasonicAC, CkpModelSpecifics) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); // Data from Issue #544 @@ -1220,8 +1220,8 @@ TEST(TestIRPanasonicAcClass, toCommon) { // // Test for DKE/DKW model / see issue #1024 TEST(TestDecodePanasonicAC, DkeIonRealMessages) { - IRsendTest irsend(0); - IRrecv irrecv(0); + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); irsend.begin(); // Data from Issue #1024 @@ -1268,4 +1268,126 @@ TEST(TestPanasonic, Housekeeping) { ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::PANASONIC_AC)); ASSERT_EQ(kPanasonicAcBits, IRsend::defaultBits(decode_type_t::PANASONIC_AC)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::PANASONIC_AC)); + // Panasonic A/Cs (64bit) + ASSERT_EQ(D_STR_PANASONIC_AC64, typeToString(decode_type_t::PANASONIC_AC64)); + ASSERT_EQ(decode_type_t::PANASONIC_AC64, + strToDecodeType(D_STR_PANASONIC_AC64)); + ASSERT_FALSE(hasACState(decode_type_t::PANASONIC_AC64)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::PANASONIC_AC64)); + ASSERT_EQ(kPanasonicAc64Bits, + IRsend::defaultBits(decode_type_t::PANASONIC_AC64)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::PANASONIC_AC64)); +} + +// Decode a real Panasonic AC 64 bit message +TEST(TestDecodePanasonicAC64, RealMessage) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1307#issuecomment-717436003 + // https://github.com/crankyoldgit/IRremoteESP8266/files/5446781/temp-take3_36khz.txt + // # Remote: 17 C, auto mode, auto top swing, auto left/right swing, auto fan + const uint16_t rawData[271] = { + 3544, 3448, + 922, 830, 918, 2576, 922, 830, 918, 830, + 918, 2576, 922, 2574, 922, 2576, 922, 2576, + 922, 826, 922, 2574, 922, 828, 920, 826, + 922, 2574, 922, 2574, 922, 2576, 920, 2576, + 918, 2578, 918, 830, 918, 2574, 922, 2574, + 922, 830, 918, 830, 918, 830, 918, 826, + 922, 2574, 922, 826, 922, 2576, 922, 2574, + 922, 826, 922, 826, 922, 826, 922, 826, + 3544, 3452, + 918, 830, 918, 2574, 922, 830, 918, 830, + 918, 2576, 922, 2576, 922, 2576, 918, 2580, + 918, 830, 918, 2580, 918, 830, 918, 832, + 916, 2576, 922, 2576, 922, 2576, 918, 2580, + 918, 2580, 916, 832, 918, 2576, 922, 2574, + 922, 826, 922, 826, 922, 826, 922, 826, + 922, 2576, 916, 830, 918, 2580, 918, 2578, + 918, 832, 920, 826, 922, 826, 922, 826, + 3544, 3450, + 922, 13946, + 3542, 3450, + 922, 826, 922, 826, 922, 2574, 922, 2574, + 922, 2574, 922, 2574, 922, 2574, 922, 2574, + 920, 830, 918, 830, 916, 2578, 918, 2580, + 916, 2580, 916, 2580, 918, 2578, 922, 2574, + 922, 826, 922, 2576, 922, 2574, 922, 826, + 922, 2574, 922, 2574, 922, 826, 922, 826, + 922, 830, 918, 2574, 922, 2574, 922, 830, + 916, 2574, 922, 2574, 922, 830, 918, 830, + 3542, 3450, + 918, 830, 916, 830, 918, 2578, 922, 2574, + 922, 2574, 922, 2574, 922, 2574, 922, 2574, + 922, 830, 918, 830, 916, 2574, 922, 2574, + 922, 2576, 920, 2574, 922, 2574, 922, 2576, + 922, 830, 916, 2580, 916, 2580, 916, 830, + 918, 2578, 916, 2580, 918, 830, 922, 826, + 922, 826, 922, 2574, 922, 2574, 924, 824, + 922, 2574, 922, 2574, 922, 826, 922, 826, + 3542, 3452, + 922}; // UNKNOWN AA94330B + + irsend.sendRaw(rawData, 271, kPanasonicFreq); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(PANASONIC_AC64, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicAc64Bits, irsend.capture.bits); + EXPECT_EQ(0x0D0DF2F23636FCFC, irsend.capture.value); + EXPECT_EQ(0x0D0DF2F2, irsend.capture.address); + EXPECT_EQ(0x3636FCFC, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + EXPECT_EQ( + "", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +// Decode a synthetic Panasonic AC 64 bit message +TEST(TestDecodePanasonicAC64, SyntheticMessage) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.sendPanasonicAC64(0x0D0DF2F23636FCFC); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(PANASONIC_AC64, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicAc64Bits, irsend.capture.bits); + EXPECT_EQ(0x0D0DF2F23636FCFC, irsend.capture.value); + EXPECT_EQ(0x0D0DF2F2, irsend.capture.address); + EXPECT_EQ(0x3636FCFC, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + EXPECT_EQ( + "f36700d50" + "m3543s3450" + "m920s828m920s2575m920s828m920s828m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s2575m920s828m920s828m920s2575m920s2575m920s2575m920s2575" + "m920s2575m920s828m920s2575m920s2575m920s828m920s828m920s828m920s828" + "m920s2575m920s828m920s2575m920s2575m920s828m920s828m920s828m920s828" + "m3543s3450" + "m920s828m920s2575m920s828m920s828m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s2575m920s828m920s828m920s2575m920s2575m920s2575m920s2575" + "m920s2575m920s828m920s2575m920s2575m920s828m920s828m920s828m920s828" + "m920s2575m920s828m920s2575m920s2575m920s828m920s828m920s828m920s828" + "m3543s3450" + "m920s13946" + "m3543s3450" + "m920s828m920s828m920s2575m920s2575m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s828m920s2575m920s2575m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s2575m920s2575m920s828m920s2575m920s2575m920s828m920s828" + "m920s828m920s2575m920s2575m920s828m920s2575m920s2575m920s828m920s828" + "m3543s3450" + "m920s828m920s828m920s2575m920s2575m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s828m920s2575m920s2575m920s2575m920s2575m920s2575m920s2575" + "m920s828m920s2575m920s2575m920s828m920s2575m920s2575m920s828m920s828" + "m920s828m920s2575m920s2575m920s828m920s2575m920s2575m920s828m920s828" + "m3543s3450" + "m920s13946", + irsend.outputStr()); }