Skip to content

Commit

Permalink
Protocol decoding, work in progress.
Browse files Browse the repository at this point in the history
  • Loading branch information
antirez committed Jan 2, 2023
1 parent 52e60b5 commit 630290f
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 11 deletions.
14 changes: 7 additions & 7 deletions app.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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);
}
Expand Down
27 changes: 27 additions & 0 deletions app.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand All @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions protocols/oregon2.c
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions protocols/oregon2.txt
Original file line number Diff line number Diff line change
@@ -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
117 changes: 114 additions & 3 deletions signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "app.h"

void decode_signal(RawSamplesBuffer *s, uint64_t len);

/* =============================================================================
* Raw signal detection
* ===========================================================================*/
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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<<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
* 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
Expand Down Expand Up @@ -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 rest = dur % rate; /* How much we are left with. */
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;
}

/* 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);
}
5 changes: 4 additions & 1 deletion view_raw_signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down

0 comments on commit 630290f

Please sign in to comment.