flipperzero-firmware/applications/tests/infrared_decoder_encoder/infrared_decoder_encoder_test.c

332 lines
12 KiB
C
Raw Normal View History

#include <furi.h>
#include "../minunit.h"
#include "infrared.h"
#include "common/infrared_common_i.h"
#include "test_data/infrared_nec_test_data.srcdata"
#include "test_data/infrared_necext_test_data.srcdata"
#include "test_data/infrared_samsung_test_data.srcdata"
#include "test_data/infrared_rc6_test_data.srcdata"
#include "test_data/infrared_rc5_test_data.srcdata"
#include "test_data/infrared_sirc_test_data.srcdata"
#define RUN_ENCODER(data, expected) \
run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
#define RUN_DECODER(data, expected) \
run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected))
#define RUN_ENCODER_DECODER(data) run_encoder_decoder((data), COUNT_OF(data))
static InfraredDecoderHandler* decoder_handler;
static InfraredEncoderHandler* encoder_handler;
static void test_setup(void) {
decoder_handler = infrared_alloc_decoder();
encoder_handler = infrared_alloc_encoder();
}
static void test_teardown(void) {
infrared_free_decoder(decoder_handler);
infrared_free_encoder(encoder_handler);
}
static void compare_message_results(
const InfraredMessage* message_decoded,
const InfraredMessage* message_expected) {
mu_check(message_decoded->protocol == message_expected->protocol);
mu_check(message_decoded->command == message_expected->command);
mu_check(message_decoded->address == message_expected->address);
if((message_expected->protocol == InfraredProtocolSIRC) ||
(message_expected->protocol == InfraredProtocolSIRC15) ||
(message_expected->protocol == InfraredProtocolSIRC20)) {
mu_check(message_decoded->repeat == false);
} else {
mu_check(message_decoded->repeat == message_expected->repeat);
}
}
/* Encodes signal and merges same levels (high+high, low+low) */
static void run_encoder_fill_array(
InfraredEncoderHandler* handler,
uint32_t* timings,
uint32_t* timings_len,
bool* start_level) {
uint32_t duration = 0;
bool level = false;
bool level_read;
InfraredStatus status = InfraredStatusError;
int i = 0;
bool first = true;
while(1) {
status = infrared_encode(handler, &duration, &level_read);
if(first) {
if(start_level) *start_level = level_read;
first = false;
timings[0] = 0;
} else if(level_read != level) {
++i;
furi_check(i < *timings_len);
timings[i] = 0;
}
level = level_read;
timings[i] += duration;
furi_check((status == InfraredStatusOk) || (status == InfraredStatusDone));
if(status == InfraredStatusDone) break;
}
*timings_len = i + 1;
}
// messages in input array for encoder should have one protocol
static void run_encoder(
const InfraredMessage input_messages[],
uint32_t input_messages_len,
const uint32_t expected_timings[],
uint32_t expected_timings_len) {
uint32_t* timings = 0;
uint32_t timings_len = 200;
uint32_t j = 0;
[FL-2274] Inventing streams and moving FFF to them (#981) * Streams: string stream * String stream: updated insert/delete api * Streams: generic stream interface and string stream implementation * Streams: helpers for insert and delete_and_insert * FFF: now compatible with streams * MinUnit: introduced tests with arguments * FFF: stream access violation * Streams: copy data between streams * Streams: file stream * FFF: documentation * FFStream: documentation * FFF: alloc as file * MinUnit: support for nested tests * Streams: changed delete_and_insert, now it returns success flag. Added ability dump stream inner parameters and data to cout. * FFF: simplified file open function * Streams: unit tests * FFF: tests * Streams: declare cache_size constant as define, to allow variable modified arrays * FFF: lib moved to a separate folder * iButton: new FFF * RFID: new FFF * Animations: new FFF * IR: new FFF * NFC: new FFF * Flipper file format: delete lib * U2F: new FFF * Subghz: new FFF and streams * Streams: read line * Streams: split * FuriCore: implement memset with extra asserts * FuriCore: implement extra heap asserts without inventing memset * Scene manager: protected access to the scene id stack with a size check * NFC worker: dirty fix for issue where hal_nfc was busy on app start * Furi: update allocator to erase memory on allocation. Replace furi_alloc with malloc. * FuriCore: cleanup memmgr code. * Furi HAL: furi_hal_init is split into critical and non-critical parts. The critical part is currently clock and console. * Memmgr: added ability to track allocations and deallocations through console. * FFStream: some speedup * Streams, FF: minor fixes * Tests: restore * File stream: a slightly more thread-safe version of file_stream_delete_and_insert Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-02-18 19:53:46 +00:00
timings = malloc(sizeof(uint32_t) * timings_len);
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
const InfraredMessage* message = &input_messages[message_counter];
if(!message->repeat) {
infrared_reset_encoder(encoder_handler, message);
}
timings_len = 200;
run_encoder_fill_array(encoder_handler, timings, &timings_len, NULL);
furi_check(timings_len <= 200);
for(int i = 0; i < timings_len; ++i, ++j) {
mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
mu_assert(j < expected_timings_len, "encoded more timings than expected");
}
}
free(timings);
mu_assert(j == expected_timings_len, "encoded less timings than expected");
}
static void
run_encoder_decoder(const InfraredMessage input_messages[], uint32_t input_messages_len) {
uint32_t* timings = 0;
uint32_t timings_len = 200;
bool level = false;
[FL-2274] Inventing streams and moving FFF to them (#981) * Streams: string stream * String stream: updated insert/delete api * Streams: generic stream interface and string stream implementation * Streams: helpers for insert and delete_and_insert * FFF: now compatible with streams * MinUnit: introduced tests with arguments * FFF: stream access violation * Streams: copy data between streams * Streams: file stream * FFF: documentation * FFStream: documentation * FFF: alloc as file * MinUnit: support for nested tests * Streams: changed delete_and_insert, now it returns success flag. Added ability dump stream inner parameters and data to cout. * FFF: simplified file open function * Streams: unit tests * FFF: tests * Streams: declare cache_size constant as define, to allow variable modified arrays * FFF: lib moved to a separate folder * iButton: new FFF * RFID: new FFF * Animations: new FFF * IR: new FFF * NFC: new FFF * Flipper file format: delete lib * U2F: new FFF * Subghz: new FFF and streams * Streams: read line * Streams: split * FuriCore: implement memset with extra asserts * FuriCore: implement extra heap asserts without inventing memset * Scene manager: protected access to the scene id stack with a size check * NFC worker: dirty fix for issue where hal_nfc was busy on app start * Furi: update allocator to erase memory on allocation. Replace furi_alloc with malloc. * FuriCore: cleanup memmgr code. * Furi HAL: furi_hal_init is split into critical and non-critical parts. The critical part is currently clock and console. * Memmgr: added ability to track allocations and deallocations through console. * FFStream: some speedup * Streams, FF: minor fixes * Tests: restore * File stream: a slightly more thread-safe version of file_stream_delete_and_insert Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-02-18 19:53:46 +00:00
timings = malloc(sizeof(uint32_t) * timings_len);
for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) {
const InfraredMessage* message_encoded = &input_messages[message_counter];
if(!message_encoded->repeat) {
infrared_reset_encoder(encoder_handler, message_encoded);
}
timings_len = 200;
run_encoder_fill_array(encoder_handler, timings, &timings_len, &level);
furi_check(timings_len <= 200);
const InfraredMessage* message_decoded = 0;
for(int i = 0; i < timings_len; ++i) {
message_decoded = infrared_decode(decoder_handler, level, timings[i]);
if((i == timings_len - 2) && level && message_decoded) {
/* In case we end with space timing - message can be decoded at last mark */
break;
} else if(i < timings_len - 1) {
mu_check(!message_decoded);
} else {
if(!message_decoded) {
message_decoded = infrared_check_decoder_ready(decoder_handler);
}
mu_check(message_decoded);
}
level = !level;
}
if(message_decoded) {
compare_message_results(message_decoded, message_encoded);
} else {
mu_check(0);
}
}
free(timings);
}
static void run_decoder(
const uint32_t* input_delays,
uint32_t input_delays_len,
const InfraredMessage* message_expected,
uint32_t message_expected_len) {
InfraredMessage message_decoded_check_local;
bool level = 0;
uint32_t message_counter = 0;
const InfraredMessage* message_decoded = 0;
for(uint32_t i = 0; i < input_delays_len; ++i) {
const InfraredMessage* message_decoded_check = 0;
if(input_delays[i] > INFRARED_RAW_RX_TIMING_DELAY_US) {
message_decoded_check = infrared_check_decoder_ready(decoder_handler);
if(message_decoded_check) {
/* infrared_decode() can reset message, but we have to call infrared_decode() to perform real
* simulation: infrared_check() by timeout, then infrared_decode() when meet edge */
message_decoded_check_local = *message_decoded_check;
message_decoded_check = &message_decoded_check_local;
}
}
message_decoded = infrared_decode(decoder_handler, level, input_delays[i]);
if(message_decoded_check || message_decoded) {
mu_assert(
!(message_decoded_check && message_decoded),
"both messages decoded: check_ready() and infrared_decode()");
if(message_decoded_check) {
message_decoded = message_decoded_check;
}
mu_assert(message_counter < message_expected_len, "decoded more than expected");
compare_message_results(message_decoded, &message_expected[message_counter]);
++message_counter;
}
level = !level;
}
message_decoded = infrared_check_decoder_ready(decoder_handler);
if(message_decoded) {
compare_message_results(message_decoded, &message_expected[message_counter]);
++message_counter;
}
mu_assert(message_counter == message_expected_len, "decoded less than expected");
}
MU_TEST(test_decoder_samsung32) {
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
}
MU_TEST(test_mix) {
RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2);
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
// can use encoder data for decoding, but can't do opposite
RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1);
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
RUN_DECODER(test_decoder_nec_input3, test_decoder_nec_expected3);
RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5);
RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1);
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
}
MU_TEST(test_decoder_nec) {
RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1);
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
RUN_DECODER(test_decoder_nec_input3, test_decoder_nec_expected3);
}
MU_TEST(test_decoder_unexpected_end_in_sequence) {
// test_decoder_nec_input1 and test_decoder_nec_input2 shuts unexpected
RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1);
RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1);
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2);
}
MU_TEST(test_decoder_necext1) {
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1);
}
MU_TEST(test_decoder_long_packets_with_nec_start) {
RUN_DECODER(test_decoder_nec42ext_input1, test_decoder_nec42ext_expected1);
RUN_DECODER(test_decoder_nec42ext_input2, test_decoder_nec42ext_expected2);
}
MU_TEST(test_encoder_sirc) {
RUN_ENCODER(test_encoder_sirc_input1, test_encoder_sirc_expected1);
RUN_ENCODER(test_encoder_sirc_input2, test_encoder_sirc_expected2);
}
MU_TEST(test_decoder_sirc) {
RUN_DECODER(test_decoder_sirc_input3, test_decoder_sirc_expected3);
RUN_DECODER(test_decoder_sirc_input1, test_decoder_sirc_expected1);
RUN_DECODER(test_decoder_sirc_input2, test_decoder_sirc_expected2);
RUN_DECODER(test_decoder_sirc_input4, test_decoder_sirc_expected4);
RUN_DECODER(test_decoder_sirc_input5, test_decoder_sirc_expected5);
RUN_ENCODER_DECODER(test_sirc);
}
MU_TEST(test_decoder_rc5) {
RUN_DECODER(test_decoder_rc5x_input1, test_decoder_rc5x_expected1);
RUN_DECODER(test_decoder_rc5_input1, test_decoder_rc5_expected1);
RUN_DECODER(test_decoder_rc5_input2, test_decoder_rc5_expected2);
RUN_DECODER(test_decoder_rc5_input3, test_decoder_rc5_expected3);
RUN_DECODER(test_decoder_rc5_input4, test_decoder_rc5_expected4);
RUN_DECODER(test_decoder_rc5_input5, test_decoder_rc5_expected5);
RUN_DECODER(test_decoder_rc5_input6, test_decoder_rc5_expected6);
RUN_DECODER(test_decoder_rc5_input_all_repeats, test_decoder_rc5_expected_all_repeats);
}
MU_TEST(test_encoder_rc5x) {
RUN_ENCODER(test_decoder_rc5x_expected1, test_decoder_rc5x_input1);
}
MU_TEST(test_encoder_rc5) {
RUN_ENCODER(test_decoder_rc5_expected_all_repeats, test_decoder_rc5_input_all_repeats);
}
MU_TEST(test_decoder_rc6) {
RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1);
}
MU_TEST(test_encoder_rc6) {
RUN_ENCODER(test_encoder_rc6_input1, test_encoder_rc6_expected1);
}
MU_TEST(test_encoder_decoder_all) {
RUN_ENCODER_DECODER(test_nec);
RUN_ENCODER_DECODER(test_necext);
RUN_ENCODER_DECODER(test_nec42);
RUN_ENCODER_DECODER(test_nec42ext);
RUN_ENCODER_DECODER(test_samsung32);
RUN_ENCODER_DECODER(test_rc6);
RUN_ENCODER_DECODER(test_rc5);
RUN_ENCODER_DECODER(test_sirc);
}
MU_TEST_SUITE(test_infrared_decoder_encoder) {
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_encoder_sirc);
MU_RUN_TEST(test_decoder_sirc);
MU_RUN_TEST(test_encoder_rc5x);
MU_RUN_TEST(test_encoder_rc5);
MU_RUN_TEST(test_decoder_rc5);
MU_RUN_TEST(test_decoder_rc6);
MU_RUN_TEST(test_encoder_rc6);
MU_RUN_TEST(test_decoder_unexpected_end_in_sequence);
MU_RUN_TEST(test_decoder_long_packets_with_nec_start);
MU_RUN_TEST(test_decoder_nec);
MU_RUN_TEST(test_decoder_samsung32);
MU_RUN_TEST(test_decoder_necext1);
MU_RUN_TEST(test_mix);
MU_RUN_TEST(test_encoder_decoder_all);
}
int run_minunit_test_infrared_decoder_encoder() {
MU_RUN_SUITE(test_infrared_decoder_encoder);
return MU_EXIT_CODE;
}