163be139eb
* SubGhz: add protocol DataRAW (binarization of data quantized by the minimum correlated duration) * SubGhz: fix name history * SubGhz: add encoder Data_RAW protocol * SubGhz: decreasing the size of the LevelDuration structure * SubGhz: history, added check that there is free RAM * SubGhz: checking for free memory, support to pass without gap * SubGhz: add running average to average the result, auto cut noise at the end of a burst * SubGhz: support for repeating sequences * SubGhz: fix secplus_v2 decoder * SubGhz: bin_RAW fix add history * SubGhz: add debug * SubGhz: debug refactoring * FURI_LOG: add FURI_LOG_RAW_x formatted string output like printf * SubGhz: fix new FURI_LOG metod * FURI_LOG: fix unit test * SubGhz: add enable/disable BinRAW protocol decoding * SubGhz: fix PVS * SubGhz: forcibly turn off the speaker when exiting SubGhz * SubGhz: adaptive adjustment to the noise level Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
1121 lines
43 KiB
C
1121 lines
43 KiB
C
#include "bin_raw.h"
|
|
|
|
#include "../blocks/const.h"
|
|
#include "../blocks/decoder.h"
|
|
#include "../blocks/encoder.h"
|
|
#include "../blocks/generic.h"
|
|
#include "../blocks/math.h"
|
|
#include <lib/toolbox/float_tools.h>
|
|
#include <lib/toolbox/stream/stream.h>
|
|
#include <lib/flipper_format/flipper_format_i.h>
|
|
|
|
#define TAG "SubGhzProtocolBinRAW"
|
|
|
|
//change very carefully, RAM ends at the most inopportune moment
|
|
#define BIN_RAW_BUF_RAW_SIZE 2048
|
|
#define BIN_RAW_BUF_DATA_SIZE 512
|
|
|
|
#define BIN_RAW_THRESHOLD_RSSI -85.0f
|
|
#define BIN_RAW_DELTA_RSSI 7.0f
|
|
#define BIN_RAW_SEARCH_CLASSES 20
|
|
#define BIN_RAW_TE_MIN_COUNT 40
|
|
#define BIN_RAW_BUF_MIN_DATA_COUNT 128
|
|
#define BIN_RAW_MAX_MARKUP_COUNT 20
|
|
|
|
//#define BIN_RAW_DEBUG
|
|
|
|
#ifdef BIN_RAW_DEBUG
|
|
#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__)
|
|
#define bin_raw_debug_tag(tag, ...) \
|
|
FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \
|
|
FURI_LOG_RAW_D(__VA_ARGS__)
|
|
#else
|
|
#define bin_raw_debug(...)
|
|
#define bin_raw_debug_tag(...)
|
|
#endif
|
|
|
|
static const SubGhzBlockConst subghz_protocol_bin_raw_const = {
|
|
.te_short = 30,
|
|
.te_long = 65000,
|
|
.te_delta = 0,
|
|
.min_count_bit_for_found = 0,
|
|
};
|
|
|
|
typedef enum {
|
|
BinRAWDecoderStepReset = 0,
|
|
BinRAWDecoderStepWrite,
|
|
BinRAWDecoderStepBufFull,
|
|
BinRAWDecoderStepNoParse,
|
|
} BinRAWDecoderStep;
|
|
|
|
typedef enum {
|
|
BinRAWTypeUnknown = 0,
|
|
BinRAWTypeNoGap,
|
|
BinRAWTypeGap,
|
|
BinRAWTypeGapRecurring,
|
|
BinRAWTypeGapRolling,
|
|
BinRAWTypeGapUnknown,
|
|
} BinRAWType;
|
|
|
|
struct BinRAW_Markup {
|
|
uint16_t byte_bias;
|
|
uint16_t bit_count;
|
|
};
|
|
typedef struct BinRAW_Markup BinRAW_Markup;
|
|
|
|
struct SubGhzProtocolDecoderBinRAW {
|
|
SubGhzProtocolDecoderBase base;
|
|
|
|
SubGhzBlockDecoder decoder;
|
|
SubGhzBlockGeneric generic;
|
|
int32_t* data_raw;
|
|
uint8_t* data;
|
|
BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];
|
|
size_t data_raw_ind;
|
|
uint32_t te;
|
|
float adaptive_threshold_rssi;
|
|
};
|
|
|
|
struct SubGhzProtocolEncoderBinRAW {
|
|
SubGhzProtocolEncoderBase base;
|
|
|
|
SubGhzProtocolBlockEncoder encoder;
|
|
SubGhzBlockGeneric generic;
|
|
|
|
uint8_t* data;
|
|
BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT];
|
|
uint32_t te;
|
|
};
|
|
|
|
const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = {
|
|
.alloc = subghz_protocol_decoder_bin_raw_alloc,
|
|
.free = subghz_protocol_decoder_bin_raw_free,
|
|
|
|
.feed = subghz_protocol_decoder_bin_raw_feed,
|
|
.reset = subghz_protocol_decoder_bin_raw_reset,
|
|
|
|
.get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data,
|
|
.serialize = subghz_protocol_decoder_bin_raw_serialize,
|
|
.deserialize = subghz_protocol_decoder_bin_raw_deserialize,
|
|
.get_string = subghz_protocol_decoder_bin_raw_get_string,
|
|
};
|
|
|
|
const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = {
|
|
.alloc = subghz_protocol_encoder_bin_raw_alloc,
|
|
.free = subghz_protocol_encoder_bin_raw_free,
|
|
|
|
.deserialize = subghz_protocol_encoder_bin_raw_deserialize,
|
|
.stop = subghz_protocol_encoder_bin_raw_stop,
|
|
.yield = subghz_protocol_encoder_bin_raw_yield,
|
|
};
|
|
|
|
const SubGhzProtocol subghz_protocol_bin_raw = {
|
|
.name = SUBGHZ_PROTOCOL_BIN_RAW_NAME,
|
|
.type = SubGhzProtocolTypeStatic,
|
|
#ifdef BIN_RAW_DEBUG
|
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
|
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
|
#else
|
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
|
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW |
|
|
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
|
#endif
|
|
.decoder = &subghz_protocol_bin_raw_decoder,
|
|
.encoder = &subghz_protocol_bin_raw_encoder,
|
|
};
|
|
|
|
static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) {
|
|
if(bit_count & 0x7) {
|
|
return (bit_count >> 3) + 1;
|
|
} else {
|
|
return (bit_count >> 3);
|
|
}
|
|
}
|
|
|
|
void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW));
|
|
|
|
instance->base.protocol = &subghz_protocol_bin_raw;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
|
|
instance->encoder.repeat = 10;
|
|
instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5;
|
|
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
|
instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t));
|
|
memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
instance->encoder.is_running = false;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_encoder_bin_raw_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderBinRAW* instance = context;
|
|
free(instance->encoder.upload);
|
|
free(instance->data);
|
|
free(instance);
|
|
}
|
|
|
|
/**
|
|
* Generating an upload from data.
|
|
* @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance
|
|
* @return true On success
|
|
*/
|
|
static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) {
|
|
furi_assert(instance);
|
|
|
|
//we glue all the pieces of the package into 1 long sequence with left alignment,
|
|
//in the uploaded data we have right alignment.
|
|
|
|
bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n");
|
|
uint16_t i = 0;
|
|
uint16_t ind = 0;
|
|
bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n");
|
|
while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
|
|
uint8_t bit_bias =
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -
|
|
instance->data_markup[i].bit_count;
|
|
bin_raw_debug(
|
|
"\t%d\t%d\t%d :\t\t%d\r\n",
|
|
i,
|
|
instance->data_markup[i].byte_bias,
|
|
instance->data_markup[i].bit_count,
|
|
bit_bias);
|
|
for(uint16_t y = instance->data_markup[i].byte_bias * 8;
|
|
y < instance->data_markup[i].byte_bias * 8 +
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 -
|
|
bit_bias;
|
|
y++) {
|
|
subghz_protocol_blocks_set_bit_array(
|
|
subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias),
|
|
instance->data,
|
|
ind++,
|
|
BIN_RAW_BUF_DATA_SIZE);
|
|
}
|
|
i++;
|
|
}
|
|
bin_raw_debug("\r\n");
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n");
|
|
for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) {
|
|
bin_raw_debug("%02X ", instance->data[y]);
|
|
}
|
|
bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind);
|
|
|
|
bin_raw_debug_tag(
|
|
TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload);
|
|
#endif
|
|
instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(
|
|
instance->data,
|
|
ind,
|
|
instance->encoder.upload,
|
|
instance->encoder.size_upload,
|
|
instance->te,
|
|
SubGhzProtocolBlockAlignBitLeft);
|
|
|
|
bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload);
|
|
bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap());
|
|
return true;
|
|
}
|
|
|
|
bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderBinRAW* instance = context;
|
|
|
|
bool res = false;
|
|
uint32_t temp_data = 0;
|
|
|
|
do {
|
|
if(!flipper_format_rewind(flipper_format)) {
|
|
FURI_LOG_E(TAG, "Rewind error");
|
|
break;
|
|
}
|
|
if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
|
|
FURI_LOG_E(TAG, "Missing Bit");
|
|
break;
|
|
}
|
|
|
|
instance->generic.data_count_bit = (uint16_t)temp_data;
|
|
|
|
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
|
FURI_LOG_E(TAG, "Missing TE");
|
|
break;
|
|
}
|
|
|
|
temp_data = 0;
|
|
uint16_t ind = 0;
|
|
uint16_t byte_bias = 0;
|
|
uint16_t byte_count = 0;
|
|
memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) {
|
|
if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {
|
|
FURI_LOG_E(TAG, "Markup overflow");
|
|
break;
|
|
}
|
|
byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);
|
|
if(byte_count > BIN_RAW_BUF_DATA_SIZE) {
|
|
FURI_LOG_E(TAG, "Receive buffer overflow");
|
|
break;
|
|
}
|
|
|
|
instance->data_markup[ind].bit_count = temp_data;
|
|
instance->data_markup[ind].byte_bias = byte_bias;
|
|
byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);
|
|
|
|
if(!flipper_format_read_hex(
|
|
flipper_format,
|
|
"Data_RAW",
|
|
instance->data + instance->data_markup[ind].byte_bias,
|
|
subghz_protocol_bin_raw_get_full_byte(temp_data))) {
|
|
instance->data_markup[ind].bit_count = 0;
|
|
FURI_LOG_E(TAG, "Missing Data_RAW");
|
|
break;
|
|
}
|
|
ind++;
|
|
}
|
|
|
|
#ifdef BIN_RAW_DEBUG
|
|
uint16_t i = 0;
|
|
bin_raw_debug_tag(TAG, "Download data to encoder\r\n");
|
|
bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data");
|
|
while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
|
|
bin_raw_debug(
|
|
"\r\n\t%d\t%d\t%d :\t",
|
|
i,
|
|
instance->data_markup[i].byte_bias,
|
|
instance->data_markup[i].bit_count);
|
|
for(uint16_t y = instance->data_markup[i].byte_bias;
|
|
y < instance->data_markup[i].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
|
|
y++) {
|
|
bin_raw_debug("%02X ", instance->data[y]);
|
|
}
|
|
i++;
|
|
}
|
|
bin_raw_debug("\r\n\r\n");
|
|
#endif
|
|
if(!flipper_format_rewind(flipper_format)) {
|
|
FURI_LOG_E(TAG, "Rewind error");
|
|
break;
|
|
}
|
|
//optional parameter parameter
|
|
flipper_format_read_uint32(
|
|
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
|
|
|
if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break;
|
|
instance->encoder.is_running = true;
|
|
|
|
res = true;
|
|
} while(0);
|
|
|
|
return res;
|
|
}
|
|
|
|
void subghz_protocol_encoder_bin_raw_stop(void* context) {
|
|
SubGhzProtocolEncoderBinRAW* instance = context;
|
|
instance->encoder.is_running = false;
|
|
}
|
|
|
|
LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) {
|
|
SubGhzProtocolEncoderBinRAW* instance = context;
|
|
|
|
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
|
instance->encoder.is_running = false;
|
|
return level_duration_reset();
|
|
}
|
|
|
|
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
|
|
|
if(++instance->encoder.front == instance->encoder.size_upload) {
|
|
instance->encoder.repeat--;
|
|
instance->encoder.front = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW));
|
|
instance->base.protocol = &subghz_protocol_bin_raw;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
instance->data_raw_ind = 0;
|
|
instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));
|
|
instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));
|
|
memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_decoder_bin_raw_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
free(instance->data_raw);
|
|
free(instance->data);
|
|
free(instance);
|
|
}
|
|
|
|
void subghz_protocol_decoder_bin_raw_reset(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
#ifdef BIN_RAW_DEBUG
|
|
UNUSED(instance);
|
|
#else
|
|
instance->decoder.parser_step = BinRAWDecoderStepNoParse;
|
|
instance->data_raw_ind = 0;
|
|
#endif
|
|
}
|
|
|
|
void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
|
|
if(instance->decoder.parser_step == BinRAWDecoderStepWrite) {
|
|
if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) {
|
|
instance->decoder.parser_step = BinRAWDecoderStepBufFull;
|
|
} else {
|
|
instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Analysis of received data
|
|
* @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance
|
|
*/
|
|
static bool
|
|
subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) {
|
|
struct {
|
|
float data;
|
|
uint16_t count;
|
|
} classes[BIN_RAW_SEARCH_CLASSES];
|
|
|
|
size_t ind = 0;
|
|
|
|
memset(classes, 0x00, sizeof(classes));
|
|
|
|
uint16_t data_markup_ind = 0;
|
|
memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
|
|
if(instance->data_raw_ind < 512) {
|
|
ind =
|
|
instance->data_raw_ind -
|
|
100; //there is usually garbage at the end of the record, we exclude it from the classification
|
|
} else {
|
|
ind = 512;
|
|
}
|
|
|
|
//sort the durations to find the shortest correlated interval
|
|
for(size_t i = 0; i < ind; i++) {
|
|
for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
|
|
if(classes[k].count == 0) {
|
|
classes[k].data = (float)(abs(instance->data_raw[i]));
|
|
classes[k].count++;
|
|
break;
|
|
} else if(
|
|
DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) <
|
|
(classes[k].data / 4)) { //if the test value does not differ by more than 25%
|
|
classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) *
|
|
0.05f; //running average k=0.05
|
|
classes[k].count++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) {
|
|
// //filling the classifier, it means that they received an unclean signal
|
|
// return false;
|
|
// }
|
|
|
|
//looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT
|
|
instance->te = subghz_protocol_bin_raw_const.te_long * 2;
|
|
|
|
bool te_ok = false;
|
|
uint16_t gap_ind = 0;
|
|
uint16_t gap_delta = 0;
|
|
uint32_t gap = 0;
|
|
int data_temp = 0;
|
|
BinRAWType bin_raw_type = BinRAWTypeUnknown;
|
|
|
|
//sort by number of occurrences
|
|
bool swap = true;
|
|
while(swap) {
|
|
swap = false;
|
|
for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) {
|
|
if(classes[i].count > classes[i - 1].count) {
|
|
uint32_t data = classes[i - 1].data;
|
|
uint32_t count = classes[i - 1].count;
|
|
classes[i - 1].data = classes[i].data;
|
|
classes[i - 1].count = classes[i].count;
|
|
classes[i].data = data;
|
|
classes[i].count = count;
|
|
swap = true;
|
|
}
|
|
}
|
|
}
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug_tag(TAG, "Sorted durations\r\n");
|
|
bin_raw_debug("\t\tind\tcount\tus\r\n");
|
|
for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
|
|
bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data);
|
|
}
|
|
bin_raw_debug("\r\n");
|
|
#endif
|
|
if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) {
|
|
//adopted only the preamble
|
|
instance->te = (uint32_t)classes[0].data;
|
|
te_ok = true;
|
|
gap = 0; //gap no
|
|
} else {
|
|
//take the 2 most common durations
|
|
//check that there are enough
|
|
if((classes[0].count < BIN_RAW_TE_MIN_COUNT) ||
|
|
(classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1)))
|
|
return false;
|
|
//arrange the first 2 date values in ascending order
|
|
if(classes[0].data > classes[1].data) {
|
|
uint32_t data = classes[1].data;
|
|
classes[0].data = classes[1].data;
|
|
classes[1].data = data;
|
|
}
|
|
|
|
//determine the value to be corrected
|
|
for(uint8_t k = 1; k < 5; k++) {
|
|
float delta = (classes[1].data / (classes[0].data / k));
|
|
bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta));
|
|
delta -= (uint32_t)delta;
|
|
|
|
if((delta < 0.20f) || (delta > 0.80f)) {
|
|
instance->te = (uint32_t)classes[0].data / k;
|
|
bin_raw_debug_tag(TAG, "K= %d\r\n", k);
|
|
te_ok = true; //found a correlated duration
|
|
break;
|
|
}
|
|
}
|
|
if(!te_ok) {
|
|
//did not find the minimum TE satisfying the condition
|
|
return false;
|
|
}
|
|
bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te);
|
|
|
|
//looking for a gap
|
|
for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) {
|
|
if((classes[k].count > 2) && (classes[k].data > gap)) {
|
|
gap = (uint32_t)classes[k].data;
|
|
gap_delta = gap / 5; //calculate 20% deviation from ideal value
|
|
}
|
|
}
|
|
|
|
if((gap / instance->te) <
|
|
10) { //make an assumption, the longest gap should be more than 10 TE
|
|
gap = 0; //check that our signal has a gap greater than 10*TE
|
|
bin_raw_type = BinRAWTypeNoGap;
|
|
} else {
|
|
bin_raw_type = BinRAWTypeGap;
|
|
//looking for the last occurrence of gap
|
|
ind = instance->data_raw_ind - 1;
|
|
while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) {
|
|
ind--;
|
|
}
|
|
gap_ind = ind;
|
|
}
|
|
}
|
|
|
|
//if we consider that there is a gap, then we divide the signal with respect to this gap
|
|
//processing input data from the end
|
|
if(bin_raw_type == BinRAWTypeGap) {
|
|
bin_raw_debug_tag(TAG, "Tinted sequence\r\n");
|
|
ind = (BIN_RAW_BUF_DATA_SIZE * 8);
|
|
uint16_t bit_count = 0;
|
|
do {
|
|
gap_ind--;
|
|
data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te));
|
|
bin_raw_debug("%d ", data_temp);
|
|
if(data_temp == 0) bit_count++; //there is noise in the package
|
|
for(size_t i = 0; i < abs(data_temp); i++) {
|
|
bit_count++;
|
|
if(ind) {
|
|
ind--;
|
|
} else {
|
|
break;
|
|
}
|
|
if(data_temp > 0) {
|
|
subghz_protocol_blocks_set_bit_array(
|
|
true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);
|
|
} else {
|
|
subghz_protocol_blocks_set_bit_array(
|
|
false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE);
|
|
}
|
|
}
|
|
//split into full bytes if gap is caught
|
|
if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) {
|
|
instance->data_markup[data_markup_ind].byte_bias = ind >> 3;
|
|
instance->data_markup[data_markup_ind++].bit_count = bit_count;
|
|
bit_count = 0;
|
|
|
|
if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break;
|
|
ind &= 0xFFFFFFF8; //jump to the pre whole byte
|
|
}
|
|
} while(gap_ind != 0);
|
|
if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) {
|
|
instance->data_markup[data_markup_ind].byte_bias = ind >> 3;
|
|
instance->data_markup[data_markup_ind++].bit_count = bit_count;
|
|
}
|
|
|
|
bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind);
|
|
|
|
//reset the classifier and classify the received data
|
|
memset(classes, 0x00, sizeof(classes));
|
|
|
|
bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n");
|
|
for(size_t i = 0; i < data_markup_ind; i++) {
|
|
for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
|
|
if(classes[k].count == 0) {
|
|
classes[k].data = instance->data_markup[i].bit_count;
|
|
classes[k].count++;
|
|
break;
|
|
} else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) {
|
|
classes[k].count++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug("\t\tind\tcount\tus\r\n");
|
|
for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) {
|
|
bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data);
|
|
}
|
|
bin_raw_debug("\r\n");
|
|
#endif
|
|
|
|
//choose the value with the maximum repetition
|
|
data_temp = 0;
|
|
for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) {
|
|
if((classes[i].count > 1) && (data_temp < classes[i].count))
|
|
data_temp = (int)classes[i].data;
|
|
}
|
|
|
|
//if(data_markup_ind == 0) return false;
|
|
|
|
#ifdef BIN_RAW_DEBUG
|
|
//output in reverse order
|
|
bin_raw_debug_tag(TAG, "Found sequences\r\n");
|
|
bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n");
|
|
uint16_t data_markup_ind_temp = data_markup_ind;
|
|
if(data_markup_ind) {
|
|
data_markup_ind_temp--;
|
|
for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) {
|
|
if(instance->data_markup[data_markup_ind_temp].byte_bias == i) {
|
|
bin_raw_debug(
|
|
"\r\n\t%d\t%d\t%d :\t",
|
|
data_markup_ind_temp,
|
|
instance->data_markup[data_markup_ind_temp].byte_bias,
|
|
instance->data_markup[data_markup_ind_temp].bit_count);
|
|
if(data_markup_ind_temp != 0) data_markup_ind_temp--;
|
|
}
|
|
bin_raw_debug("%02X ", instance->data[i]);
|
|
}
|
|
bin_raw_debug("\r\n\r\n");
|
|
}
|
|
//compare data in chunks with the same number of bits
|
|
bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp);
|
|
#endif
|
|
|
|
//if(data_temp == 0) data_temp = (int)classes[0].data;
|
|
|
|
if(data_temp != 0) {
|
|
//check that data in transmission is repeated every packet
|
|
for(uint16_t i = 0; i < data_markup_ind - 1; i++) {
|
|
if((instance->data_markup[i].bit_count == data_temp) &&
|
|
(instance->data_markup[i + 1].bit_count == data_temp)) {
|
|
//if the number of bits in adjacent parcels is the same, compare the data
|
|
bin_raw_debug_tag(
|
|
TAG,
|
|
"Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n",
|
|
i,
|
|
i + 1,
|
|
instance->data[instance->data_markup[i].byte_bias],
|
|
instance->data[instance->data_markup[i + 1].byte_bias],
|
|
instance->data
|
|
[instance->data_markup[i].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[i].bit_count) -
|
|
1],
|
|
instance->data
|
|
[instance->data_markup[i + 1].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[i + 1].bit_count) -
|
|
1]);
|
|
|
|
uint16_t byte_count =
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
|
|
if(memcmp(
|
|
instance->data + instance->data_markup[i].byte_bias,
|
|
instance->data + instance->data_markup[i + 1].byte_bias,
|
|
byte_count - 1) == 0) {
|
|
bin_raw_debug_tag(
|
|
TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n");
|
|
|
|
//place in 1 element the offset to valid data
|
|
instance->data_markup[0].bit_count = instance->data_markup[i].bit_count;
|
|
instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias;
|
|
//markup end sign
|
|
instance->data_markup[1].bit_count = 0;
|
|
instance->data_markup[1].byte_bias = 0;
|
|
|
|
bin_raw_type = BinRAWTypeGapRecurring;
|
|
i = data_markup_ind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bin_raw_type == BinRAWTypeGap) {
|
|
// check that retry occurs every n packets
|
|
for(uint16_t i = 0; i < data_markup_ind - 2; i++) {
|
|
uint16_t byte_count =
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count);
|
|
for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) {
|
|
bin_raw_debug_tag(
|
|
TAG,
|
|
"Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n",
|
|
i,
|
|
y,
|
|
instance->data[instance->data_markup[i].byte_bias],
|
|
instance->data[instance->data_markup[y].byte_bias],
|
|
instance->data
|
|
[instance->data_markup[i].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[i].bit_count) -
|
|
1],
|
|
instance->data
|
|
[instance->data_markup[y].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[y].bit_count) -
|
|
1]);
|
|
|
|
if(byte_count ==
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[y].bit_count)) { //if the length in bytes matches
|
|
|
|
if((memcmp(
|
|
instance->data + instance->data_markup[i].byte_bias,
|
|
instance->data + instance->data_markup[y].byte_bias,
|
|
byte_count - 1) == 0) &&
|
|
(memcmp(
|
|
instance->data + instance->data_markup[i + 1].byte_bias,
|
|
instance->data + instance->data_markup[y + 1].byte_bias,
|
|
byte_count - 1) == 0)) {
|
|
uint8_t index = 0;
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug_tag(
|
|
TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n");
|
|
//output in reverse order
|
|
bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n");
|
|
index = y - 1;
|
|
for(size_t z = instance->data_markup[y].byte_bias + byte_count;
|
|
z < instance->data_markup[i].byte_bias + byte_count;
|
|
z++) {
|
|
if(instance->data_markup[index].byte_bias == z) {
|
|
bin_raw_debug(
|
|
"\r\n\t%d\t%d\t%d :\t",
|
|
index,
|
|
instance->data_markup[index].byte_bias,
|
|
instance->data_markup[index].bit_count);
|
|
if(index != 0) index--;
|
|
}
|
|
bin_raw_debug("%02X ", instance->data[z]);
|
|
}
|
|
|
|
bin_raw_debug("\r\n\r\n");
|
|
#endif
|
|
//todo can be optimized
|
|
BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];
|
|
memcpy(
|
|
markup_temp,
|
|
instance->data_markup,
|
|
BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
memset(
|
|
instance->data_markup,
|
|
0x00,
|
|
BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
|
|
for(index = i; index < y; index++) {
|
|
instance->data_markup[index - i].bit_count =
|
|
markup_temp[y - index - 1].bit_count;
|
|
instance->data_markup[index - i].byte_bias =
|
|
markup_temp[y - index - 1].byte_bias;
|
|
}
|
|
|
|
bin_raw_type = BinRAWTypeGapRolling;
|
|
i = data_markup_ind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//todo can be optimized
|
|
if(bin_raw_type == BinRAWTypeGap) {
|
|
if(data_temp != 0) { //there are sequences with the same number of bits
|
|
|
|
BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT];
|
|
memcpy(
|
|
markup_temp,
|
|
instance->data_markup,
|
|
BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
memset(
|
|
instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp);
|
|
uint16_t index = 0;
|
|
uint16_t it = BIN_RAW_MAX_MARKUP_COUNT;
|
|
do {
|
|
it--;
|
|
if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) ==
|
|
byte_count) {
|
|
instance->data_markup[index].bit_count = markup_temp[it].bit_count;
|
|
instance->data_markup[index].byte_bias = markup_temp[it].byte_bias;
|
|
index++;
|
|
bin_raw_type = BinRAWTypeGapUnknown;
|
|
}
|
|
} while(it != 0);
|
|
}
|
|
}
|
|
|
|
if(bin_raw_type != BinRAWTypeGap)
|
|
return true;
|
|
else
|
|
return false;
|
|
|
|
} else {
|
|
// if bin_raw_type == BinRAWTypeGap
|
|
bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n");
|
|
ind = 0;
|
|
for(size_t i = 0; i < instance->data_raw_ind; i++) {
|
|
int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te));
|
|
if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise
|
|
bin_raw_debug("%d ", data_temp);
|
|
|
|
for(size_t k = 0; k < abs(data_temp); k++) {
|
|
if(data_temp > 0) {
|
|
subghz_protocol_blocks_set_bit_array(
|
|
true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);
|
|
} else {
|
|
subghz_protocol_blocks_set_bit_array(
|
|
false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);
|
|
}
|
|
if(ind == BIN_RAW_BUF_DATA_SIZE * 8) {
|
|
i = instance->data_raw_ind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ind != 0) {
|
|
bin_raw_type = BinRAWTypeNoGap;
|
|
//right alignment
|
|
uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind;
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug(
|
|
"\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n",
|
|
ind,
|
|
subghz_protocol_bin_raw_get_full_byte(ind),
|
|
bit_bias);
|
|
|
|
for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
|
|
bin_raw_debug("%02X ", instance->data[i]);
|
|
}
|
|
bin_raw_debug("\r\n\r\n");
|
|
#endif
|
|
//checking that the received sequence contains useful data
|
|
bool data_check = false;
|
|
for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
|
|
if(instance->data[i] != 0) {
|
|
data_check = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(data_check) {
|
|
for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) {
|
|
instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) |
|
|
(instance->data[i] >> bit_bias);
|
|
}
|
|
instance->data[0] = (instance->data[0] >> bit_bias);
|
|
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug_tag(TAG, "Data right alignment\r\n");
|
|
for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) {
|
|
bin_raw_debug("%02X ", instance->data[i]);
|
|
}
|
|
bin_raw_debug("\r\n\r\n");
|
|
#endif
|
|
instance->data_markup[0].bit_count = ind;
|
|
instance->data_markup[0].byte_bias = 0;
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void subghz_protocol_decoder_bin_raw_data_input_rssi(
|
|
SubGhzProtocolDecoderBinRAW* instance,
|
|
float rssi) {
|
|
furi_assert(instance);
|
|
switch(instance->decoder.parser_step) {
|
|
case BinRAWDecoderStepReset:
|
|
|
|
bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi);
|
|
if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {
|
|
instance->data_raw_ind = 0;
|
|
memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t));
|
|
memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t));
|
|
instance->decoder.parser_step = BinRAWDecoderStepWrite;
|
|
bin_raw_debug_tag(TAG, "RSSI\r\n");
|
|
} else {
|
|
//adaptive noise level adjustment
|
|
instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f;
|
|
}
|
|
break;
|
|
|
|
case BinRAWDecoderStepBufFull:
|
|
case BinRAWDecoderStepWrite:
|
|
#ifdef BIN_RAW_DEBUG
|
|
if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) {
|
|
bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi);
|
|
} else {
|
|
bin_raw_debug("%ld ", (int32_t)rssi);
|
|
}
|
|
#endif
|
|
if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug("\r\n\r\n");
|
|
bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n");
|
|
for(size_t i = 0; i < instance->data_raw_ind; i++) {
|
|
bin_raw_debug("%ld ", instance->data_raw[i]);
|
|
}
|
|
bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind);
|
|
#endif
|
|
instance->decoder.parser_step = BinRAWDecoderStepReset;
|
|
instance->generic.data_count_bit = 0;
|
|
if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) {
|
|
if(subghz_protocol_bin_raw_check_remote_controller(instance)) {
|
|
bin_raw_debug_tag(TAG, "Sequence found\r\n");
|
|
bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data");
|
|
uint16_t i = 0;
|
|
while((i < BIN_RAW_MAX_MARKUP_COUNT) &&
|
|
(instance->data_markup[i].bit_count != 0)) {
|
|
instance->generic.data_count_bit += instance->data_markup[i].bit_count;
|
|
#ifdef BIN_RAW_DEBUG
|
|
bin_raw_debug(
|
|
"\r\n\t%d\t%d\t%d :\t",
|
|
i,
|
|
instance->data_markup[i].byte_bias,
|
|
instance->data_markup[i].bit_count);
|
|
for(uint16_t y = instance->data_markup[i].byte_bias;
|
|
y < instance->data_markup[i].byte_bias +
|
|
subghz_protocol_bin_raw_get_full_byte(
|
|
instance->data_markup[i].bit_count);
|
|
y++) {
|
|
bin_raw_debug("%02X ", instance->data[y]);
|
|
}
|
|
#endif
|
|
i++;
|
|
}
|
|
bin_raw_debug("\r\n");
|
|
if(instance->base.callback)
|
|
instance->base.callback(&instance->base, instance->base.context);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state
|
|
if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) {
|
|
instance->decoder.parser_step = BinRAWDecoderStepReset;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
return subghz_protocol_blocks_add_bytes(
|
|
instance->data + instance->data_markup[0].byte_bias,
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count));
|
|
}
|
|
|
|
bool subghz_protocol_decoder_bin_raw_serialize(
|
|
void* context,
|
|
FlipperFormat* flipper_format,
|
|
SubGhzRadioPreset* preset) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
|
|
bool res = false;
|
|
FuriString* temp_str;
|
|
temp_str = furi_string_alloc();
|
|
do {
|
|
stream_clean(flipper_format_get_raw_stream(flipper_format));
|
|
if(!flipper_format_write_header_cstr(
|
|
flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) {
|
|
FURI_LOG_E(TAG, "Unable to add header");
|
|
break;
|
|
}
|
|
|
|
if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) {
|
|
FURI_LOG_E(TAG, "Unable to add Frequency");
|
|
break;
|
|
}
|
|
|
|
subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
|
|
if(!flipper_format_write_string_cstr(
|
|
flipper_format, "Preset", furi_string_get_cstr(temp_str))) {
|
|
FURI_LOG_E(TAG, "Unable to add Preset");
|
|
break;
|
|
}
|
|
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
|
if(!flipper_format_write_string_cstr(
|
|
flipper_format, "Custom_preset_module", "CC1101")) {
|
|
FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
|
|
break;
|
|
}
|
|
if(!flipper_format_write_hex(
|
|
flipper_format, "Custom_preset_data", preset->data, preset->data_size)) {
|
|
FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
|
|
break;
|
|
}
|
|
}
|
|
if(!flipper_format_write_string_cstr(
|
|
flipper_format, "Protocol", instance->generic.protocol_name)) {
|
|
FURI_LOG_E(TAG, "Unable to add Protocol");
|
|
break;
|
|
}
|
|
|
|
uint32_t temp = instance->generic.data_count_bit;
|
|
if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) {
|
|
FURI_LOG_E(TAG, "Unable to add Bit");
|
|
break;
|
|
}
|
|
|
|
if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) {
|
|
FURI_LOG_E(TAG, "Unable to add TE");
|
|
break;
|
|
}
|
|
|
|
uint16_t i = 0;
|
|
while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) {
|
|
temp = instance->data_markup[i].bit_count;
|
|
if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) {
|
|
FURI_LOG_E(TAG, "Bit_RAW");
|
|
break;
|
|
}
|
|
if(!flipper_format_write_hex(
|
|
flipper_format,
|
|
"Data_RAW",
|
|
instance->data + instance->data_markup[i].byte_bias,
|
|
subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) {
|
|
FURI_LOG_E(TAG, "Unable to add Data_RAW");
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
res = true;
|
|
} while(false);
|
|
furi_string_free(temp_str);
|
|
return res;
|
|
}
|
|
|
|
bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
|
|
bool res = false;
|
|
uint32_t temp_data = 0;
|
|
|
|
do {
|
|
if(!flipper_format_rewind(flipper_format)) {
|
|
FURI_LOG_E(TAG, "Rewind error");
|
|
break;
|
|
}
|
|
if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
|
|
FURI_LOG_E(TAG, "Missing Bit");
|
|
break;
|
|
}
|
|
|
|
instance->generic.data_count_bit = (uint16_t)temp_data;
|
|
|
|
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
|
FURI_LOG_E(TAG, "Missing TE");
|
|
break;
|
|
}
|
|
|
|
temp_data = 0;
|
|
uint16_t ind = 0;
|
|
uint16_t byte_bias = 0;
|
|
uint16_t byte_count = 0;
|
|
memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup));
|
|
while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) {
|
|
if(ind >= BIN_RAW_MAX_MARKUP_COUNT) {
|
|
FURI_LOG_E(TAG, "Markup overflow");
|
|
break;
|
|
}
|
|
byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data);
|
|
if(byte_count > BIN_RAW_BUF_DATA_SIZE) {
|
|
FURI_LOG_E(TAG, "Receive buffer overflow");
|
|
break;
|
|
}
|
|
|
|
instance->data_markup[ind].bit_count = temp_data;
|
|
instance->data_markup[ind].byte_bias = byte_bias;
|
|
byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data);
|
|
|
|
if(!flipper_format_read_hex(
|
|
flipper_format,
|
|
"Data_RAW",
|
|
instance->data + instance->data_markup[ind].byte_bias,
|
|
subghz_protocol_bin_raw_get_full_byte(temp_data))) {
|
|
instance->data_markup[ind].bit_count = 0;
|
|
FURI_LOG_E(TAG, "Missing Data_RAW");
|
|
break;
|
|
}
|
|
ind++;
|
|
}
|
|
|
|
res = true;
|
|
} while(0);
|
|
|
|
return res;
|
|
}
|
|
|
|
void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderBinRAW* instance = context;
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s %dbit\r\n"
|
|
"Key:",
|
|
instance->generic.protocol_name,
|
|
instance->generic.data_count_bit);
|
|
|
|
uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit);
|
|
for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) {
|
|
furi_string_cat_printf(output, "%02X", instance->data[i]);
|
|
}
|
|
|
|
furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te);
|
|
}
|