mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Alpha support for Milestag2 (#1380)
I've implemented MilesTag2 Lasertag protocol described here: http://hosting.cmalton.me.uk/chrism/lasertag/MT2Proto.pdf decoding actually meaning of bit's in protocol is on users side, they could just macro it for example: ```cpp #define MT_SHOT(player, team, damage) (unsigned long)(((player & 127) << 6) | ((team & 3 ) << 4) | (damage & 15 )) #define MT_PLAYER_VALUE(shot_data) (unsigned int)((shot_data >> 6) & 127) #define MT_TEAM_VALUE(shot_data) (unsigned short)((shot_data >> 4) & 3) #define MT_DAMAGE_VALUE(shot_data) (unsigned short)(shot_data & 15) ``` Tested it on ESP8266 and ESP32 and it works fine for me. But I don't have actual commercial Lasertag taggers and receivers with MilesTag2, to test against real world conditions. But I think this should be a good start. * Extend `matchData()`, `matchBytes()`, & `matchGeneric()` to handle no trailing space. * Add basic unit tests * Unit tests - Housekeeping - Sending Shot & Msg sizes. - Self-decoding - Failure when packets are not formatted correctly. Note: No real-world test data as yet. Co-authored-by: crankyoldgit <david@xyzzy.com.au> X-Ref #1360
This commit is contained in:
@@ -596,6 +596,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
|
||||
DPRINTLN("Attempting NEC decode");
|
||||
if (decodeNEC(results, offset)) return true;
|
||||
#endif
|
||||
#if DECODE_MILESTAG2
|
||||
DPRINTLN("Attempting MilesTag2 decode");
|
||||
// Try decodeMilestag2() before decodeSony() because the protocols are
|
||||
// similar in timings & structure, but the Miles one differs in nbits
|
||||
// so this one should be tried first to try to reduce false detection
|
||||
if (decodeMilestag2(results, offset, kMilesTag2MsgBits) ||
|
||||
decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true;
|
||||
#endif
|
||||
#if DECODE_SONY
|
||||
DPRINTLN("Attempting Sony decode");
|
||||
if (decodeSony(results, offset)) return true;
|
||||
@@ -1202,30 +1210,49 @@ bool IRrecv::decodeHash(decode_results *results) {
|
||||
/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess)
|
||||
/// @param[in] MSBfirst Bit order to save the data in. (Def: true)
|
||||
/// true is Most Significant Bit First Order, false is Least Significant First
|
||||
/// @param[in] expectlastspace Do we expect a space at the end of the message?
|
||||
/// @return 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, const uint16_t nbits, const uint16_t onemark,
|
||||
const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance, const int16_t excess, const bool MSBfirst) {
|
||||
const uint8_t tolerance, const int16_t excess, const bool MSBfirst,
|
||||
const bool expectlastspace) {
|
||||
match_result_t result;
|
||||
result.success = false; // Fail by default.
|
||||
result.data = 0;
|
||||
for (result.used = 0; result.used < nbits * 2;
|
||||
result.used += 2, data_ptr += 2) {
|
||||
// Is the bit a '1'?
|
||||
if (matchMark(*data_ptr, onemark, tolerance, excess) &&
|
||||
matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) {
|
||||
result.data = (result.data << 1) | 1;
|
||||
} else if (matchMark(*data_ptr, zeromark, tolerance, excess) &&
|
||||
matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) {
|
||||
result.data <<= 1; // The bit is a '0'.
|
||||
} else {
|
||||
if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2);
|
||||
return result; // It's neither, so fail.
|
||||
if (expectlastspace) { // We are expecting data with a final space.
|
||||
for (result.used = 0; result.used < nbits * 2;
|
||||
result.used += 2, data_ptr += 2) {
|
||||
// Is the bit a '1'?
|
||||
if (matchMark(*data_ptr, onemark, tolerance, excess) &&
|
||||
matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) {
|
||||
result.data = (result.data << 1) | 1;
|
||||
} else if (matchMark(*data_ptr, zeromark, tolerance, excess) &&
|
||||
matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) {
|
||||
result.data <<= 1; // The bit is a '0'.
|
||||
} else {
|
||||
if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2);
|
||||
return result; // It's neither, so fail.
|
||||
}
|
||||
}
|
||||
result.success = true;
|
||||
} else { // We are expecting data without a final space.
|
||||
// Match all but the last bit, as it may not match easily.
|
||||
result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace,
|
||||
zeromark, zerospace, tolerance, excess, true, true);
|
||||
if (result.success) {
|
||||
// Is the bit a '1'?
|
||||
if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess))
|
||||
result.data = (result.data << 1) | 1;
|
||||
else if (matchMark(*(data_ptr + result.used), zeromark, tolerance,
|
||||
excess))
|
||||
result.data <<= 1; // The bit is a '0'.
|
||||
else
|
||||
result.success = false;
|
||||
if (result.success) result.used++;
|
||||
}
|
||||
}
|
||||
result.success = true;
|
||||
if (!MSBfirst) result.data = reverseBits(result.data, nbits);
|
||||
return result;
|
||||
}
|
||||
@@ -1245,20 +1272,23 @@ match_result_t IRrecv::matchData(
|
||||
/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess)
|
||||
/// @param[in] MSBfirst Bit order to save the data in. (Def: true)
|
||||
/// true is Most Significant Bit First Order, false is Least Significant First
|
||||
/// @param[in] expectlastspace Do we expect a space at the end of the message?
|
||||
/// @return If successful, how many buffer entries were used. Otherwise 0.
|
||||
uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbytes,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance, const int16_t excess,
|
||||
const bool MSBfirst) {
|
||||
const bool MSBfirst, const bool expectlastspace) {
|
||||
// Check if there is enough capture buffer to possibly have the desired bytes.
|
||||
if (remaining < nbytes * 8 * 2) return 0; // Nope, so abort.
|
||||
if (remaining + expectlastspace < (nbytes * 8 * 2) + 1)
|
||||
return 0; // Nope, so abort.
|
||||
uint16_t offset = 0;
|
||||
for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) {
|
||||
bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true;
|
||||
match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace,
|
||||
zeromark, zerospace, tolerance, excess,
|
||||
MSBfirst);
|
||||
MSBfirst, lastspace);
|
||||
if (result.success == false) return 0; // Fail
|
||||
result_ptr[byte_pos] = (uint8_t)result.data;
|
||||
offset += result.used;
|
||||
@@ -1316,8 +1346,10 @@ uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
|
||||
const bool MSBfirst) {
|
||||
// If we are expecting byte sizes, check it's a factor of 8 or fail.
|
||||
if (!use_bits && nbits % 8 != 0) return 0;
|
||||
// Calculate if we expect a trailing space in the data section.
|
||||
const bool kexpectspace = footermark || (onespace != zerospace);
|
||||
// Calculate how much remaining buffer is required.
|
||||
uint16_t min_remaining = nbits * 2;
|
||||
uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1);
|
||||
|
||||
if (hdrmark) min_remaining++;
|
||||
if (hdrspace) min_remaining++;
|
||||
@@ -1340,7 +1372,7 @@ uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
|
||||
match_result_t result = IRrecv::matchData(data_ptr + offset, nbits,
|
||||
onemark, onespace,
|
||||
zeromark, zerospace, tolerance,
|
||||
excess, MSBfirst);
|
||||
excess, MSBfirst, kexpectspace);
|
||||
if (!result.success) return 0;
|
||||
*result_bits_ptr = result.data;
|
||||
offset += result.used;
|
||||
@@ -1349,7 +1381,7 @@ uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
|
||||
remaining - offset, nbits / 8,
|
||||
onemark, onespace,
|
||||
zeromark, zerospace, tolerance,
|
||||
excess, MSBfirst);
|
||||
excess, MSBfirst, kexpectspace);
|
||||
if (!data_used) return 0;
|
||||
offset += data_used;
|
||||
}
|
||||
|
||||
11
src/IRrecv.h
11
src/IRrecv.h
@@ -192,14 +192,16 @@ class IRrecv {
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
const bool MSBfirst = true,
|
||||
const bool expectlastspace = true);
|
||||
uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbytes,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
const bool MSBfirst = true,
|
||||
const bool expectlastspace = true);
|
||||
uint16_t matchGeneric(volatile uint16_t *data_ptr,
|
||||
uint64_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbits,
|
||||
@@ -509,6 +511,11 @@ class IRrecv {
|
||||
const uint16_t nbits = kLasertagBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MILESTAG2
|
||||
bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMilesTag2ShotBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_CARRIER_AC
|
||||
bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAcBits,
|
||||
|
||||
@@ -719,6 +719,13 @@
|
||||
#define SEND_ELITESCREENS _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_ELITESCREENS
|
||||
|
||||
#ifndef DECODE_MILESTAG2
|
||||
#define DECODE_MILESTAG2 _IR_ENABLE_DEFAULT_
|
||||
#endif // DECODE_MILESTAG2
|
||||
#ifndef SEND_MILESTAG2
|
||||
#define SEND_MILESTAG2 _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_MILESTAG2
|
||||
|
||||
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
|
||||
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
|
||||
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
|
||||
@@ -867,8 +874,9 @@ enum decode_type_t {
|
||||
MIRAGE,
|
||||
ELITESCREENS, // 95
|
||||
PANASONIC_AC32,
|
||||
MILESTAG2,
|
||||
// Add new entries before this one, and update it to point to the last entry.
|
||||
kLastDecodeType = PANASONIC_AC32,
|
||||
kLastDecodeType = MILESTAG2,
|
||||
};
|
||||
|
||||
// Message lengths & required repeat values
|
||||
@@ -1094,6 +1102,9 @@ const uint16_t kZepealBits = 16;
|
||||
const uint16_t kZepealMinRepeat = 4;
|
||||
const uint16_t kVoltasBits = 80;
|
||||
const uint16_t kVoltasStateLength = 10;
|
||||
const uint16_t kMilesTag2ShotBits = 14;
|
||||
const uint16_t kMilesTag2MsgBits = 24;
|
||||
const uint16_t kMilesMinRepeat = 0;
|
||||
|
||||
|
||||
// Legacy defines. (Deprecated)
|
||||
|
||||
@@ -712,6 +712,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
|
||||
return kHitachiAc424Bits;
|
||||
case KELVINATOR:
|
||||
return kKelvinatorBits;
|
||||
case MILESTAG2:
|
||||
return kMilesTag2ShotBits;
|
||||
case MIRAGE:
|
||||
return kMirageBits;
|
||||
case MITSUBISHI_AC:
|
||||
@@ -897,6 +899,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
|
||||
sendMidea24(data, nbits, min_repeat);
|
||||
break;
|
||||
#endif // SEND_MIDEA24
|
||||
#if SEND_MILESTAG2
|
||||
case MILESTAG2:
|
||||
sendMilestag2(data, nbits, min_repeat);
|
||||
break;
|
||||
#endif // SEND_MILESTAG2
|
||||
#if SEND_MITSUBISHI
|
||||
case MITSUBISHI:
|
||||
sendMitsubishi(data, nbits, min_repeat);
|
||||
|
||||
@@ -682,6 +682,13 @@ class IRsend {
|
||||
const uint16_t nbits = kEliteScreensBits,
|
||||
const uint16_t repeat = kEliteScreensDefaultRepeat);
|
||||
#endif // SEND_ELITESCREENS
|
||||
#if SEND_MILESTAG2
|
||||
// Since There 2 types of transmissions
|
||||
// (14bits for Shooting by default, you can set 24 bit for msg delivery)
|
||||
void sendMilestag2(const uint64_t data,
|
||||
const uint16_t nbits = kMilesTag2ShotBits,
|
||||
const uint16_t repeat = kMilesMinRepeat);
|
||||
#endif // SEND_MILESTAG2
|
||||
|
||||
protected:
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
@@ -278,5 +278,6 @@ const PROGMEM char *kAllProtocolNamesStr =
|
||||
D_STR_MIRAGE "\x0"
|
||||
D_STR_ELITESCREENS "\x0"
|
||||
D_STR_PANASONIC_AC32 "\x0"
|
||||
D_STR_MILESTAG2 "\x0"
|
||||
///< New protocol strings should be added just above this line.
|
||||
"\x0"; ///< This string requires double null termination.
|
||||
|
||||
113
src/ir_MilesTag2.cpp
Normal file
113
src/ir_MilesTag2.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2021 Victor Mukayev (vitos1k)
|
||||
// Copyright 2021 David Conran (crankyoldgit)
|
||||
|
||||
/// @file
|
||||
/// @brief Support for the MilesTag2 IR protocol for LaserTag gaming
|
||||
/// @see http://hosting.cmalton.me.uk/chrism/lasertag/MT2Proto.pdf
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1360
|
||||
|
||||
// Supports:
|
||||
// Brand: Milestag2, Model: Various
|
||||
|
||||
// TODO(vitos1k): This implementation would support only
|
||||
// short SHOT packets(14bits) and MSGs = 24bits. Support
|
||||
// for long MSGs > 24bits is TODO
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
// Shot packets have this bit as `0`
|
||||
const uint16_t kMilesTag2ShotMask = 1 << (kMilesTag2ShotBits - 1);
|
||||
// Msg packets have this bit as `1`
|
||||
const uint32_t kMilesTag2MsgMask = 1 << (kMilesTag2MsgBits - 1);
|
||||
const uint8_t kMilesTag2MsgTerminator = 0xE8;
|
||||
const uint16_t kMilesTag2HdrMark = 2400; /// uSeconds.
|
||||
const uint16_t kMilesTag2Space = 600; /// uSeconds.
|
||||
const uint16_t kMilesTag2OneMark = 1200; /// uSeconds.
|
||||
const uint16_t kMilesTag2ZeroMark = 600; /// uSeconds.
|
||||
const uint16_t kMilesTag2RptLength = 32000; /// uSeconds.
|
||||
const uint16_t kMilesTag2StdFreq = 38000; /// Hz.
|
||||
const uint16_t kMilesTag2StdDuty = 25; /// Percentage.
|
||||
|
||||
#if SEND_MILESTAG2
|
||||
/// Send a MilesTag2 formatted Shot/Msg packet.
|
||||
/// Status: ALPHA / Probably works but needs testing with a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendMilestag2(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(
|
||||
kMilesTag2HdrMark, kMilesTag2Space, // Header
|
||||
kMilesTag2OneMark, kMilesTag2Space, // 1 bit
|
||||
kMilesTag2ZeroMark, kMilesTag2Space, // 0 bit
|
||||
0, // No footer mark
|
||||
kMilesTag2RptLength, data, nbits, kMilesTag2StdFreq, true, // MSB First
|
||||
repeat, kMilesTag2StdDuty);
|
||||
}
|
||||
#endif // SEND_MILESTAG2
|
||||
|
||||
#if DECODE_MILESTAG2
|
||||
/// Decode the supplied MilesTag2 message.
|
||||
/// Status: ALPHA / Probably works but needs testing with a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the 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 True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1360
|
||||
bool IRrecv::decodeMilestag2(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
uint64_t data = 0;
|
||||
// Header + Data + Optional Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kMilesTag2HdrMark, kMilesTag2Space,
|
||||
kMilesTag2OneMark, kMilesTag2Space,
|
||||
kMilesTag2ZeroMark, kMilesTag2Space,
|
||||
0, kMilesTag2RptLength, true)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case kMilesTag2ShotBits:
|
||||
// Is it a valid shot packet?
|
||||
if (data & kMilesTag2ShotMask) return false;
|
||||
break;
|
||||
case kMilesTag2MsgBits:
|
||||
// Is it a valid msg packet? i.e. Msg bit set & Terminator present.
|
||||
if (!(data & kMilesTag2MsgMask) ||
|
||||
((data & 0xFF) != kMilesTag2MsgTerminator))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
DPRINT("incorrect nbits:");
|
||||
DPRINTLN(nbits);
|
||||
return false; // The request doesn't strictly match the protocol defn.
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = decode_type_t::MILESTAG2;
|
||||
switch (nbits) {
|
||||
case kMilesTag2ShotBits:
|
||||
results->command = data & 0x3F; // Team & Damage
|
||||
results->address = data >> 6; // Player ID.
|
||||
break;
|
||||
case kMilesTag2MsgBits:
|
||||
results->command = (data >> 8) & 0xFF; // Message data
|
||||
results->address = (data >> 16) & 0x7F; // Message ID
|
||||
break;
|
||||
default:
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MILESTAG2
|
||||
@@ -628,6 +628,9 @@
|
||||
#ifndef D_STR_MIDEA24
|
||||
#define D_STR_MIDEA24 "MIDEA24"
|
||||
#endif // D_STR_MIDEA24
|
||||
#ifndef D_STR_MILESTAG2
|
||||
#define D_STR_MILESTAG2 "MILESTAG2"
|
||||
#endif // D_STR_MILESTAG2
|
||||
#ifndef D_STR_MIRAGE
|
||||
#define D_STR_MIRAGE "MIRAGE"
|
||||
#endif // D_STR_MIRAGE
|
||||
|
||||
@@ -1014,6 +1014,74 @@ TEST(TestMatchGeneric, MissingHeaderFooter) {
|
||||
EXPECT_EQ(kentries - 2, entries_used);
|
||||
}
|
||||
|
||||
TEST(TestMatchGeneric, MissingFooterMarkEncoded) {
|
||||
IRsendTest irsend(0);
|
||||
IRrecv irrecv(1);
|
||||
irsend.begin();
|
||||
|
||||
const uint16_t kentries = 10;
|
||||
uint16_t data[kentries] = { // Mark encoded data.
|
||||
8000, // Header mark
|
||||
4000, // Header space
|
||||
2000, 500, // Bit #0 (1)
|
||||
1000, 500, // Bit #1 (0)
|
||||
2000, 500, // Bit #2 (1)
|
||||
1000, 500}; // Bit #3 (0)
|
||||
// (No Footer)
|
||||
|
||||
uint16_t offset = kStartOffset;
|
||||
irsend.reset();
|
||||
|
||||
// Send it with the "trailing data space."
|
||||
irsend.sendRaw(data, kentries, 38000);
|
||||
irsend.makeDecodeResult();
|
||||
uint16_t entries_used = 0;
|
||||
|
||||
uint64_t result_data = 0;
|
||||
|
||||
// No footer match
|
||||
entries_used = irrecv.matchGeneric(
|
||||
irsend.capture.rawbuf + offset, &result_data,
|
||||
irsend.capture.rawlen - offset,
|
||||
4, // nbits
|
||||
8000, 4000, // Header
|
||||
2000, 500, // one mark & space
|
||||
1000, 500, // zero mark & space
|
||||
0, 0, // NO Footer
|
||||
true, // atleast on the footer space.
|
||||
1, // 1% Tolerance
|
||||
0, // No excess margin
|
||||
true); // MSB first.
|
||||
ASSERT_NE(0, entries_used);
|
||||
EXPECT_EQ(0b1010, result_data);
|
||||
EXPECT_EQ(irsend.capture.rawlen- kStartOffset, kentries);
|
||||
EXPECT_EQ(irsend.capture.rawlen - kStartOffset - 1, entries_used);
|
||||
EXPECT_EQ(kentries - 1, entries_used);
|
||||
|
||||
// Now send it again, but make it appear like a real capture.
|
||||
// i.e. The trailing space is removed.
|
||||
irsend.reset();
|
||||
irsend.sendRaw(data, kentries - 1, 38000);
|
||||
irsend.makeDecodeResult();
|
||||
entries_used = irrecv.matchGeneric(
|
||||
irsend.capture.rawbuf + offset, &result_data,
|
||||
irsend.capture.rawlen - offset,
|
||||
4, // nbits
|
||||
8000, 4000, // Header
|
||||
2000, 500, // one mark & space
|
||||
1000, 500, // zero mark & space
|
||||
0, 0, // NO Footer
|
||||
true, // atleast on the footer space.
|
||||
1, // 1% Tolerance
|
||||
0, // No excess margin
|
||||
true); // MSB first.
|
||||
ASSERT_NE(0, entries_used);
|
||||
EXPECT_EQ(0b1010, result_data);
|
||||
EXPECT_EQ(irsend.capture.rawlen - kStartOffset, kentries - 1);
|
||||
EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used);
|
||||
EXPECT_EQ(kentries - 1, entries_used);
|
||||
}
|
||||
|
||||
TEST(TestMatchGeneric, BitOrdering) {
|
||||
IRsendTest irsend(0);
|
||||
IRrecv irrecv(1);
|
||||
|
||||
129
test/ir_Milestag2_test.cpp
Normal file
129
test/ir_Milestag2_test.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2021 David Conran
|
||||
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRrecv_test.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRsend_test.h"
|
||||
#include "IRutils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(TestUtils, Housekeeping) {
|
||||
ASSERT_EQ("MILESTAG2", typeToString(decode_type_t::MILESTAG2));
|
||||
ASSERT_EQ(decode_type_t::MILESTAG2, strToDecodeType("MILESTAG2"));
|
||||
ASSERT_FALSE(hasACState(decode_type_t::MILESTAG2));
|
||||
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::MILESTAG2));
|
||||
ASSERT_EQ(kMilesTag2ShotBits, IRsend::defaultBits(decode_type_t::MILESTAG2));
|
||||
}
|
||||
|
||||
// Test sending typical data only.
|
||||
TEST(TestSendMilestag2, SendDataOnly) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
irsend.begin();
|
||||
|
||||
// Shot packet
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x379);
|
||||
EXPECT_EQ(
|
||||
"f38000d25"
|
||||
"m2400s600"
|
||||
"m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600m1200s600"
|
||||
"m1200s600m1200s600m1200s600m600s600m600s600m1200s32600",
|
||||
irsend.outputStr());
|
||||
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x379, kMilesTag2ShotBits);
|
||||
EXPECT_EQ(
|
||||
"f38000d25"
|
||||
"m2400s600"
|
||||
"m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600m1200s600"
|
||||
"m1200s600m1200s600m1200s600m600s600m600s600m1200s32600",
|
||||
irsend.outputStr());
|
||||
|
||||
// Msg packet
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x8123E8, kMilesTag2MsgBits);
|
||||
EXPECT_EQ(
|
||||
"f38000d25"
|
||||
"m2400s600"
|
||||
"m1200s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600"
|
||||
"m600s600m600s600m1200s600m600s600m600s600m600s600m1200s600m1200s600"
|
||||
"m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600m600s32600",
|
||||
irsend.outputStr());
|
||||
}
|
||||
|
||||
TEST(TestDecodeMilestag2, SyntheticSelfDecode) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
|
||||
irsend.begin();
|
||||
// Shot packet
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x379);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(MILESTAG2, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kMilesTag2ShotBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x379, irsend.capture.value);
|
||||
EXPECT_EQ(0xD, irsend.capture.address);
|
||||
EXPECT_EQ(0x39, irsend.capture.command);
|
||||
|
||||
// Msg packet
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x8123E8, kMilesTag2MsgBits);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(MILESTAG2, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kMilesTag2MsgBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x8123E8, irsend.capture.value);
|
||||
EXPECT_EQ(0x1, irsend.capture.address);
|
||||
EXPECT_EQ(0x23, irsend.capture.command);
|
||||
}
|
||||
|
||||
TEST(TestDecodeMilestag2, FailToDecodeBadData) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
|
||||
irsend.begin();
|
||||
// Shot packet with the Shot packet bit incorrectly set.
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0xFF79);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_NE(MILESTAG2, irsend.capture.decode_type);
|
||||
|
||||
// Msg packet with the Msg packet bit cleared.
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x0123E8, kMilesTag2MsgBits);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_NE(MILESTAG2, irsend.capture.decode_type);
|
||||
|
||||
// Msg packet with a bad Msg terminator.
|
||||
irsend.reset();
|
||||
irsend.sendMilestag2(0x8123E9, kMilesTag2MsgBits);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_NE(MILESTAG2, irsend.capture.decode_type);
|
||||
}
|
||||
|
||||
TEST(TestDecodeMilestag2, RealSelfDecodeExample) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/pull/1380#issuecomment-761159985
|
||||
const uint16_t rawData[29] = {
|
||||
2440, 602,
|
||||
608, 600, 606, 600, 606, 600, 602, 606, 1208, 602, 1216, 596, 604, 600,
|
||||
1214, 598, 1212, 600, 1208, 604, 1208, 602, 606, 600, 610, 596, 1210};
|
||||
|
||||
irsend.begin();
|
||||
irsend.reset();
|
||||
irsend.sendRaw(rawData, 29, 38000);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(MILESTAG2, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kMilesTag2ShotBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x379, irsend.capture.value);
|
||||
EXPECT_EQ(0xD, irsend.capture.address);
|
||||
EXPECT_EQ(0x39, irsend.capture.command);
|
||||
}
|
||||
Reference in New Issue
Block a user