signal.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
  2. * See the LICENSE file for information about the license. */
  3. #include "app.h"
  4. /* =============================================================================
  5. * Raw signal detection
  6. * ===========================================================================*/
  7. /* Return the time difference between a and b, always >= 0 since
  8. * the absolute value is returned. */
  9. uint32_t duration_delta(uint32_t a, uint32_t b) {
  10. return a > b ? a - b : b - a;
  11. }
  12. /* This function starts scanning samples at offset idx looking for the
  13. * longest run of pulses, either high or low, that are among 10%
  14. * of each other, for a maximum of three classes. The classes are
  15. * counted separtely for high and low signals (RF on / off) because
  16. * many devices tend to have different pulse lenghts depending on
  17. * the level of the pulse.
  18. *
  19. * For instance Oregon2 sensors, in the case of protocol 2.1 will send
  20. * pulses of ~400us (RF on) VS ~580us (RF off). */
  21. #define SEARCH_CLASSES 3
  22. uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
  23. struct {
  24. uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
  25. uint32_t count[2]; /* Associated observed frequency. */
  26. } classes[SEARCH_CLASSES];
  27. memset(classes,0,sizeof(classes));
  28. uint32_t minlen = 40, maxlen = 4000; /* Depends on data rate, here we
  29. allow for high and low. */
  30. uint32_t len = 0; /* Observed len of coherent samples. */
  31. s->short_pulse_dur = 0;
  32. for (uint32_t j = idx; j < idx+500; j++) {
  33. bool level;
  34. uint32_t dur;
  35. raw_samples_get(s, j, &level, &dur);
  36. if (dur < minlen || dur > maxlen) break; /* return. */
  37. /* Let's see if it matches a class we already have or if we
  38. * can populate a new (yet empty) class. */
  39. uint32_t k;
  40. for (k = 0; k < SEARCH_CLASSES; k++) {
  41. if (classes[k].count[level] == 0) {
  42. classes[k].dur[level] = dur;
  43. classes[k].count[level] = 1;
  44. break; /* Sample accepted. */
  45. } else {
  46. uint32_t classavg = classes[k].dur[level];
  47. uint32_t count = classes[k].count[level];
  48. uint32_t delta = duration_delta(dur,classavg);
  49. if (delta < classavg/10) {
  50. /* It is useful to compute the average of the class
  51. * we are observing. We know how many samples we got so
  52. * far, so we can recompute the average easily.
  53. * By always having a better estimate of the pulse len
  54. * we can avoid missing next samples in case the first
  55. * observed samples are too off. */
  56. classavg = ((classavg * count) + dur) / (count+1);
  57. classes[k].dur[level] = classavg;
  58. classes[k].count[level]++;
  59. break; /* Sample accepted. */
  60. }
  61. }
  62. }
  63. if (k == SEARCH_CLASSES) break; /* No match, return. */
  64. /* If we are here, we accepted this sample. Try with the next
  65. * one. */
  66. len++;
  67. }
  68. /* Update the buffer setting the shortest pulse we found
  69. * among the three classes. This will be used when scaling
  70. * for visualization. */
  71. for (int j = 0; j < SEARCH_CLASSES; j++) {
  72. for (int level = 0; level < 2; level++) {
  73. if (classes[j].dur[level] == 0) continue;
  74. if (classes[j].count[level] < 3) continue;
  75. if (s->short_pulse_dur == 0 ||
  76. s->short_pulse_dur > classes[j].dur[level])
  77. {
  78. s->short_pulse_dur = classes[j].dur[level];
  79. }
  80. }
  81. }
  82. return len;
  83. }
  84. /* Search the buffer with the stored signal (last N samples received)
  85. * in order to find a coherent signal. If a signal that does not appear to
  86. * be just noise is found, it is set in DetectedSamples global signal
  87. * buffer, that is what is rendered on the screen. */
  88. void scan_for_signal(ProtoViewApp *app) {
  89. /* We need to work on a copy: the RawSamples buffer is populated
  90. * by the background thread receiving data. */
  91. RawSamplesBuffer *copy = raw_samples_alloc();
  92. raw_samples_copy(copy,RawSamples);
  93. /* Try to seek on data that looks to have a regular high low high low
  94. * pattern. */
  95. uint32_t minlen = 13; /* Min run of coherent samples. Up to
  96. 12 samples it's very easy to mistake
  97. noise for signal. */
  98. uint32_t i = 0;
  99. while (i < copy->total-1) {
  100. uint32_t thislen = search_coherent_signal(copy,i);
  101. if (thislen > minlen && thislen > app->signal_bestlen) {
  102. app->signal_bestlen = thislen;
  103. raw_samples_copy(DetectedSamples,copy);
  104. DetectedSamples->idx = (DetectedSamples->idx+i)%
  105. DetectedSamples->total;
  106. FURI_LOG_E(TAG, "Displayed sample updated (%d samples)",
  107. (int)thislen);
  108. }
  109. i += thislen ? thislen : 1;
  110. }
  111. raw_samples_free(copy);
  112. }
  113. /* =============================================================================
  114. * Decoding
  115. * ===========================================================================*/
  116. /* Set the 'bitpos' bit to value 'val', in the specified bitmap
  117. * 'b' of len 'blen'.
  118. * Out of range bits will silently be discarded. */
  119. void set_bit(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
  120. uint32_t byte = bitpos/8;
  121. uint32_t bit = bitpos&7;
  122. if (byte >= blen) return;
  123. if (val)
  124. b[byte] |= 1<<bit;
  125. else
  126. b[byte] &= ~(1<<bit);
  127. }
  128. /* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
  129. * Out of range bits return false (not bit set). */
  130. bool get_bit(uint8_t *b, uint32_t blen, uint32_t bitpos) {
  131. uint32_t byte = bitpos/8;
  132. uint32_t bit = bitpos&7;
  133. if (byte >= blen) return 0;
  134. return (b[byte] & (1<<bit)) != 0;
  135. }
  136. /* Take the raw signal and turn it into a sequence of bits inside the
  137. * buffer 'b'. Note that such 0s and 1s are NOT the actual data in the
  138. * signal, but is just a low level representation of the line code. Basically
  139. * if the short pulse we find in the signal is 320us, we convert high and
  140. * low levels in the raw sample in this way:
  141. *
  142. * If for instance we see a high level lasting ~600 us, we will add
  143. * two 1s bit. If then the signal goes down for 330us, we will add one zero,
  144. * and so forth. So for each period of high and low we find the closest
  145. * multiple and set the relevant number of bits.
  146. *
  147. * In case of a short pulse of 320us detected, 320*2 is the closest to a
  148. * high pulse of 600us, so 2 bits will be set.
  149. *
  150. * In other terms what this function does is sampling the signal at
  151. * fixed 'rate' intervals.
  152. *
  153. * This representation makes it simple to decode the signal at a higher
  154. * level later, translating it from Marshal coding or other line codes
  155. * to the actual bits/bytes.
  156. *
  157. * The 'idx' argument marks the detected signal start index into the
  158. * raw samples buffer. The 'count' tells the function how many raw
  159. * samples to convert into bits. The function returns the number of
  160. * bits set into the buffer 'b'. The 'rate' argument, in microseconds, is
  161. * the detected short-pulse duration. We expect the line code to be
  162. * meaningful when interpreted at multiples of 'rate'. */
  163. uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) {
  164. if (rate == 0) return 0; /* We can't perform the conversion. */
  165. uint32_t bitpos = 0;
  166. for (uint32_t j = 0; j < count; j++) {
  167. uint32_t dur;
  168. bool level;
  169. raw_samples_get(s, j+idx, &level, &dur);
  170. uint32_t numbits = dur / rate; /* full bits that surely fit. */
  171. uint32_t rest = dur % rate; /* How much we are left with. */
  172. if (rest > rate/2) numbits++; /* There is another one. */
  173. while(numbits--) set_bit(b,blen,bitpos++,s[j].level);
  174. }
  175. return bitpos;
  176. }