#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 #include #include #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 = SubGhzProtocolTypeBinRAW, #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; } SubGhzProtocolStatus subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderBinRAW* instance = context; SubGhzProtocolStatus res = SubGhzProtocolStatusError; uint32_t temp_data = 0; do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); res = SubGhzProtocolStatusErrorParserBitCount; 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"); res = SubGhzProtocolStatusErrorParserTe; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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; res = SubGhzProtocolStatusErrorEncoderGetUpload; } instance->encoder.is_running = true; res = SubGhzProtocolStatusOk; } 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 FL-3557: 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 FL-3557: 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)); } SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderBinRAW* instance = context; SubGhzProtocolStatus res = SubGhzProtocolStatusError; 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"); res = SubGhzProtocolStatusErrorParserHeader; break; } if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); res = SubGhzProtocolStatusErrorParserFrequency; 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"); res = SubGhzProtocolStatusErrorParserPreset; 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"); res = SubGhzProtocolStatusErrorParserCustomPreset; 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"); res = SubGhzProtocolStatusErrorParserCustomPreset; break; } } if(!flipper_format_write_string_cstr( flipper_format, "Protocol", instance->generic.protocol_name)) { FURI_LOG_E(TAG, "Unable to add Protocol"); res = SubGhzProtocolStatusErrorParserProtocolName; 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"); res = SubGhzProtocolStatusErrorParserBitCount; break; } if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { FURI_LOG_E(TAG, "Unable to add TE"); res = SubGhzProtocolStatusErrorParserTe; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; break; } i++; } res = SubGhzProtocolStatusOk; } while(false); furi_string_free(temp_str); return res; } SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderBinRAW* instance = context; SubGhzProtocolStatus res = SubGhzProtocolStatusError; uint32_t temp_data = 0; do { if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); res = SubGhzProtocolStatusErrorParserOthers; break; } if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Bit"); res = SubGhzProtocolStatusErrorParserBitCount; 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"); res = SubGhzProtocolStatusErrorParserTe; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; 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"); res = SubGhzProtocolStatusErrorParserOthers; break; } ind++; } res = SubGhzProtocolStatusOk; } 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); }