Просмотр исходного кода

Protocol decoding, work in progress.

antirez 3 лет назад
Родитель
Сommit
630290f36a
6 измененных файлов с 173 добавлено и 11 удалено
  1. 7 7
      app.c
  2. 27 0
      app.h
  3. 15 0
      protocols/oregon2.c
  4. 6 0
      protocols/oregon2.txt
  5. 114 3
      signal.c
  6. 4 1
      view_raw_signal.c

+ 7 - 7
app.c

@@ -38,9 +38,7 @@ static void render_callback(Canvas *const canvas, void *ctx) {
 static void input_callback(InputEvent* input_event, void* ctx)
 static void input_callback(InputEvent* input_event, void* ctx)
 {
 {
     ProtoViewApp *app = ctx;
     ProtoViewApp *app = ctx;
-
     furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
     furi_message_queue_put(app->event_queue,input_event,FuriWaitForever);
-    FURI_LOG_E(TAG, "INPUT CALLBACK %d", (int)input_event->key);
 }
 }
 
 
 /* Allocate the application state and initialize a number of stuff.
 /* Allocate the application state and initialize a number of stuff.
@@ -67,7 +65,7 @@ ProtoViewApp* protoview_app_alloc() {
 
 
     // Signal found and visualization defaults
     // Signal found and visualization defaults
     app->signal_bestlen = 0;
     app->signal_bestlen = 0;
-    app->us_scale = 100;
+    app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
     app->signal_offset = 0;
     app->signal_offset = 0;
 
 
     //init Worker & Protocol
     //init Worker & Protocol
@@ -159,8 +157,8 @@ int32_t protoview_app_entry(void* p) {
     while(app->running) {
     while(app->running) {
         FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
         FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100);
         if (qstat == FuriStatusOk) {
         if (qstat == FuriStatusOk) {
-            FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
-                input.type, input.key);
+            if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u",
+                    input.type, input.key);
 
 
             /* Handle navigation here. Then handle view-specific inputs
             /* Handle navigation here. Then handle view-specific inputs
              * in the view specific handling function. */
              * in the view specific handling function. */
@@ -200,8 +198,10 @@ int32_t protoview_app_entry(void* p) {
         } else {
         } else {
             /* Useful to understand if the app is still alive when it
             /* Useful to understand if the app is still alive when it
              * does not respond because of bugs. */
              * does not respond because of bugs. */
-            static int c = 0; c++;
-            if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
+            if (DEBUG_MSG) {
+                static int c = 0; c++;
+                if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout");
+            }
         }
         }
         view_port_update(app->view_port);
         view_port_update(app->view_port);
     }
     }

+ 27 - 0
app.h

@@ -23,6 +23,10 @@
 #include "app_buffer.h"
 #include "app_buffer.h"
 
 
 #define TAG "ProtoView"
 #define TAG "ProtoView"
+#define PROTOVIEW_RAW_VIEW_DEFAULT_SCALE 100
+#define BITMAP_SEEK_NOT_FOUND UINT32_MAX
+
+#define DEBUG_MSG 1
 
 
 typedef struct ProtoViewApp ProtoViewApp;
 typedef struct ProtoViewApp ProtoViewApp;
 
 
@@ -86,6 +90,26 @@ struct ProtoViewApp {
                                 ProtoViewModulations table. */
                                 ProtoViewModulations table. */
 };
 };
 
 
+/* This stucture is filled by the decoder for specific protocols with the
+ * informations about the message. ProtoView will display such information
+ * in the message info view. */
+#define PROTOVIEW_MSG_STR_LEN 16
+typedef struct ProtoViewMsgInfo {
+    char name[PROTOVIEW_MSG_STR_LEN]; /* Protocol name and version. */
+    char raw[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific raw representation.*/
+    /* The following is what the decoder wants to show to user. Each decoder
+     * can use the number of fileds it needs. */
+    char info1[16];     /* Protocol specific decoded string, line 1. */
+    char info2[16];     /* Protocol specific decoded string, line 2. */
+    char info3[16];     /* Protocol specific decoded string, line 3. */
+    uint64_t len;       /* Bits found. */
+} ProtoViewMsgInfo;
+
+typedef struct ProtoViewDecoder {
+    const char *name;   /* Protocol name. */
+    bool (*decode)(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info);
+} ProtoViewDecoder;
+
 extern RawSamplesBuffer *RawSamples, *DetectedSamples;
 extern RawSamplesBuffer *RawSamples, *DetectedSamples;
 
 
 /* app_radio.c */
 /* app_radio.c */
@@ -98,6 +122,9 @@ void radio_sleep(ProtoViewApp* app);
 /* signal.c */
 /* signal.c */
 uint32_t duration_delta(uint32_t a, uint32_t b);
 uint32_t duration_delta(uint32_t a, uint32_t b);
 void scan_for_signal(ProtoViewApp *app);
 void scan_for_signal(ProtoViewApp *app);
+bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos);
+bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits);
+uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, const char *bits);
 
 
 /* view_*.c */
 /* view_*.c */
 void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app);
 void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app);

+ 15 - 0
protocols/oregon2.c

@@ -0,0 +1,15 @@
+#include "../app.h"
+
+static bool decode(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info) {
+    const char *sync_pattern = "01100110" "01100110" "10010110" "10010110";
+    uint64_t off = bitmap_seek_bits(bits,numbits,0,sync_pattern);
+    if (off == BITMAP_SEEK_NOT_FOUND) return false;
+
+    FURI_LOG_E(TAG, "Oregon2 prelude+sync found");
+    snprintf(info->name,sizeof(info->name),"%s","Oregon v2.1");
+    return true;
+}
+
+ProtoViewDecoder Oregon2Decoder = {
+    "Oregon2", decode
+};

+ 6 - 0
protocols/oregon2.txt

@@ -0,0 +1,6 @@
+11001100110011001100110011001100110011001100110011001100110 (Preamble)
+10 01 01 10 10 01 01 10 (Sync)
+01 10 10 01 10 01 10 01 01 10 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 10 01 01 10 01 10 01 10 01 10 01 10 01 10 10 01 01 10 01 10 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 11 0
+
+We need to seek the following bytes: 01100110 01100110 10010110 10010110
+                                        0x66     0x66     96       96

+ 114 - 3
signal.c

@@ -3,6 +3,8 @@
 
 
 #include "app.h"
 #include "app.h"
 
 
+void decode_signal(RawSamplesBuffer *s, uint64_t len);
+
 /* =============================================================================
 /* =============================================================================
  * Raw signal detection
  * Raw signal detection
  * ===========================================================================*/
  * ===========================================================================*/
@@ -117,6 +119,7 @@ void scan_for_signal(ProtoViewApp *app) {
                                    DetectedSamples->total;
                                    DetectedSamples->total;
             FURI_LOG_E(TAG, "Displayed sample updated (%d samples)",
             FURI_LOG_E(TAG, "Displayed sample updated (%d samples)",
                 (int)thislen);
                 (int)thislen);
+            decode_signal(DetectedSamples,thislen);
         }
         }
         i += thislen ? thislen : 1;
         i += thislen ? thislen : 1;
     }
     }
@@ -125,12 +128,21 @@ void scan_for_signal(ProtoViewApp *app) {
 
 
 /* =============================================================================
 /* =============================================================================
  * Decoding
  * Decoding
+ *
+ * The following code will translates the raw singals as received by
+ * the CC1101 into logical signals: a bitmap of 0s and 1s sampled at
+ * the detected data clock interval.
+ *
+ * Then the converted signal is passed to the protocols decoders, that look
+ * for protocol-specific information. We stop at the first decoder that is
+ * able to decode the data, so protocols here should be registered in
+ * order of complexity and specificity, with the generic ones at the end.
  * ===========================================================================*/
  * ===========================================================================*/
 
 
 /* Set the 'bitpos' bit to value 'val', in the specified bitmap
 /* Set the 'bitpos' bit to value 'val', in the specified bitmap
  * 'b' of len 'blen'.
  * 'b' of len 'blen'.
  * Out of range bits will silently be discarded. */
  * Out of range bits will silently be discarded. */
-void set_bit(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
+void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
     uint32_t byte = bitpos/8;
     uint32_t byte = bitpos/8;
     uint32_t bit = bitpos&7;
     uint32_t bit = bitpos&7;
     if (byte >= blen) return;
     if (byte >= blen) return;
@@ -142,13 +154,39 @@ void set_bit(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) {
 
 
 /* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
 /* Get the bit 'bitpos' of the bitmap 'b' of 'blen' bytes.
  * Out of range bits return false (not bit set). */
  * Out of range bits return false (not bit set). */
-bool get_bit(uint8_t *b, uint32_t blen, uint32_t bitpos) {
+bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
     uint32_t byte = bitpos/8;
     uint32_t byte = bitpos/8;
     uint32_t bit = bitpos&7;
     uint32_t bit = bitpos&7;
     if (byte >= blen) return 0;
     if (byte >= blen) return 0;
     return (b[byte] & (1<<bit)) != 0;
     return (b[byte] & (1<<bit)) != 0;
 }
 }
 
 
+/* Return true if the specified sequence of bits, provided as a string in the
+ * form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos'
+ * position. */
+bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) {
+    size_t l = strlen(bits);
+    for (size_t j = 0; j < l; j++) {
+        bool expected = (bits[j] == '1') ? true : false;
+        if (bitmap_get(b,blen,bitpos+j) != expected) return false;
+    }
+    return true;
+}
+
+/* Search for the specified bit sequence (see bitmap_match_bits() for details)
+ * in the bitmap 'b' of 'blen' bytes. Returns the offset (in bits) of the
+ * match, or BITMAP_SEEK_NOT_FOUND if not found.
+ *
+ * Note: there are better algorithms, such as Boyer-Moore. Here we hope that
+ * for the kind of patterns we search we'll have a lot of early stops so
+ * we use a vanilla approach. */
+uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, const char *bits) {
+    uint32_t endpos = blen*8;
+    for (uint32_t j = startpos; j < endpos; j++)
+        if (bitmap_match_bits(b,blen,j,bits)) return j;
+    return BITMAP_SEEK_NOT_FOUND;
+}
+
 /* Take the raw signal and turn it into a sequence of bits inside the
 /* Take the raw signal and turn it into a sequence of bits inside the
  * buffer 'b'. Note that such 0s and 1s are NOT the actual data in the
  * buffer 'b'. Note that such 0s and 1s are NOT the actual data in the
  * signal, but is just a low level representation of the line code. Basically
  * signal, but is just a low level representation of the line code. Basically
@@ -187,7 +225,80 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s,
         uint32_t numbits = dur / rate; /* full bits that surely fit. */
         uint32_t numbits = dur / rate; /* full bits that surely fit. */
         uint32_t rest = dur % rate;    /* How much we are left with. */
         uint32_t rest = dur % rate;    /* How much we are left with. */
         if (rest > rate/2) numbits++;  /* There is another one. */
         if (rest > rate/2) numbits++;  /* There is another one. */
-        while(numbits--) set_bit(b,blen,bitpos++,s[j].level);
+
+        FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur,numbits,(int)level);
+
+        /* If the signal is too short, let's claim it an interference
+         * and ignore it completely. */
+        if (numbits == 0) continue;
+
+        while(numbits--) bitmap_set(b,blen,bitpos++,level);
     }
     }
     return bitpos;
     return bitpos;
 }
 }
+
+/* This function converts the line code used to the final data representation.
+ * The representation is put inside 'buf', for up to 'buflen' bytes of total
+ * data. For instance in order to convert manchester I can use "10" and "01"
+ * as zero and one patterns. It is possible to use "?" inside patterns in
+ * order to skip certain bits. For instance certain devices encode data twice,
+ * with each bit encoded in manchester encoding and then in its reversed
+ * representation. In such a case I could use "10??" and "01??".
+ *
+ * The function returns the number of bits converted. It will stop as soon
+ * as it finds a pattern that does not match zero or one patterns. */
+#if 0
+uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, const char *zero_pattern, const char *one_pattern)
+{
+}
+#endif
+
+/* Supported protocols go here, with the relevant implementation inside
+ * protocols/<name>.c */
+
+extern ProtoViewDecoder Oregon2Decoder;
+
+ProtoViewDecoder *Decoders[] = {
+    &Oregon2Decoder,
+    NULL
+};
+
+/* This function is called when a new signal is detected. It converts it
+ * to a bitstream, and the calls the protocol specific functions for
+ * decoding. */
+void decode_signal(RawSamplesBuffer *s, uint64_t len) {
+    uint32_t bitmap_bits_size = 4096*8;
+    uint32_t bitmap_size = bitmap_bits_size/8;
+
+    /* We call the decoders with an offset a few bits before the actual
+     * signal detected and for a len of a few bits after its end. */
+    uint32_t before_after_bits = 2;
+
+    uint8_t *bitmap = malloc(bitmap_size);
+    uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_after_bits,len+before_after_bits*2,s->short_pulse_dur);
+
+    if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */
+        char *str = malloc(1024);
+        uint32_t j;
+        for (j = 0; j < bits && j < 1023; j++) {
+            str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0';
+        }
+        str[j] = 0;
+        FURI_LOG_E(TAG, "%lu bits decoded: %s", bits, str);
+        free(str);
+    }
+
+    /* Try all the decoders available. */
+    int j = 0;
+    while(Decoders[j]) {
+        FURI_LOG_E(TAG, "Calling decoder %s", Decoders[j]->name);
+        ProtoViewMsgInfo info;
+        if (Decoders[j]->decode(bitmap,bits,&info)) {
+            FURI_LOG_E(TAG, "Message detected by %s", Decoders[j]->name);
+            break;
+        }
+        j++;
+    }
+    if (Decoders[j] == NULL) FURI_LOG_E(TAG, "No decoding possible");
+    free(bitmap);
+}

+ 4 - 1
view_raw_signal.c

@@ -73,7 +73,10 @@ void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) {
          * previous samples. */
          * previous samples. */
         if (input.key == InputKeyRight) app->signal_offset++;
         if (input.key == InputKeyRight) app->signal_offset++;
         else if (input.key == InputKeyLeft) app->signal_offset--;
         else if (input.key == InputKeyLeft) app->signal_offset--;
-        else if (input.key == InputKeyOk) app->signal_offset = 0;
+        else if (input.key == InputKeyOk) {
+            app->signal_offset = 0;
+            app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
+        }
     } else if (input.type == InputTypeShort) {
     } else if (input.type == InputTypeShort) {
         if (input.key == InputKeyOk) {
         if (input.key == InputKeyOk) {
             /* Reset the current sample to capture the next. */
             /* Reset the current sample to capture the next. */