infrared: add Kaseikyo IR protocol (#1965)

* infrared: add Kaseikyo IR protocol

Add Kaseikyo IR protocol support. This protocol is also called the Panasonic protocol and is used by a number of manufacturers including Denon.

The protocol includes a vendor field and a number of fields that are vendor specific. To support the format of address+command used by flipper the vendor+genre1+genre2+id fields are encoded into the address while the data is used for the command.

There are older versions of the protocol that used a reverse bit order that are not supported.

Protocol information:
- https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
- http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo
- https://www.denon.com/-/media/files/documentmaster/denonna/avr-x3700h_avc-x3700h_ir_code_v01_04062020.doc

* Format and add unit test to Kaseikyo IR codec.

Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com>
This commit is contained in:
Samuel Stauffer 2022-11-07 06:38:04 -08:00 committed by GitHub
parent 65005e71d2
commit aa2ecbe80f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 322 additions and 0 deletions

View file

@ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) {
infrared_test_run_decoder(InfraredProtocolRC5, 5);
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST(infrared_test_decoder_nec) {
@ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) {
infrared_test_run_encoder(InfraredProtocolRC6, 1);
}
MU_TEST(infrared_test_decoder_kaseikyo) {
infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
}
MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
@ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) {
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
}
MU_TEST_SUITE(infrared_test) {
@ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) {
MU_RUN_TEST(infrared_test_decoder_nec);
MU_RUN_TEST(infrared_test_decoder_samsung32);
MU_RUN_TEST(infrared_test_decoder_necext1);
MU_RUN_TEST(infrared_test_decoder_kaseikyo);
MU_RUN_TEST(infrared_test_decoder_mixed);
MU_RUN_TEST(infrared_test_encoder_decoder_all);
}

View file

@ -0,0 +1,105 @@
Filetype: IR tests file
Version: 1
#
name: decoder_input1
type: raw
data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436
#
name: decoder_expected1
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 41 54 32 00
command: 1B 00 00 00
repeat: false
#
name: decoder_input2
type: raw
data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434
#
name: decoder_expected2
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 41 54 32 00
command: 1C 00 00 00
repeat: false
#
name: decoder_input3
type: raw
data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415
#
name: decoder_expected3
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 41 54 32 00
command: 70 01 00 00
repeat: false
#
name: decoder_input4
type: raw
data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414
#
name: decoder_expected4
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 43 54 32 00
command: 70 01 00 00
repeat: false
#
name: decoder_input5
type: raw
data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434
#
name: decoder_expected5
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 43 54 32 00
command: 1B 00 00 00
repeat: false
#
name: decoder_input6
type: raw
data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412
#
name: decoder_expected6
type: parsed_array
count: 1
#
protocol: Kaseikyo
address: 43 54 32 00
command: 05 00 00 00
repeat: false
#
name: encoder_decoder_input1
type: parsed_array
count: 4
#
protocol: Kaseikyo
address: 41 54 32 00
command: 1B 00 00 00
repeat: false
#
protocol: Kaseikyo
address: 41 54 32 00
command: 70 01 00 00
repeat: false
#
protocol: Kaseikyo
address: 43 54 32 00
command: 05 00 00 00
repeat: false
#
protocol: Kaseikyo
address: 43 54 32 00
command: 1B 00 00 00
repeat: false
#

View file

@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = {
.decode_repeat = NULL,
.encode_repeat = infrared_encoder_sirc_encode_repeat,
};
const InfraredCommonProtocolSpec protocol_kaseikyo = {
.timings =
{
.preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK,
.preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE,
.bit1_mark = INFRARED_KASEIKYO_BIT1_MARK,
.bit1_space = INFRARED_KASEIKYO_BIT1_SPACE,
.bit0_mark = INFRARED_KASEIKYO_BIT0_MARK,
.bit0_space = INFRARED_KASEIKYO_BIT0_SPACE,
.preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE,
.bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE,
.silence_time = INFRARED_KASEIKYO_SILENCE,
.min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME,
},
.databit_len[0] = 48,
.no_stop_bit = false,
.decode = infrared_common_decode_pdwm,
.encode = infrared_common_encode_pdwm,
.interpret = infrared_decoder_kaseikyo_interpret,
.decode_repeat = NULL,
.encode_repeat = NULL,
};

View file

@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = {
.free = infrared_encoder_sirc_free},
.get_protocol_spec = infrared_sirc_get_spec,
},
{
.decoder =
{.alloc = infrared_decoder_kaseikyo_alloc,
.decode = infrared_decoder_kaseikyo_decode,
.reset = infrared_decoder_kaseikyo_reset,
.check_ready = infrared_decoder_kaseikyo_check_ready,
.free = infrared_decoder_kaseikyo_free},
.encoder =
{.alloc = infrared_encoder_kaseikyo_alloc,
.encode = infrared_encoder_kaseikyo_encode,
.reset = infrared_encoder_kaseikyo_reset,
.free = infrared_encoder_kaseikyo_free},
.get_protocol_spec = infrared_kaseikyo_get_spec,
},
};
static int infrared_find_index_by_protocol(InfraredProtocol protocol);

View file

@ -31,6 +31,7 @@ typedef enum {
InfraredProtocolSIRC,
InfraredProtocolSIRC15,
InfraredProtocolSIRC20,
InfraredProtocolKaseikyo,
InfraredProtocolMAX,
} InfraredProtocol;

View file

@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat(
bool* level);
extern const InfraredCommonProtocolSpec protocol_sirc;
/***************************************************************************************************
* Kaseikyo protocol description
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
****************************************************************************************************
* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble
* mark space Modulation up to period repeat repeat
* mark space
*
* 3360 1665 48 bit ...130000 3456 1728
* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________
* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________
*
***************************************************************************************************/
#define INFRARED_KASEIKYO_UNIT 432
#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT)
#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT
#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000
#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD
#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000
#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK
#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000)
#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
void* infrared_decoder_kaseikyo_alloc(void);
void infrared_decoder_kaseikyo_reset(void* decoder);
void infrared_decoder_kaseikyo_free(void* decoder);
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder);
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration);
void* infrared_encoder_kaseikyo_alloc(void);
InfraredStatus
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level);
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message);
void infrared_encoder_kaseikyo_free(void* encoder_ptr);
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder);
InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder);
InfraredStatus infrared_encoder_kaseikyo_encode_repeat(
InfraredCommonEncoder* encoder,
uint32_t* duration,
bool* level);
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol);
extern const InfraredCommonProtocolSpec protocol_kaseikyo;

View file

@ -0,0 +1,54 @@
#include "infrared.h"
#include "infrared_protocol_defs_i.h"
#include <stdbool.h>
#include <stdint.h>
#include <furi.h>
#include "../infrared_i.h"
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) {
return infrared_common_decoder_check_ready(ctx);
}
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) {
furi_assert(decoder);
bool result = false;
uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0];
uint8_t vendor_parity = decoder->data[2] & 0x0f;
uint8_t genre1 = decoder->data[2] >> 4;
uint8_t genre2 = decoder->data[3] & 0x0f;
uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4);
uint8_t id = decoder->data[4] >> 6;
uint8_t parity = decoder->data[5];
uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1];
vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4);
uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4];
if(vendor_parity == vendor_parity_check && parity == parity_check) {
decoder->message.command = (uint32_t)data;
decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) |
((uint32_t)genre1 << 4) | (uint32_t)genre2;
decoder->message.protocol = InfraredProtocolKaseikyo;
decoder->message.repeat = false;
result = true;
}
return result;
}
void* infrared_decoder_kaseikyo_alloc(void) {
return infrared_common_decoder_alloc(&protocol_kaseikyo);
}
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) {
return infrared_common_decode(decoder, level, duration);
}
void infrared_decoder_kaseikyo_free(void* decoder) {
infrared_common_decoder_free(decoder);
}
void infrared_decoder_kaseikyo_reset(void* decoder) {
infrared_common_decoder_reset(decoder);
}

View file

@ -0,0 +1,45 @@
#include <core/check.h>
#include "common/infrared_common_i.h"
#include <stdint.h>
#include "../infrared_i.h"
#include "infrared_protocol_defs_i.h"
#include <furi.h>
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) {
furi_assert(encoder_ptr);
InfraredCommonEncoder* encoder = encoder_ptr;
infrared_common_encoder_reset(encoder);
uint32_t address = message->address;
uint16_t command = message->command;
uint8_t id = (address >> 24) & 3;
uint16_t vendor_id = (address >> 8) & 0xffff;
uint8_t genre1 = (address >> 4) & 0xf;
uint8_t genre2 = address & 0xf;
encoder->data[0] = (uint8_t)(vendor_id & 0xff);
encoder->data[1] = (uint8_t)(vendor_id >> 8);
uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1];
vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4);
encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4);
encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4);
encoder->data[4] = (id << 6) | (uint8_t)(command >> 4);
encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4];
encoder->bits_to_encode = encoder->protocol->databit_len[0];
}
void* infrared_encoder_kaseikyo_alloc(void) {
return infrared_common_encoder_alloc(&protocol_kaseikyo);
}
void infrared_encoder_kaseikyo_free(void* encoder_ptr) {
infrared_common_encoder_free(encoder_ptr);
}
InfraredStatus
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) {
return infrared_common_encode(encoder_ptr, duration, level);
}

View file

@ -0,0 +1,17 @@
#include "../infrared_i.h"
#include "infrared_protocol_defs_i.h"
static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = {
.name = "Kaseikyo",
.address_length = 26,
.command_length = 10,
.frequency = INFRARED_COMMON_CARRIER_FREQUENCY,
.duty_cycle = INFRARED_COMMON_DUTY_CYCLE,
};
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) {
if(protocol == InfraredProtocolKaseikyo)
return &infrared_kaseikyo_protocol_specification;
else
return NULL;
}