infrared_test.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. #include <furi.h>
  2. #include <flipper_format.h>
  3. #include <infrared.h>
  4. #include <common/infrared_common_i.h>
  5. #include "../minunit.h"
  6. #define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/")
  7. #define IR_TEST_FILE_PREFIX "test_"
  8. #define IR_TEST_FILE_SUFFIX ".irtest"
  9. typedef struct {
  10. InfraredDecoderHandler* decoder_handler;
  11. InfraredEncoderHandler* encoder_handler;
  12. FuriString* file_path;
  13. FlipperFormat* ff;
  14. } InfraredTest;
  15. static InfraredTest* test;
  16. static void infrared_test_alloc() {
  17. Storage* storage = furi_record_open(RECORD_STORAGE);
  18. test = malloc(sizeof(InfraredTest));
  19. test->decoder_handler = infrared_alloc_decoder();
  20. test->encoder_handler = infrared_alloc_encoder();
  21. test->ff = flipper_format_buffered_file_alloc(storage);
  22. test->file_path = furi_string_alloc();
  23. }
  24. static void infrared_test_free() {
  25. furi_assert(test);
  26. infrared_free_decoder(test->decoder_handler);
  27. infrared_free_encoder(test->encoder_handler);
  28. flipper_format_free(test->ff);
  29. furi_string_free(test->file_path);
  30. furi_record_close(RECORD_STORAGE);
  31. free(test);
  32. test = NULL;
  33. }
  34. static bool infrared_test_prepare_file(const char* protocol_name) {
  35. FuriString* file_type;
  36. file_type = furi_string_alloc();
  37. bool success = false;
  38. furi_string_printf(
  39. test->file_path,
  40. "%s%s%s%s",
  41. IR_TEST_FILES_DIR,
  42. IR_TEST_FILE_PREFIX,
  43. protocol_name,
  44. IR_TEST_FILE_SUFFIX);
  45. do {
  46. uint32_t format_version;
  47. if(!flipper_format_buffered_file_open_existing(
  48. test->ff, furi_string_get_cstr(test->file_path)))
  49. break;
  50. if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;
  51. if(furi_string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
  52. success = true;
  53. } while(false);
  54. furi_string_free(file_type);
  55. return success;
  56. }
  57. static bool infrared_test_load_raw_signal(
  58. FlipperFormat* ff,
  59. const char* signal_name,
  60. uint32_t** timings,
  61. uint32_t* timings_count) {
  62. FuriString* buf;
  63. buf = furi_string_alloc();
  64. bool success = false;
  65. do {
  66. bool is_name_found = false;
  67. for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
  68. is_name_found = !furi_string_cmp(buf, signal_name))
  69. ;
  70. if(!is_name_found) break;
  71. if(!flipper_format_read_string(ff, "type", buf) || furi_string_cmp_str(buf, "raw")) break;
  72. if(!flipper_format_get_value_count(ff, "data", timings_count)) break;
  73. if(!*timings_count) break;
  74. *timings = malloc(*timings_count * sizeof(uint32_t*));
  75. if(!flipper_format_read_uint32(ff, "data", *timings, *timings_count)) {
  76. free(*timings);
  77. break;
  78. }
  79. success = true;
  80. } while(false);
  81. furi_string_free(buf);
  82. return success;
  83. }
  84. static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) {
  85. FuriString* buf;
  86. buf = furi_string_alloc();
  87. bool success = false;
  88. do {
  89. if(!flipper_format_read_string(ff, "protocol", buf)) break;
  90. message->protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));
  91. if(!infrared_is_protocol_valid(message->protocol)) break;
  92. if(!flipper_format_read_hex(ff, "address", (uint8_t*)&message->address, sizeof(uint32_t)))
  93. break;
  94. if(!flipper_format_read_hex(ff, "command", (uint8_t*)&message->command, sizeof(uint32_t)))
  95. break;
  96. if(!flipper_format_read_bool(ff, "repeat", &message->repeat, 1)) break;
  97. success = true;
  98. } while(false);
  99. furi_string_free(buf);
  100. return success;
  101. }
  102. static bool infrared_test_load_messages(
  103. FlipperFormat* ff,
  104. const char* signal_name,
  105. InfraredMessage** messages,
  106. uint32_t* messages_count) {
  107. FuriString* buf;
  108. buf = furi_string_alloc();
  109. bool success = false;
  110. do {
  111. bool is_name_found = false;
  112. for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
  113. is_name_found = !furi_string_cmp(buf, signal_name))
  114. ;
  115. if(!is_name_found) break;
  116. if(!flipper_format_read_string(ff, "type", buf) ||
  117. furi_string_cmp_str(buf, "parsed_array"))
  118. break;
  119. if(!flipper_format_read_uint32(ff, "count", messages_count, 1)) break;
  120. if(!*messages_count) break;
  121. *messages = malloc(*messages_count * sizeof(InfraredMessage));
  122. uint32_t i;
  123. for(i = 0; i < *messages_count; ++i) {
  124. if(!infrared_test_read_message(ff, (*messages) + i)) {
  125. break;
  126. }
  127. }
  128. if(*messages_count != i) {
  129. free(*messages);
  130. break;
  131. }
  132. success = true;
  133. } while(false);
  134. furi_string_free(buf);
  135. return success;
  136. }
  137. static void infrared_test_compare_message_results(
  138. const InfraredMessage* message_decoded,
  139. const InfraredMessage* message_expected) {
  140. mu_check(message_decoded->protocol == message_expected->protocol);
  141. mu_check(message_decoded->command == message_expected->command);
  142. mu_check(message_decoded->address == message_expected->address);
  143. if((message_expected->protocol == InfraredProtocolSIRC) ||
  144. (message_expected->protocol == InfraredProtocolSIRC15) ||
  145. (message_expected->protocol == InfraredProtocolSIRC20)) {
  146. mu_check(message_decoded->repeat == false);
  147. } else {
  148. mu_check(message_decoded->repeat == message_expected->repeat);
  149. }
  150. }
  151. /* Encodes signal and merges same levels (high+high, low+low) */
  152. static void infrared_test_run_encoder_fill_array(
  153. InfraredEncoderHandler* handler,
  154. uint32_t* timings,
  155. uint32_t* timings_len,
  156. bool* start_level) {
  157. uint32_t duration = 0;
  158. bool level = false;
  159. bool level_read;
  160. InfraredStatus status = InfraredStatusError;
  161. size_t i = 0;
  162. bool first = true;
  163. while(1) {
  164. status = infrared_encode(handler, &duration, &level_read);
  165. if(first) {
  166. if(start_level) *start_level = level_read;
  167. first = false;
  168. timings[0] = 0;
  169. } else if(level_read != level) {
  170. ++i;
  171. furi_check(i < *timings_len);
  172. timings[i] = 0;
  173. }
  174. level = level_read;
  175. timings[i] += duration;
  176. furi_check((status == InfraredStatusOk) || (status == InfraredStatusDone));
  177. if(status == InfraredStatusDone) break;
  178. }
  179. *timings_len = i + 1;
  180. }
  181. // messages in input array for encoder should have one protocol
  182. static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_index) {
  183. uint32_t* timings;
  184. uint32_t timings_count = 200;
  185. uint32_t* expected_timings;
  186. uint32_t expected_timings_count;
  187. InfraredMessage* input_messages;
  188. uint32_t input_messages_count;
  189. FuriString* buf;
  190. buf = furi_string_alloc();
  191. const char* protocol_name = infrared_get_protocol_name(protocol);
  192. mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
  193. furi_string_printf(buf, "encoder_input%ld", test_index);
  194. mu_assert(
  195. infrared_test_load_messages(
  196. test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),
  197. "Failed to load messages from file");
  198. furi_string_printf(buf, "encoder_expected%ld", test_index);
  199. mu_assert(
  200. infrared_test_load_raw_signal(
  201. test->ff, furi_string_get_cstr(buf), &expected_timings, &expected_timings_count),
  202. "Failed to load raw signal from file");
  203. flipper_format_buffered_file_close(test->ff);
  204. furi_string_free(buf);
  205. uint32_t j = 0;
  206. timings = malloc(sizeof(uint32_t) * timings_count);
  207. for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
  208. const InfraredMessage* message = &input_messages[message_counter];
  209. if(!message->repeat) {
  210. infrared_reset_encoder(test->encoder_handler, message);
  211. }
  212. timings_count = 200;
  213. infrared_test_run_encoder_fill_array(test->encoder_handler, timings, &timings_count, NULL);
  214. furi_check(timings_count <= 200);
  215. for(size_t i = 0; i < timings_count; ++i, ++j) {
  216. mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
  217. mu_assert(j < expected_timings_count, "encoded more timings than expected");
  218. }
  219. }
  220. free(input_messages);
  221. free(expected_timings);
  222. free(timings);
  223. mu_assert(j == expected_timings_count, "encoded less timings than expected");
  224. }
  225. static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_t test_index) {
  226. uint32_t* timings = 0;
  227. uint32_t timings_count = 200;
  228. InfraredMessage* input_messages;
  229. uint32_t input_messages_count;
  230. bool level = false;
  231. FuriString* buf;
  232. buf = furi_string_alloc();
  233. timings = malloc(sizeof(uint32_t) * timings_count);
  234. const char* protocol_name = infrared_get_protocol_name(protocol);
  235. mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
  236. furi_string_printf(buf, "encoder_decoder_input%ld", test_index);
  237. mu_assert(
  238. infrared_test_load_messages(
  239. test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count),
  240. "Failed to load messages from file");
  241. flipper_format_buffered_file_close(test->ff);
  242. furi_string_free(buf);
  243. for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
  244. const InfraredMessage* message_encoded = &input_messages[message_counter];
  245. if(!message_encoded->repeat) {
  246. infrared_reset_encoder(test->encoder_handler, message_encoded);
  247. }
  248. timings_count = 200;
  249. infrared_test_run_encoder_fill_array(
  250. test->encoder_handler, timings, &timings_count, &level);
  251. furi_check(timings_count <= 200);
  252. const InfraredMessage* message_decoded = 0;
  253. for(size_t i = 0; i < timings_count; ++i) {
  254. message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
  255. if((i == timings_count - 2) && level && message_decoded) {
  256. /* In case we end with space timing - message can be decoded at last mark */
  257. break;
  258. } else if(i < timings_count - 1) {
  259. mu_check(!message_decoded);
  260. } else {
  261. if(!message_decoded) {
  262. message_decoded = infrared_check_decoder_ready(test->decoder_handler);
  263. }
  264. mu_check(message_decoded);
  265. }
  266. level = !level;
  267. }
  268. if(message_decoded) {
  269. infrared_test_compare_message_results(message_decoded, message_encoded);
  270. } else {
  271. mu_check(0);
  272. }
  273. }
  274. free(input_messages);
  275. free(timings);
  276. }
  277. static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_index) {
  278. uint32_t* timings;
  279. uint32_t timings_count;
  280. InfraredMessage* messages;
  281. uint32_t messages_count;
  282. FuriString* buf;
  283. buf = furi_string_alloc();
  284. mu_assert(
  285. infrared_test_prepare_file(infrared_get_protocol_name(protocol)),
  286. "Failed to prepare test file");
  287. furi_string_printf(buf, "decoder_input%ld", test_index);
  288. mu_assert(
  289. infrared_test_load_raw_signal(
  290. test->ff, furi_string_get_cstr(buf), &timings, &timings_count),
  291. "Failed to load raw signal from file");
  292. furi_string_printf(buf, "decoder_expected%ld", test_index);
  293. mu_assert(
  294. infrared_test_load_messages(
  295. test->ff, furi_string_get_cstr(buf), &messages, &messages_count),
  296. "Failed to load messages from file");
  297. flipper_format_buffered_file_close(test->ff);
  298. furi_string_free(buf);
  299. InfraredMessage message_decoded_check_local;
  300. bool level = 0;
  301. uint32_t message_counter = 0;
  302. const InfraredMessage* message_decoded = 0;
  303. for(uint32_t i = 0; i < timings_count; ++i) {
  304. const InfraredMessage* message_decoded_check = 0;
  305. if(timings[i] > INFRARED_RAW_RX_TIMING_DELAY_US) {
  306. message_decoded_check = infrared_check_decoder_ready(test->decoder_handler);
  307. if(message_decoded_check) {
  308. /* infrared_decode() can reset message, but we have to call infrared_decode() to perform real
  309. * simulation: infrared_check() by timeout, then infrared_decode() when meet edge */
  310. message_decoded_check_local = *message_decoded_check;
  311. message_decoded_check = &message_decoded_check_local;
  312. }
  313. }
  314. message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
  315. if(message_decoded_check || message_decoded) {
  316. mu_assert(
  317. !(message_decoded_check && message_decoded),
  318. "both messages decoded: check_ready() and infrared_decode()");
  319. if(message_decoded_check) {
  320. message_decoded = message_decoded_check;
  321. }
  322. mu_assert(message_counter < messages_count, "decoded more than expected");
  323. infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
  324. ++message_counter;
  325. }
  326. level = !level;
  327. }
  328. message_decoded = infrared_check_decoder_ready(test->decoder_handler);
  329. if(message_decoded) {
  330. infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
  331. ++message_counter;
  332. }
  333. free(timings);
  334. free(messages);
  335. mu_assert(message_counter == messages_count, "decoded less than expected");
  336. }
  337. MU_TEST(infrared_test_decoder_samsung32) {
  338. infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
  339. }
  340. MU_TEST(infrared_test_decoder_mixed) {
  341. infrared_test_run_decoder(InfraredProtocolRC5, 2);
  342. infrared_test_run_decoder(InfraredProtocolSIRC, 1);
  343. infrared_test_run_decoder(InfraredProtocolNECext, 1);
  344. infrared_test_run_decoder(InfraredProtocolRC6, 2);
  345. infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
  346. infrared_test_run_decoder(InfraredProtocolRC6, 1);
  347. infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
  348. infrared_test_run_decoder(InfraredProtocolRC5, 1);
  349. infrared_test_run_decoder(InfraredProtocolSIRC, 2);
  350. infrared_test_run_decoder(InfraredProtocolNECext, 1);
  351. infrared_test_run_decoder(InfraredProtocolSIRC, 4);
  352. infrared_test_run_decoder(InfraredProtocolNEC, 2);
  353. infrared_test_run_decoder(InfraredProtocolRC6, 1);
  354. infrared_test_run_decoder(InfraredProtocolNECext, 1);
  355. infrared_test_run_decoder(InfraredProtocolSIRC, 5);
  356. infrared_test_run_decoder(InfraredProtocolNEC, 3);
  357. infrared_test_run_decoder(InfraredProtocolRC5, 5);
  358. infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
  359. infrared_test_run_decoder(InfraredProtocolSIRC, 3);
  360. infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
  361. }
  362. MU_TEST(infrared_test_decoder_nec) {
  363. infrared_test_run_decoder(InfraredProtocolNEC, 1);
  364. infrared_test_run_decoder(InfraredProtocolNEC, 2);
  365. infrared_test_run_decoder(InfraredProtocolNEC, 3);
  366. }
  367. MU_TEST(infrared_test_decoder_unexpected_end_in_sequence) {
  368. infrared_test_run_decoder(InfraredProtocolNEC, 1);
  369. infrared_test_run_decoder(InfraredProtocolNEC, 1);
  370. infrared_test_run_decoder(InfraredProtocolNEC, 2);
  371. infrared_test_run_decoder(InfraredProtocolNEC, 2);
  372. }
  373. MU_TEST(infrared_test_decoder_necext1) {
  374. infrared_test_run_decoder(InfraredProtocolNECext, 1);
  375. infrared_test_run_decoder(InfraredProtocolNECext, 1);
  376. }
  377. MU_TEST(infrared_test_decoder_long_packets_with_nec_start) {
  378. infrared_test_run_decoder(InfraredProtocolNEC42ext, 1);
  379. infrared_test_run_decoder(InfraredProtocolNEC42ext, 2);
  380. }
  381. MU_TEST(infrared_test_encoder_sirc) {
  382. infrared_test_run_encoder(InfraredProtocolSIRC, 1);
  383. infrared_test_run_encoder(InfraredProtocolSIRC, 2);
  384. }
  385. MU_TEST(infrared_test_decoder_sirc) {
  386. infrared_test_run_decoder(InfraredProtocolSIRC, 3);
  387. infrared_test_run_decoder(InfraredProtocolSIRC, 1);
  388. infrared_test_run_decoder(InfraredProtocolSIRC, 2);
  389. infrared_test_run_decoder(InfraredProtocolSIRC, 4);
  390. infrared_test_run_decoder(InfraredProtocolSIRC, 5);
  391. }
  392. MU_TEST(infrared_test_decoder_rc5) {
  393. infrared_test_run_decoder(InfraredProtocolRC5X, 1);
  394. infrared_test_run_decoder(InfraredProtocolRC5, 1);
  395. infrared_test_run_decoder(InfraredProtocolRC5, 2);
  396. infrared_test_run_decoder(InfraredProtocolRC5, 3);
  397. infrared_test_run_decoder(InfraredProtocolRC5, 4);
  398. infrared_test_run_decoder(InfraredProtocolRC5, 5);
  399. infrared_test_run_decoder(InfraredProtocolRC5, 6);
  400. infrared_test_run_decoder(InfraredProtocolRC5, 7);
  401. }
  402. MU_TEST(infrared_test_encoder_rc5x) {
  403. infrared_test_run_encoder(InfraredProtocolRC5X, 1);
  404. }
  405. MU_TEST(infrared_test_encoder_rc5) {
  406. infrared_test_run_encoder(InfraredProtocolRC5, 1);
  407. }
  408. MU_TEST(infrared_test_decoder_rc6) {
  409. infrared_test_run_decoder(InfraredProtocolRC6, 1);
  410. }
  411. MU_TEST(infrared_test_encoder_rc6) {
  412. infrared_test_run_encoder(InfraredProtocolRC6, 1);
  413. }
  414. MU_TEST(infrared_test_decoder_kaseikyo) {
  415. infrared_test_run_decoder(InfraredProtocolKaseikyo, 1);
  416. infrared_test_run_decoder(InfraredProtocolKaseikyo, 2);
  417. infrared_test_run_decoder(InfraredProtocolKaseikyo, 3);
  418. infrared_test_run_decoder(InfraredProtocolKaseikyo, 4);
  419. infrared_test_run_decoder(InfraredProtocolKaseikyo, 5);
  420. infrared_test_run_decoder(InfraredProtocolKaseikyo, 6);
  421. }
  422. MU_TEST(infrared_test_encoder_decoder_all) {
  423. infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
  424. infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
  425. infrared_test_run_encoder_decoder(InfraredProtocolNEC42, 1);
  426. infrared_test_run_encoder_decoder(InfraredProtocolNEC42ext, 1);
  427. infrared_test_run_encoder_decoder(InfraredProtocolSamsung32, 1);
  428. infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
  429. infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
  430. infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
  431. infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1);
  432. }
  433. MU_TEST_SUITE(infrared_test) {
  434. MU_SUITE_CONFIGURE(&infrared_test_alloc, &infrared_test_free);
  435. MU_RUN_TEST(infrared_test_encoder_sirc);
  436. MU_RUN_TEST(infrared_test_decoder_sirc);
  437. MU_RUN_TEST(infrared_test_encoder_rc5x);
  438. MU_RUN_TEST(infrared_test_encoder_rc5);
  439. MU_RUN_TEST(infrared_test_decoder_rc5);
  440. MU_RUN_TEST(infrared_test_decoder_rc6);
  441. MU_RUN_TEST(infrared_test_encoder_rc6);
  442. MU_RUN_TEST(infrared_test_decoder_unexpected_end_in_sequence);
  443. MU_RUN_TEST(infrared_test_decoder_long_packets_with_nec_start);
  444. MU_RUN_TEST(infrared_test_decoder_nec);
  445. MU_RUN_TEST(infrared_test_decoder_samsung32);
  446. MU_RUN_TEST(infrared_test_decoder_necext1);
  447. MU_RUN_TEST(infrared_test_decoder_kaseikyo);
  448. MU_RUN_TEST(infrared_test_decoder_mixed);
  449. MU_RUN_TEST(infrared_test_encoder_decoder_all);
  450. }
  451. int run_minunit_test_infrared() {
  452. MU_RUN_SUITE(infrared_test);
  453. return MU_EXIT_CODE;
  454. }