unknown.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include "../app.h"
  2. /* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved
  3. * See the LICENSE file for information about the license.
  4. *
  5. * ----------------------------------------------------------------------------
  6. * The "unknown" decoder fires as the last one, once we are sure no other
  7. * decoder was able to identify the signal. The goal is to detect the
  8. * preamble and line code used in the received signal, then turn the
  9. * decoded bits into bytes.
  10. *
  11. * The techniques used for the detection are described in the comments
  12. * below.
  13. * ----------------------------------------------------------------------------
  14. */
  15. /* Scan the signal bitmap looking for a PWM modulation. In this case
  16. * for PWM we are referring to two exact patterns of high and low
  17. * signal (each bit in the bitmap is worth the smallest gap/pulse duration
  18. * we detected) that repeat each other in a given segment of the message.
  19. *
  20. * This modulation is quite common, for instance sometimes zero and
  21. * one are rappresented by a 700us pulse followed by 350 gap,
  22. * and 350us pulse followed by a 700us gap. So the signal bitmap received
  23. * by the decoder would contain 110 and 100 symbols.
  24. *
  25. * The way this function work is commented inline.
  26. *
  27. * The function returns the number of consecutive symbols found, having
  28. * a symbol length of 'symlen' (3 in the above example), and stores
  29. * in *s1i the offset of the first symbol found, and in *s2i the offset
  30. * of the second symbol. The function can't tell which is one and which
  31. * zero. */
  32. static uint32_t find_pwm(uint8_t *bits, uint32_t numbytes, uint32_t numbits,
  33. uint32_t symlen, uint32_t *s1i, uint32_t *s2i)
  34. {
  35. uint32_t best_count = 0; /* Max number of symbols found in this try. */
  36. uint32_t best_idx1 = 0; /* First symbol offset of longest sequence found.
  37. * This is also the start sequence offset. */
  38. uint32_t best_idx2 = 0; /* Second symbol offset. */
  39. /* Try all the possible symbol offsets that are less of our
  40. * symbol len. This is likely not really useful but we take
  41. * a conservative approach. Because if have have, for instance,
  42. * repeating symbols "100" and "110", they will form a sequence
  43. * that is choerent at different offsets, but out-of-sync.
  44. *
  45. * Anyway at the end of the function we try to fix the sync. */
  46. for (uint32_t off = 0; off < symlen; off++) {
  47. uint32_t c = 0; // Number of contiguous symbols found.
  48. uint32_t c1 = 0, c2 = 0; // Occurrences of first/second symbol.
  49. *s1i = off; // Assume we start at one symbol boundaty.
  50. *s2i = UINT32_MAX; // Second symbol first index still unknown.
  51. uint32_t next = off;
  52. /* We scan the whole bitmap in one pass, resetting the state
  53. * each time we find a pattern that is not one of the two
  54. * symbols we found so far. */
  55. while(next < numbits-symlen) {
  56. bool match1 = bitmap_match_bitmap(bits,numbytes,next,
  57. bits,numbytes,*s1i,
  58. symlen);
  59. if (!match1 && *s2i == UINT32_MAX) {
  60. /* It's not the first sybol. We don't know how the
  61. * second look like. Assume we found an occurrence of
  62. * the second symbol. */
  63. *s2i = next;
  64. }
  65. bool match2 = bitmap_match_bitmap(bits,numbytes,next,
  66. bits,numbytes,*s2i,
  67. symlen);
  68. /* One or the other should match. */
  69. if (match1 || match2) {
  70. c++;
  71. if (match1) c1++;
  72. if (match2) c2++;
  73. if (c > best_count &&
  74. c1 >= best_count/5 && // Require enough presence of both
  75. c2 >= best_count/5) // zero and one.
  76. {
  77. best_count = c;
  78. best_idx1 = *s1i;
  79. best_idx2 = *s2i;
  80. }
  81. next += symlen;
  82. } else {
  83. /* No match. Continue resetting the signal info. */
  84. c = 0; // Start again to count contiguous symbols.
  85. c1 = 0;
  86. c2 = 0;
  87. *s1i = next; // First symbol always at start.
  88. *s2i = UINT32_MAX; // Second symbol unknown.
  89. }
  90. }
  91. }
  92. /* We don't know if we are really synchronized with the bits at this point.
  93. * For example if zero bit is 100 and one bit is 110 in a specific
  94. * line code, our detector could randomly believe it's 001 and 101.
  95. * However PWD line codes normally start with a pulse in both symbols.
  96. * If that is the case, let's align. */
  97. uint32_t shift;
  98. for (shift = 0; shift < symlen; shift++) {
  99. if (bitmap_get(bits,numbytes,best_idx1+shift) &&
  100. bitmap_get(bits,numbytes,best_idx2+shift)) break;
  101. }
  102. if (shift != symlen) {
  103. best_idx1 += shift;
  104. best_idx2 += shift;
  105. }
  106. *s1i = best_idx1;
  107. *s2i = best_idx2;
  108. return best_count;
  109. }
  110. /* Find the longest sequence that looks like Manchester coding.
  111. *
  112. * Manchester coding requires each pairs of bits to be either
  113. * 01 or 10. We'll have to try odd and even offsets to be
  114. * sure to find it.
  115. *
  116. * Note that this will also detect differential Manchester, but
  117. * will report it as Manchester. I can't think of any way to
  118. * distinguish between the two line codes, because shifting them
  119. * one symbol will make one to look like the other.
  120. *
  121. * Only option could be to decode the message with both line
  122. * codes and use statistical properties (common byte values)
  123. * to determine what's more likely, but this looks very fragile.
  124. *
  125. * Fortunately differential Manchester is more rarely used,
  126. * so we can assume Manchester most of the times. Yet we are left
  127. * with the indetermination about zero being pulse-gap or gap-pulse
  128. * or the other way around.
  129. *
  130. * If the 'only_raising' parameter is true, the function detects
  131. * only sequences going from gap to pulse: this is useful in order
  132. * to locate preambles of alternating gaps and pulses. */
  133. static uint32_t find_alternating_bits(uint8_t *bits, uint32_t numbytes,
  134. uint32_t numbits, uint32_t *start, bool only_raising)
  135. {
  136. uint32_t best_count = 0; // Max number of symbols found
  137. uint32_t best_off = 0; // Max symbols start offset.
  138. for (int odd = 0; odd < 2; odd++) {
  139. uint32_t count = 0; // Symbols found so far
  140. uint32_t start_off = odd;
  141. uint32_t j = odd;
  142. while (j < numbits-1) {
  143. bool bit1 = bitmap_get(bits,numbytes,j);
  144. bool bit2 = bitmap_get(bits,numbytes,j+1);
  145. if ((!only_raising && bit1 != bit2) ||
  146. (only_raising && !bit1 && bit2))
  147. {
  148. count++;
  149. if (count > best_count) {
  150. best_count = count;
  151. best_off = start_off;
  152. }
  153. } else {
  154. /* End of sequence. Continue with the next
  155. * part of the signal. */
  156. count = 0;
  157. start_off = j + 2;
  158. }
  159. j += 2;
  160. }
  161. }
  162. *start = best_off;
  163. return best_count;
  164. }
  165. /* Wrapper to find Manchester code. */
  166. static uint32_t find_manchester(uint8_t *bits, uint32_t numbytes,
  167. uint32_t numbits, uint32_t *start)
  168. {
  169. return find_alternating_bits(bits,numbytes,numbits,start,false);
  170. }
  171. /* Wrapper to find preamble sections. */
  172. static uint32_t find_preamble(uint8_t *bits, uint32_t numbytes,
  173. uint32_t numbits, uint32_t *start)
  174. {
  175. return find_alternating_bits(bits,numbytes,numbits,start,true);
  176. }
  177. typedef enum {
  178. LineCodeNone,
  179. LineCodeManchester,
  180. LineCodePWM3,
  181. LineCodePWM4,
  182. } LineCodeGuess;
  183. static char *get_linecode_name(LineCodeGuess lc) {
  184. switch(lc) {
  185. case LineCodeNone: return "none";
  186. case LineCodeManchester: return "Manchester";
  187. case LineCodePWM3: return "PWM3";
  188. case LineCodePWM4: return "PWM4";
  189. }
  190. return "unknown";
  191. }
  192. static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
  193. /* No decoder was able to detect this message. Let's try if we can
  194. * find some structure. To start, we'll see if it looks like is
  195. * manchester coded, or PWM with symbol len of 3 or 4. */
  196. /* For PWM, start1 and start2 are the offsets at which the two
  197. * sequences composing the message appear the first time.
  198. * So start1 is also the message start offset. Start2 is not used
  199. * for Manchester, that does not have two separated symbols like
  200. * PWM. */
  201. uint32_t start1 = 0, start2 = 0;
  202. uint32_t msgbits; // Number of message bits in the bitmap, so
  203. // this will be the number of symbols, not actual
  204. // bits after the message is decoded.
  205. uint32_t tmp1, tmp2; // Temp vars to store the start.
  206. uint32_t minbits = 16; // Less than that gets undetected.
  207. uint32_t pwm_len; // Bits per symbol, in the case of PWM.
  208. LineCodeGuess linecode = LineCodeNone;
  209. // Try PWM3
  210. uint32_t pwm3_bits = find_pwm(bits,numbytes,numbits,3,&tmp1,&tmp2);
  211. if (pwm3_bits >= minbits) {
  212. linecode = LineCodePWM3;
  213. start1 = tmp1;
  214. start2 = tmp2;
  215. pwm_len = 3;
  216. msgbits = pwm3_bits*pwm_len;
  217. }
  218. // Try PWM4
  219. uint32_t pwm4_bits = find_pwm(bits,numbytes,numbits,4,&tmp1,&tmp2);
  220. if (pwm4_bits >= minbits && pwm4_bits > pwm3_bits) {
  221. linecode = LineCodePWM4;
  222. start1 = tmp1;
  223. start2 = tmp2;
  224. pwm_len = 4;
  225. msgbits = pwm3_bits*pwm_len;
  226. }
  227. // Try Manchester
  228. uint32_t manchester_bits = find_manchester(bits,numbytes,numbits,&tmp1);
  229. if (manchester_bits > minbits &&
  230. manchester_bits > pwm3_bits &&
  231. manchester_bits > pwm4_bits)
  232. {
  233. linecode = LineCodeManchester;
  234. start1 = tmp1;
  235. msgbits = manchester_bits*2;
  236. //FURI_LOG_T(TAG, "MANCHESTER START: %lu", tmp1);s
  237. }
  238. if (linecode == LineCodeNone) return false;
  239. /* Often there is a preamble before the signal. We'll try to find
  240. * it, and if it is not too far away from our signal, we'll claim
  241. * our signal starts at the preamble. */
  242. uint32_t preamble_len = find_preamble(bits,numbytes,numbits,&tmp1);
  243. uint32_t min_preamble_len = 10;
  244. uint32_t max_preamble_distance = 32;
  245. uint32_t preamble_start = 0;
  246. bool preamble_found = false;
  247. /* Note that because of the following checks, if the Manchester detector
  248. * detected the preamble bits as data, we are ok with that, since it
  249. * means that the synchronization is not designed to "break" the bits
  250. * flow. In this case we ignore the preamble and return all as data. */
  251. if (preamble_len >= min_preamble_len && // Not too short.
  252. tmp1 < start1 && // Should be before the data.
  253. start1-tmp1 <= max_preamble_distance) // Not too far.
  254. {
  255. preamble_start = tmp1;
  256. preamble_found = true;
  257. }
  258. info->start_off = preamble_found ? preamble_start : start1;
  259. info->pulses_count = (start1+msgbits) - info->start_off;
  260. info->pulses_count += 20; /* Add a few more, so that if the user resends
  261. * the message, it is more likely we will
  262. * transfer all that is needed, like a message
  263. * terminator (that we don't detect). */
  264. // if (preamble_found)
  265. // FURI_LOG_T(TAG, "PREAMBLE AT: %lu", preamble_start);
  266. // FURI_LOG_T(TAG, "START: %lu", info->start_off);
  267. // FURI_LOG_T(TAG, "MSGBITS: %lu", msgbits);
  268. // FURI_LOG_T(TAG, "DATASTART: %lu", start1);
  269. // FURI_LOG_T(TAG, "PULSES: %lu", info->pulses_count);
  270. /* We think there is a message and we know where it starts and the
  271. * line code used. We can turn it into bits and bytes. */
  272. uint32_t decoded;
  273. uint8_t data[32];
  274. uint32_t datalen;
  275. char symbol1[5], symbol2[5];
  276. if (linecode == LineCodePWM3 || linecode == LineCodePWM4) {
  277. bitmap_to_string(symbol1,bits,numbytes,start1,pwm_len);
  278. bitmap_to_string(symbol2,bits,numbytes,start2,pwm_len);
  279. } else if (linecode == LineCodeManchester) {
  280. memcpy(symbol1,"01",3);
  281. memcpy(symbol2,"10",3);
  282. }
  283. decoded = convert_from_line_code(data,sizeof(data),bits,numbytes,start1,
  284. symbol1,symbol2);
  285. datalen = (decoded+7)/8;
  286. char *linecode_name = get_linecode_name(linecode);
  287. fieldset_add_str(info->fieldset,"line code",
  288. linecode_name,strlen(linecode_name));
  289. fieldset_add_uint(info->fieldset,"data bits",decoded,8);
  290. if (preamble_found)
  291. fieldset_add_uint(info->fieldset,"preamble len",preamble_len,8);
  292. fieldset_add_str(info->fieldset,"first symbol",symbol1,strlen(symbol1));
  293. fieldset_add_str(info->fieldset,"second symbol",symbol2,strlen(symbol2));
  294. for (uint32_t j = 0; j < datalen; j++) {
  295. char label[16];
  296. snprintf(label,sizeof(label),"data[%lu]",j);
  297. fieldset_add_bytes(info->fieldset,label,data+j,2);
  298. }
  299. return true;
  300. }
  301. ProtoViewDecoder UnknownDecoder = {
  302. .name = "Unknown",
  303. .decode = decode,
  304. .get_fields = NULL,
  305. .build_message = NULL
  306. };