signal.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. /* Return the time difference between a and b, always >= 0 since
  5. * the absolute value is returned. */
  6. uint32_t duration_delta(uint32_t a, uint32_t b) {
  7. return a > b ? a - b : b - a;
  8. }
  9. /* This function starts scanning samples at offset idx looking for the
  10. * longest run of pulses, either high or low, that are among 10%
  11. * of each other, for a maximum of three classes. The classes are
  12. * counted separtely for high and low signals (RF on / off) because
  13. * many devices tend to have different pulse lenghts depending on
  14. * the level of the pulse.
  15. *
  16. * For instance Oregon2 sensors, in the case of protocol 2.1 will send
  17. * pulses of ~400us (RF on) VS ~580us (RF off). */
  18. #define SEARCH_CLASSES 3
  19. uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) {
  20. struct {
  21. uint32_t dur[2]; /* dur[0] = low, dur[1] = high */
  22. uint32_t count[2]; /* Associated observed frequency. */
  23. } classes[SEARCH_CLASSES];
  24. memset(classes,0,sizeof(classes));
  25. uint32_t minlen = 40, maxlen = 4000; /* Depends on data rate, here we
  26. allow for high and low. */
  27. uint32_t len = 0; /* Observed len of coherent samples. */
  28. s->short_pulse_dur = 0;
  29. for (uint32_t j = idx; j < idx+500; j++) {
  30. bool level;
  31. uint32_t dur;
  32. raw_samples_get(s, j, &level, &dur);
  33. if (dur < minlen || dur > maxlen) break; /* return. */
  34. /* Let's see if it matches a class we already have or if we
  35. * can populate a new (yet empty) class. */
  36. uint32_t k;
  37. for (k = 0; k < SEARCH_CLASSES; k++) {
  38. if (classes[k].count[level] == 0) {
  39. classes[k].dur[level] = dur;
  40. classes[k].count[level] = 1;
  41. break; /* Sample accepted. */
  42. } else {
  43. uint32_t classavg = classes[k].dur[level];
  44. uint32_t count = classes[k].count[level];
  45. uint32_t delta = duration_delta(dur,classavg);
  46. if (delta < classavg/10) {
  47. /* It is useful to compute the average of the class
  48. * we are observing. We know how many samples we got so
  49. * far, so we can recompute the average easily.
  50. * By always having a better estimate of the pulse len
  51. * we can avoid missing next samples in case the first
  52. * observed samples are too off. */
  53. classavg = ((classavg * count) + dur) / (count+1);
  54. classes[k].dur[level] = classavg;
  55. classes[k].count[level]++;
  56. break; /* Sample accepted. */
  57. }
  58. }
  59. }
  60. if (k == SEARCH_CLASSES) break; /* No match, return. */
  61. /* If we are here, we accepted this sample. Try with the next
  62. * one. */
  63. len++;
  64. }
  65. /* Update the buffer setting the shortest pulse we found
  66. * among the three classes. This will be used when scaling
  67. * for visualization. */
  68. for (int j = 0; j < SEARCH_CLASSES; j++) {
  69. for (int level = 0; level < 2; level++) {
  70. if (classes[j].dur[level] == 0) continue;
  71. if (classes[j].count[level] < 3) continue;
  72. if (s->short_pulse_dur == 0 ||
  73. s->short_pulse_dur > classes[j].dur[level])
  74. {
  75. s->short_pulse_dur = classes[j].dur[level];
  76. }
  77. }
  78. }
  79. return len;
  80. }
  81. /* Search the buffer with the stored signal (last N samples received)
  82. * in order to find a coherent signal. If a signal that does not appear to
  83. * be just noise is found, it is set in DetectedSamples global signal
  84. * buffer, that is what is rendered on the screen. */
  85. void scan_for_signal(ProtoViewApp *app) {
  86. /* We need to work on a copy: the RawSamples buffer is populated
  87. * by the background thread receiving data. */
  88. RawSamplesBuffer *copy = raw_samples_alloc();
  89. raw_samples_copy(copy,RawSamples);
  90. /* Try to seek on data that looks to have a regular high low high low
  91. * pattern. */
  92. uint32_t minlen = 13; /* Min run of coherent samples. Up to
  93. 12 samples it's very easy to mistake
  94. noise for signal. */
  95. uint32_t i = 0;
  96. while (i < copy->total-1) {
  97. uint32_t thislen = search_coherent_signal(copy,i);
  98. if (thislen > minlen && thislen > app->signal_bestlen) {
  99. app->signal_bestlen = thislen;
  100. raw_samples_copy(DetectedSamples,copy);
  101. DetectedSamples->idx = (DetectedSamples->idx+i)%
  102. DetectedSamples->total;
  103. FURI_LOG_E(TAG, "Displayed sample updated (%d samples)",
  104. (int)thislen);
  105. }
  106. i += thislen ? thislen : 1;
  107. }
  108. raw_samples_free(copy);
  109. }