Skip to content

Commit

Permalink
Fixes, comments, Oregon2 basic protocol.
Browse files Browse the repository at this point in the history
  • Loading branch information
antirez committed Jan 3, 2023
1 parent 31fd6df commit 0f44252
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 36 deletions.
1 change: 1 addition & 0 deletions app.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ ProtoViewApp* protoview_app_alloc() {

// Signal found and visualization defaults
app->signal_bestlen = 0;
app->signal_decoded = false;
app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE;
app->signal_offset = 0;

Expand Down
13 changes: 12 additions & 1 deletion app.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ struct ProtoViewApp {
/* Generic app state. */
int running; /* Once false exists the app. */
uint32_t signal_bestlen; /* Longest coherent signal observed so far. */
bool signal_decoded; /* Was the current signal decoded? */

/* Raw view apps state. */
uint32_t us_scale; /* microseconds per pixel. */
Expand Down Expand Up @@ -107,7 +108,14 @@ typedef struct ProtoViewMsgInfo {

typedef struct ProtoViewDecoder {
const char *name; /* Protocol name. */
bool (*decode)(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info);
/* The decode function takes a buffer that is actually a bitmap, with
* high and low levels represented as 0 and 1. The number of high/low
* pulses represented by the bitmap is passed as the 'numbits' argument,
* while 'numbytes' represents the total size of the bitmap pointed by
* 'bits'. So 'numbytes' is mainly useful to pass as argument to other
* functions that perform bit extraction with bound checking, such as
* bitmap_get() and so forth. */
bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info);
} ProtoViewDecoder;

extern RawSamplesBuffer *RawSamples, *DetectedSamples;
Expand All @@ -121,8 +129,11 @@ void radio_sleep(ProtoViewApp* app);

/* signal.c */
uint32_t duration_delta(uint32_t a, uint32_t b);
void reset_current_signal(ProtoViewApp *app);
void scan_for_signal(ProtoViewApp *app);
bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos);
void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val);
void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len);
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);
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern);
Expand Down
6 changes: 6 additions & 0 deletions app_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ void raw_samples_reset(RawSamplesBuffer *s) {
furi_mutex_release(s->mutex);
}

/* Set the raw sample internal index so that what is currently at
* offset 'offset', will appear to be at 0 index. */
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) {
s->idx = (s->idx+offset) % RAW_SAMPLES_NUM;
}

/* Add the specified sample in the circular buffer. */
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) {
furi_mutex_acquire(s->mutex,FuriWaitForever);
Expand Down
1 change: 1 addition & 0 deletions app_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef struct RawSamplesBuffer {

RawSamplesBuffer *raw_samples_alloc(void);
void raw_samples_reset(RawSamplesBuffer *s);
void raw_samples_center(RawSamplesBuffer *s, uint32_t offset);
void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur);
void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur);
void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src);
Expand Down
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ App(
entry_point="protoview_app_entry",
cdefines=["APP_PROTOVIEW"],
requires=["gui"],
stack_size=8 * 1024,
stack_size=8*1024,
order=50,
fap_icon="appicon.png",
fap_category="Tools",
Expand Down
8 changes: 5 additions & 3 deletions protocols/b4b1.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

#include "../app.h"

static bool decode(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info) {
static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
if (numbits < 30) return false;
const char *sync_patterns[3] = {
"10000000000000000000000000000001", /* 30 zero bits. */
"100000000000000000000000000000001", /* 31 zero bits. */
Expand All @@ -17,7 +18,7 @@ static bool decode(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info) {
uint32_t off;
int j;
for (j = 0; j < 3; j++) {
off = bitmap_seek_bits(bits,numbits,0,sync_patterns[j]);
off = bitmap_seek_bits(bits,numbytes,0,sync_patterns[j]);
if (off != BITMAP_SEEK_NOT_FOUND) break;
}
if (off == BITMAP_SEEK_NOT_FOUND) return false;
Expand All @@ -26,10 +27,11 @@ static bool decode(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info) {

uint8_t d[3]; /* 24 bits of data. */
uint32_t decoded =
convert_from_line_code(d,sizeof(d),bits,numbits,off,"1000","1110");
convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110");

if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded);
if (decoded != 24) return false;
bitmap_invert_bytes_bits(d,sizeof(d));
snprintf(info->name,PROTOVIEW_MSG_STR_LEN,"PT/SC remote");
snprintf(info->raw,PROTOVIEW_MSG_STR_LEN,"%02X%02X%02X",d[0],d[1],d[2]);
info->len = off+(4*24);
Expand Down
80 changes: 77 additions & 3 deletions protocols/oregon2.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,86 @@
#include "../app.h"

static bool decode(uint8_t *bits, uint64_t numbits, ProtoViewMsgInfo *info) {
#if 0
/* Invert byte ordering. */
static void invert_nibbles(uint8_t *p, uint32_t len) {
len *= 8;
for (uint32_t j = 0; j < len; j += 4) {
bool b0 = bitmap_get(p,len,j);
bool b1 = bitmap_get(p,len,j+1);
bool b2 = bitmap_get(p,len,j+2);
bool b3 = bitmap_get(p,len,j+3);
bitmap_set(p,len,j,b3);
bitmap_set(p,len,j+1,b2);
bitmap_set(p,len,j+2,b1);
bitmap_set(p,len,j+3,b0);
}
}
#endif

static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
if (numbits < 32) return false;
const char *sync_pattern = "01100110" "01100110" "10010110" "10010110";
uint64_t off = bitmap_seek_bits(bits,numbits,0,sync_pattern);
uint64_t off = bitmap_seek_bits(bits,numbytes,0,sync_pattern);
if (off == BITMAP_SEEK_NOT_FOUND) return false;

FURI_LOG_E(TAG, "Oregon2 prelude+sync found");

off += 32; /* Skip preamble. */

FURI_LOG_E(TAG, "Bits: %d%d%d%d",
bitmap_get(bits,numbytes,off),
bitmap_get(bits,numbytes,off+1),
bitmap_get(bits,numbytes,off+2),
bitmap_get(bits,numbytes,off+3));

FURI_LOG_E(TAG, "Bits: %d%d%d%d",
bitmap_get(bits,numbytes,off+4),
bitmap_get(bits,numbytes,off+5),
bitmap_get(bits,numbytes,off+6),
bitmap_get(bits,numbytes,off+7));

FURI_LOG_E(TAG, "Bits: %d%d%d%d",
bitmap_get(bits,numbytes,off+8),
bitmap_get(bits,numbytes,off+9),
bitmap_get(bits,numbytes,off+10),
bitmap_get(bits,numbytes,off+11));

FURI_LOG_E(TAG, "Bits: %d%d%d%d",
bitmap_get(bits,numbytes,off+12),
bitmap_get(bits,numbytes,off+13),
bitmap_get(bits,numbytes,off+14),
bitmap_get(bits,numbytes,off+15));

uint8_t buffer[8];
uint32_t decoded =
convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110");
FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded);

char temp[3] = {0}, deviceid[2] = {0};
for (int j = 0; j < 64; j += 4) {
uint8_t nib[1];
nib[0] = 0;
bitmap_set(nib,1,0,bitmap_get(buffer,8,j+0));
bitmap_set(nib,1,1,bitmap_get(buffer,8,j+1));
bitmap_set(nib,1,2,bitmap_get(buffer,8,j+2));
bitmap_set(nib,1,3,bitmap_get(buffer,8,j+3));
FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j/4, (unsigned int)nib[0]);
switch(j/4) {
case 1: deviceid[0] |= nib[0]; break;
case 0: deviceid[0] |= nib[0] << 4; break;
case 3: deviceid[1] |= nib[0]; break;
case 2: deviceid[1] |= nib[0] << 4; break;
case 10: temp[0] = nib[0]; break;
case 9: temp[1] = nib[0]; break;
case 8: temp[2] = nib[0]; break;
}
}

snprintf(info->name,sizeof(info->name),"%s","Oregon v2.1");
snprintf(info->raw,sizeof(info->raw),"%08llX", *((uint64_t*)buffer));
snprintf(info->info1,sizeof(info->info1),"Temp %d%d.%d",
temp[0],temp[1],temp[2]);
snprintf(info->info2,sizeof(info->info2),"ID %02X%02X",
deviceid[0], deviceid[1]);
return true;
}

Expand Down
94 changes: 70 additions & 24 deletions signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

#include "app.h"

void decode_signal(RawSamplesBuffer *s, uint64_t len);
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info);
void initialize_msg_info(ProtoViewMsgInfo *i);

/* =============================================================================
* Raw signal detection
Expand All @@ -15,6 +16,15 @@ uint32_t duration_delta(uint32_t a, uint32_t b) {
return a > b ? a - b : b - a;
}

/* Reset the current signal, so that a new one can be detected. */
void reset_current_signal(ProtoViewApp *app) {
app->signal_bestlen = 0;
app->signal_offset = 0;
app->signal_decoded = false;
raw_samples_reset(DetectedSamples);
raw_samples_reset(RawSamples);
}

/* This function starts scanning samples at offset idx looking for the
* longest run of pulses, either high or low, that are not much different
* from each other, for a maximum of three duration classes.
Expand Down Expand Up @@ -126,15 +136,34 @@ void scan_for_signal(ProtoViewApp *app) {

uint32_t i = 0;
while (i < copy->total-1) {
ProtoViewMsgInfo info;
uint32_t thislen = search_coherent_signal(copy,i);
if (thislen > minlen && thislen > app->signal_bestlen) {
app->signal_bestlen = thislen;
raw_samples_copy(DetectedSamples,copy);
DetectedSamples->idx = (DetectedSamples->idx+i)%
DetectedSamples->total;
FURI_LOG_E(TAG, "Displayed sample updated (%d samples %lu us)",
(int)thislen, DetectedSamples->short_pulse_dur);
decode_signal(DetectedSamples,thislen);

/* For messages that are long enough, attempt decoding. */
if (thislen > minlen) {

initialize_msg_info(&info);
uint32_t saved_idx = copy->idx; /* Save index, see later. */
/* decode_signal() expects the detected signal to start
* from index .*/
raw_samples_center(copy,i);
bool decoded = decode_signal(copy,thislen,&info);
copy->idx = saved_idx; /* Restore the index as we are scanning
the signal in the loop. */

/* Accept this signal as the new signal if either it's longer
* than the previous one, or the previous one was unknown and
* this is decoded. */
if (thislen > app->signal_bestlen ||
(app->signal_decoded == false && decoded))
{
app->signal_bestlen = thislen;
app->signal_decoded = decoded;
raw_samples_copy(DetectedSamples,copy);
raw_samples_center(DetectedSamples,i);
FURI_LOG_E(TAG, "Displayed sample updated (%d samples %lu us)",
(int)thislen, DetectedSamples->short_pulse_dur);
}
}
i += thislen ? thislen : 1;
}
Expand Down Expand Up @@ -176,6 +205,17 @@ bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
return (b[byte] & (1<<bit)) != 0;
}

/* We decode bits assuming the first bit we receive is the LSB
* (see bitmap_set/get functions). Many devices send data
* encoded in the reverse way. */
void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len) {
for (uint32_t j = 0; j < len*8; j += 8) {
bool bits[8];
for (int i = 0; i < 8; i++) bits[i] = bitmap_get(p,len,j+i);
for (int i = 0; i < 8; i++) bitmap_set(p,len,j+i,bits[7-i]);
}
}

/* 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. */
Expand Down Expand Up @@ -267,23 +307,27 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s,
* 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. The
* decoding starts at the specified offset 'off'. */
* as it finds a pattern that does not match zero or one patterns, or when
* the end of the bitmap pointed by 'bits' is reached (the length is
* specified in bytes by the caller, via the 'len' parameters).
*
* The decoding starts at the specified offset (in bits) 'off'. */
uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern)
{
uint32_t decoded = 0; /* Number of bits extracted. */
len *= 8; /* Convert bytes to bits. */
while(off < len) {
bool level;
bool bitval;
if (bitmap_match_bits(bits,len,off,zero_pattern)) {
level = true;
bitval = false;
off += strlen(zero_pattern);
} else if (bitmap_match_bits(bits,len,off,one_pattern)) {
level = false;
off += strlen(zero_pattern);
bitval = true;
off += strlen(one_pattern);
} else {
break;
}
bitmap_set(buf,buflen,decoded++,level);
bitmap_set(buf,buflen,decoded++,bitval);
if (decoded/8 == buflen) break; /* No space left on target buffer. */
}
return decoded;
Expand All @@ -309,8 +353,9 @@ void initialize_msg_info(ProtoViewMsgInfo *i) {

/* 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) {
* decoding. If the signal was decoded correctly by some protocol, true
* is returned. Otherwise false is returned. */
bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) {
uint32_t bitmap_bits_size = 4096*8;
uint32_t bitmap_size = bitmap_bits_size/8;

Expand All @@ -328,28 +373,29 @@ void decode_signal(RawSamplesBuffer *s, uint64_t len) {
str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0';
}
str[j] = 0;
FURI_LOG_E(TAG, "%lu bits decoded: %s", bits, str);
FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str);
free(str);
}

/* Try all the decoders available. */
int j = 0;
ProtoViewMsgInfo info;
initialize_msg_info(&info);

bool decoded = false;
while(Decoders[j]) {
FURI_LOG_E(TAG, "Calling decoder %s", Decoders[j]->name);
if (Decoders[j]->decode(bitmap,bits,&info)) {
if (Decoders[j]->decode(bitmap,bitmap_size,bits,info)) {
FURI_LOG_E(TAG, "Message detected by %s", Decoders[j]->name);
decoded = true;
break;
}
j++;
}

if (Decoders[j] == NULL) {
if (!decoded) {
FURI_LOG_E(TAG, "No decoding possible");
} else {
FURI_LOG_E(TAG, "Decoded %s, raw=%s", info.name, info.raw);
FURI_LOG_E(TAG, "Decoded %s, raw=%s info=[%s,%s,%s]", info->name, info->raw, info->info1, info->info2, info->info3);
}
free(bitmap);
return decoded;
}
5 changes: 1 addition & 4 deletions view_raw_signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,7 @@ void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) {
} else if (input.type == InputTypeShort) {
if (input.key == InputKeyOk) {
/* Reset the current sample to capture the next. */
app->signal_bestlen = 0;
app->signal_offset = 0;
raw_samples_reset(DetectedSamples);
raw_samples_reset(RawSamples);
reset_current_signal(app);
} else if (input.key == InputKeyDown) {
/* Rescaling. The set becomes finer under 50us per pixel. */
uint32_t scale_step = app->us_scale >= 50 ? 50 : 10;
Expand Down

0 comments on commit 0f44252

Please sign in to comment.