#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>
 
#include <math.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;
}
 
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)) {
            res = SubGhzProtocolStatusErrorEncoderGetUpload;
            break;
        }
 
        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]), (int32_t)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)(roundf((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 < (size_t)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]), (int32_t)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
                            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;
                        }
                    }
                }
            }
        }
        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)(roundf((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 < (size_t)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_check(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);
}

V784 The size of the bit mask is less than the size of the first operand. This will cause the loss of higher bits.

V1044 Loop break conditions do not depend on the number of iterations.