diff --git a/app.c b/app.c index 3570f5fd26b..4a833a0261e 100644 --- a/app.c +++ b/app.c @@ -38,9 +38,7 @@ static void render_callback(Canvas *const canvas, void *ctx) { static void input_callback(InputEvent* input_event, void* ctx) { ProtoViewApp *app = ctx; - 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. @@ -67,7 +65,7 @@ ProtoViewApp* protoview_app_alloc() { // Signal found and visualization defaults app->signal_bestlen = 0; - app->us_scale = 100; + app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; app->signal_offset = 0; //init Worker & Protocol @@ -159,8 +157,8 @@ int32_t protoview_app_entry(void* p) { while(app->running) { FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); 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 * in the view specific handling function. */ @@ -200,8 +198,10 @@ int32_t protoview_app_entry(void* p) { } else { /* Useful to understand if the app is still alive when it * 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); } diff --git a/app.h b/app.h index 4e413e79b08..39003c30d3d 100644 --- a/app.h +++ b/app.h @@ -23,6 +23,10 @@ #include "app_buffer.h" #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; @@ -86,6 +90,26 @@ struct ProtoViewApp { 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; /* app_radio.c */ @@ -98,6 +122,9 @@ void radio_sleep(ProtoViewApp* app); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); 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 */ void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app); diff --git a/protocols/oregon2.c b/protocols/oregon2.c new file mode 100644 index 00000000000..becbd9d772b --- /dev/null +++ b/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 +}; diff --git a/protocols/oregon2.txt b/protocols/oregon2.txt new file mode 100644 index 00000000000..36263143114 --- /dev/null +++ b/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 diff --git a/signal.c b/signal.c index 30465915bed..f40b482a8da 100644 --- a/signal.c +++ b/signal.c @@ -3,6 +3,8 @@ #include "app.h" +void decode_signal(RawSamplesBuffer *s, uint64_t len); + /* ============================================================================= * Raw signal detection * ===========================================================================*/ @@ -117,6 +119,7 @@ void scan_for_signal(ProtoViewApp *app) { DetectedSamples->total; FURI_LOG_E(TAG, "Displayed sample updated (%d samples)", (int)thislen); + decode_signal(DetectedSamples,thislen); } i += thislen ? thislen : 1; } @@ -125,12 +128,21 @@ void scan_for_signal(ProtoViewApp *app) { /* ============================================================================= * 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 * 'b' of len 'blen'. * 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 bit = bitpos&7; 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. * 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 bit = bitpos&7; if (byte >= blen) return 0; return (b[byte] & (1< 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; } + +/* 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/.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); +} diff --git a/view_raw_signal.c b/view_raw_signal.c index 80b0c082161..dce5ea506b0 100644 --- a/view_raw_signal.c +++ b/view_raw_signal.c @@ -73,7 +73,10 @@ void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) { * previous samples. */ if (input.key == InputKeyRight) 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) { if (input.key == InputKeyOk) { /* Reset the current sample to capture the next. */