mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-10-22 16:41:15 +00:00
FAP Build / ufbt: Build for Momentum dev (push) Canceled after 0s
FAP Build / ufbt: Build for Momentum release (push) Canceled after 0s
FAP Build / ufbt: Build for OFW dev channel (push) Canceled after 0s
FAP Build / ufbt: Build for OFW release channel (push) Canceled after 0s
FAP Build / ufbt: Build for Unleashed dev (push) Canceled after 0s
FAP Build / ufbt: Build for Unleashed release (push) Canceled after 0s
570 lines
18 KiB
C
570 lines
18 KiB
C
#include "ford_v3.h"
|
|
#include "../protopirate_app_i.h"
|
|
#include "protocols_common.h"
|
|
#include <string.h>
|
|
|
|
#define FORD_V3_TE_SHORT 240U
|
|
#define FORD_V3_TE_LONG 480U
|
|
#define FORD_V3_TE_DELTA 60U
|
|
#define FORD_V3_CELL_TE_DELTA 120U
|
|
#define FORD_V3_DATA_BITS 104U
|
|
#define FORD_V3_DATA_BYTES 13U
|
|
#define FORD_V3_PREAMBLE_MIN 30U
|
|
#define FORD_V3_CELL_CAP 320U
|
|
#define FORD_V3_CELL_MIN 200U
|
|
#define FORD_V3_CELL_MIN_BITS 100U
|
|
#define FORD_V3_FF_VARIANT "Variant"
|
|
|
|
#define FORD_V3_BTN_LOCK 0x01U
|
|
#define FORD_V3_BTN_UNLOCK 0x02U
|
|
|
|
#define FORD_V3_VARIANT_EU 0U
|
|
#define FORD_V3_VARIANT_US 1U
|
|
|
|
static const SubGhzBlockConst subghz_protocol_ford_v3_const = {
|
|
.te_short = FORD_V3_TE_SHORT,
|
|
.te_long = FORD_V3_TE_LONG,
|
|
.te_delta = FORD_V3_TE_DELTA,
|
|
.min_count_bit_for_found = FORD_V3_DATA_BITS,
|
|
};
|
|
|
|
static const SubGhzBlockConst subghz_protocol_ford_v3_cell_const = {
|
|
.te_short = FORD_V3_TE_SHORT,
|
|
.te_long = FORD_V3_TE_LONG,
|
|
.te_delta = FORD_V3_CELL_TE_DELTA,
|
|
.min_count_bit_for_found = FORD_V3_DATA_BITS,
|
|
};
|
|
|
|
typedef enum {
|
|
FordV3DecoderStepReset = 0,
|
|
FordV3DecoderStepPreamble = 1,
|
|
FordV3DecoderStepData = 2,
|
|
} FordV3DecoderStep;
|
|
|
|
typedef struct SubGhzProtocolDecoderFordV3 {
|
|
SubGhzProtocolDecoderBase base;
|
|
SubGhzBlockDecoder decoder;
|
|
SubGhzBlockGeneric generic;
|
|
|
|
ManchesterState manchester_state;
|
|
uint8_t manchester_raw[FORD_V3_DATA_BYTES];
|
|
uint8_t manchester_bit_count;
|
|
uint16_t preamble_count;
|
|
|
|
uint8_t cells[FORD_V3_CELL_CAP];
|
|
uint16_t cell_count;
|
|
|
|
uint8_t raw_bytes[FORD_V3_DATA_BYTES];
|
|
uint8_t last_raw_bytes[FORD_V3_DATA_BYTES];
|
|
bool last_raw_valid;
|
|
|
|
uint8_t variant;
|
|
uint8_t flag;
|
|
uint32_t serial;
|
|
uint16_t counter;
|
|
} SubGhzProtocolDecoderFordV3;
|
|
|
|
static void ford_v3_reset_manchester(SubGhzProtocolDecoderFordV3* instance);
|
|
static void ford_v3_reset_cells(SubGhzProtocolDecoderFordV3* instance);
|
|
static void ford_v3_add_manchester_bit(SubGhzProtocolDecoderFordV3* instance, bool bit);
|
|
static bool ford_v3_cell_frame_valid(const uint8_t* raw);
|
|
static uint8_t ford_v3_variant_from_saved_or_raw(const uint8_t* raw, uint32_t saved_variant);
|
|
static void ford_v3_parse_fields(SubGhzProtocolDecoderFordV3* instance);
|
|
static bool ford_v3_commit_frame(
|
|
SubGhzProtocolDecoderFordV3* instance,
|
|
const uint8_t* raw,
|
|
uint8_t variant);
|
|
static void ford_v3_manchester_emit_if_ready(SubGhzProtocolDecoderFordV3* instance);
|
|
static void ford_v3_cell_process(SubGhzProtocolDecoderFordV3* instance);
|
|
static void
|
|
ford_v3_cell_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration);
|
|
static void
|
|
ford_v3_manchester_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration);
|
|
static const char* ford_v3_button_name(uint8_t btn, uint8_t variant);
|
|
|
|
static const char* ford_v3_button_name(uint8_t btn, uint8_t variant) {
|
|
if(variant == FORD_V3_VARIANT_US) {
|
|
switch(btn) {
|
|
case FORD_V3_BTN_LOCK:
|
|
return "Lock";
|
|
case FORD_V3_BTN_UNLOCK:
|
|
return "Unlock";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
switch(btn) {
|
|
case FORD_V3_BTN_LOCK:
|
|
return "Lock";
|
|
case FORD_V3_BTN_UNLOCK:
|
|
return "Unlock";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
static bool ford_v3_cell_frame_valid(const uint8_t* raw) {
|
|
if(raw[0] != 0xFFU) {
|
|
return false;
|
|
}
|
|
|
|
const uint32_t serial = ((uint32_t)raw[1] << 24) | ((uint32_t)raw[2] << 16) |
|
|
((uint32_t)raw[3] << 8) | (uint32_t)raw[4];
|
|
if(serial == 0U || serial == 0xFFFFFFFFU) {
|
|
return false;
|
|
}
|
|
|
|
if(raw[6] != FORD_V3_BTN_LOCK && raw[6] != FORD_V3_BTN_UNLOCK) {
|
|
return false;
|
|
}
|
|
|
|
if((raw[5] & 0x80U) == 0U) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint8_t ford_v3_variant_from_saved_or_raw(const uint8_t* raw, uint32_t saved_variant) {
|
|
if(saved_variant == FORD_V3_VARIANT_US) {
|
|
return FORD_V3_VARIANT_US;
|
|
}
|
|
if(saved_variant == FORD_V3_VARIANT_EU) {
|
|
return FORD_V3_VARIANT_EU;
|
|
}
|
|
|
|
return ford_v3_cell_frame_valid(raw) ? FORD_V3_VARIANT_US : FORD_V3_VARIANT_EU;
|
|
}
|
|
|
|
static void ford_v3_reset_manchester(SubGhzProtocolDecoderFordV3* instance) {
|
|
memset(instance->manchester_raw, 0, sizeof(instance->manchester_raw));
|
|
instance->manchester_bit_count = 0;
|
|
instance->preamble_count = 0;
|
|
manchester_advance(
|
|
instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL);
|
|
}
|
|
|
|
static void ford_v3_reset_cells(SubGhzProtocolDecoderFordV3* instance) {
|
|
instance->cell_count = 0;
|
|
}
|
|
|
|
static void ford_v3_add_manchester_bit(SubGhzProtocolDecoderFordV3* instance, bool bit) {
|
|
if(instance->manchester_bit_count >= FORD_V3_DATA_BITS) {
|
|
return;
|
|
}
|
|
|
|
const uint8_t byte_index = instance->manchester_bit_count / 8U;
|
|
const uint8_t bit_in_byte = 7U - (instance->manchester_bit_count % 8U);
|
|
if(bit) {
|
|
instance->manchester_raw[byte_index] |= (uint8_t)(1U << bit_in_byte);
|
|
}
|
|
instance->manchester_bit_count++;
|
|
}
|
|
|
|
static void ford_v3_parse_fields(SubGhzProtocolDecoderFordV3* instance) {
|
|
const uint8_t* b = instance->raw_bytes;
|
|
|
|
instance->serial = ((uint32_t)b[1] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 8) |
|
|
(uint32_t)b[4];
|
|
instance->generic.serial = instance->serial;
|
|
|
|
if(instance->variant == FORD_V3_VARIANT_US) {
|
|
instance->flag = b[5];
|
|
instance->counter = (uint16_t)(((uint16_t)b[7] << 8) | (uint16_t)b[8]);
|
|
instance->generic.btn = b[6];
|
|
} else {
|
|
instance->flag = 0;
|
|
instance->counter = (uint16_t)((((uint16_t)(uint8_t)~b[7]) << 8) | (uint8_t)~b[8]);
|
|
instance->generic.btn = (b[6] & 0x01U) ? FORD_V3_BTN_UNLOCK : FORD_V3_BTN_LOCK;
|
|
}
|
|
|
|
instance->generic.cnt = instance->counter;
|
|
}
|
|
|
|
static bool ford_v3_commit_frame(
|
|
SubGhzProtocolDecoderFordV3* instance,
|
|
const uint8_t* raw,
|
|
uint8_t variant) {
|
|
if(instance->last_raw_valid &&
|
|
memcmp(instance->last_raw_bytes, raw, FORD_V3_DATA_BYTES) == 0) {
|
|
return true;
|
|
}
|
|
|
|
memcpy(instance->raw_bytes, raw, FORD_V3_DATA_BYTES);
|
|
memcpy(instance->last_raw_bytes, raw, FORD_V3_DATA_BYTES);
|
|
instance->last_raw_valid = true;
|
|
|
|
instance->variant = variant;
|
|
instance->generic.data_count_bit = FORD_V3_DATA_BITS;
|
|
ford_v3_parse_fields(instance);
|
|
|
|
if(instance->base.callback) {
|
|
instance->base.callback(&instance->base, instance->base.context);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ford_v3_manchester_emit_if_ready(SubGhzProtocolDecoderFordV3* instance) {
|
|
if(instance->manchester_bit_count < FORD_V3_DATA_BITS) {
|
|
return;
|
|
}
|
|
|
|
(void)ford_v3_commit_frame(instance, instance->manchester_raw, FORD_V3_VARIANT_EU);
|
|
}
|
|
|
|
static bool ford_v3_cell_decode(const uint8_t* cells, uint16_t cell_count, uint8_t* raw_out) {
|
|
for(int phase = 0; phase < 2; phase++) {
|
|
uint8_t frame[FORD_V3_DATA_BYTES];
|
|
memset(frame, 0, sizeof(frame));
|
|
|
|
int bit_count = 0;
|
|
bool ok = true;
|
|
for(int i = phase; (i + 1) < (int)cell_count && bit_count < (int)FORD_V3_DATA_BITS;
|
|
i += 2) {
|
|
const uint8_t first = cells[i];
|
|
const uint8_t second = cells[i + 1];
|
|
if(first == second) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if(first) {
|
|
frame[bit_count >> 3] |= (uint8_t)(1U << (7 - (bit_count & 7)));
|
|
}
|
|
bit_count++;
|
|
}
|
|
|
|
if(!ok || bit_count < (int)FORD_V3_CELL_MIN_BITS) {
|
|
continue;
|
|
}
|
|
if(!ford_v3_cell_frame_valid(frame)) {
|
|
continue;
|
|
}
|
|
|
|
memcpy(raw_out, frame, FORD_V3_DATA_BYTES);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void ford_v3_cell_process(SubGhzProtocolDecoderFordV3* instance) {
|
|
if(instance->cell_count < FORD_V3_CELL_MIN) {
|
|
return;
|
|
}
|
|
|
|
uint8_t raw[FORD_V3_DATA_BYTES];
|
|
if(!ford_v3_cell_decode(instance->cells, instance->cell_count, raw)) {
|
|
return;
|
|
}
|
|
|
|
(void)ford_v3_commit_frame(instance, raw, FORD_V3_VARIANT_US);
|
|
}
|
|
|
|
static void
|
|
ford_v3_cell_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration) {
|
|
if(pp_is_short(duration, &subghz_protocol_ford_v3_cell_const)) {
|
|
if(instance->cell_count < FORD_V3_CELL_CAP) {
|
|
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
|
}
|
|
} else if(pp_is_long(duration, &subghz_protocol_ford_v3_cell_const)) {
|
|
if(instance->cell_count + 2U <= FORD_V3_CELL_CAP) {
|
|
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
|
instance->cells[instance->cell_count++] = level ? 1U : 0U;
|
|
}
|
|
} else {
|
|
ford_v3_cell_process(instance);
|
|
instance->cell_count = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ford_v3_manchester_feed(SubGhzProtocolDecoderFordV3* instance, bool level, uint32_t duration) {
|
|
switch(instance->decoder.parser_step) {
|
|
case FordV3DecoderStepReset:
|
|
if(pp_is_short(duration, &subghz_protocol_ford_v3_const)) {
|
|
ford_v3_reset_manchester(instance);
|
|
instance->preamble_count = 1U;
|
|
instance->decoder.parser_step = FordV3DecoderStepPreamble;
|
|
}
|
|
break;
|
|
|
|
case FordV3DecoderStepPreamble:
|
|
if(pp_is_short(duration, &subghz_protocol_ford_v3_const)) {
|
|
instance->preamble_count++;
|
|
} else if(
|
|
instance->preamble_count >= FORD_V3_PREAMBLE_MIN &&
|
|
pp_is_long(duration, &subghz_protocol_ford_v3_const)) {
|
|
instance->manchester_state = ManchesterStateMid1;
|
|
|
|
const ManchesterEvent event = level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
|
|
|
bool data_bit = false;
|
|
const bool valid = manchester_advance(
|
|
instance->manchester_state, event, &instance->manchester_state, &data_bit);
|
|
if(valid) {
|
|
ford_v3_add_manchester_bit(instance, data_bit);
|
|
}
|
|
instance->decoder.parser_step = FordV3DecoderStepData;
|
|
} else {
|
|
instance->decoder.parser_step = FordV3DecoderStepReset;
|
|
}
|
|
break;
|
|
|
|
case FordV3DecoderStepData:
|
|
if(!pp_is_short(duration, &subghz_protocol_ford_v3_const) &&
|
|
!pp_is_long(duration, &subghz_protocol_ford_v3_const)) {
|
|
ford_v3_manchester_emit_if_ready(instance);
|
|
instance->decoder.parser_step = FordV3DecoderStepReset;
|
|
|
|
if(pp_is_short(duration, &subghz_protocol_ford_v3_const)) {
|
|
ford_v3_reset_manchester(instance);
|
|
instance->preamble_count = 1U;
|
|
instance->decoder.parser_step = FordV3DecoderStepPreamble;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ManchesterEvent event;
|
|
if(level) {
|
|
event = pp_is_short(duration, &subghz_protocol_ford_v3_const) ?
|
|
ManchesterEventShortHigh :
|
|
ManchesterEventLongHigh;
|
|
} else {
|
|
event = pp_is_short(duration, &subghz_protocol_ford_v3_const) ?
|
|
ManchesterEventShortLow :
|
|
ManchesterEventLongLow;
|
|
}
|
|
|
|
bool data_bit = false;
|
|
const bool valid = manchester_advance(
|
|
instance->manchester_state, event, &instance->manchester_state, &data_bit);
|
|
|
|
if(valid) {
|
|
ford_v3_add_manchester_bit(instance, data_bit);
|
|
if(instance->manchester_bit_count >= FORD_V3_DATA_BITS) {
|
|
ford_v3_manchester_emit_if_ready(instance);
|
|
instance->decoder.parser_step = FordV3DecoderStepReset;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void* subghz_protocol_decoder_ford_v3_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = calloc(1, sizeof(SubGhzProtocolDecoderFordV3));
|
|
furi_check(instance);
|
|
|
|
instance->base.protocol = &ford_protocol_v3;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_decoder_ford_v3_reset(void* context) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
instance->decoder.parser_step = FordV3DecoderStepReset;
|
|
ford_v3_reset_manchester(instance);
|
|
ford_v3_reset_cells(instance);
|
|
instance->last_raw_valid = false;
|
|
}
|
|
|
|
void subghz_protocol_decoder_ford_v3_feed(void* context, bool level, uint32_t duration) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
ford_v3_cell_feed(instance, level, duration);
|
|
ford_v3_manchester_feed(instance, level, duration);
|
|
}
|
|
|
|
uint8_t subghz_protocol_decoder_ford_v3_get_hash_data(void* context) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
uint8_t hash = 0;
|
|
|
|
for(size_t i = 0; i < FORD_V3_DATA_BYTES; i++) {
|
|
hash ^= instance->raw_bytes[i];
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
SubGhzProtocolStatus subghz_protocol_decoder_ford_v3_serialize(
|
|
void* context,
|
|
FlipperFormat* flipper_format,
|
|
SubGhzRadioPreset* preset) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
|
|
instance->generic.data =
|
|
((uint64_t)instance->raw_bytes[0] << 56) | ((uint64_t)instance->raw_bytes[1] << 48) |
|
|
((uint64_t)instance->raw_bytes[2] << 40) | ((uint64_t)instance->raw_bytes[3] << 32) |
|
|
((uint64_t)instance->raw_bytes[4] << 24) | ((uint64_t)instance->raw_bytes[5] << 16) |
|
|
((uint64_t)instance->raw_bytes[6] << 8) | (uint64_t)instance->raw_bytes[7];
|
|
instance->generic.data_count_bit = FORD_V3_DATA_BITS;
|
|
|
|
SubGhzProtocolStatus ret =
|
|
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
|
|
|
if(ret == SubGhzProtocolStatusOk) {
|
|
flipper_format_rewind(flipper_format);
|
|
flipper_format_insert_or_update_hex(
|
|
flipper_format, "Raw", instance->raw_bytes, FORD_V3_DATA_BYTES);
|
|
|
|
pp_flipper_update_or_insert_u32(flipper_format, FF_SERIAL, instance->generic.serial);
|
|
pp_flipper_update_or_insert_u32(flipper_format, FF_BTN, instance->generic.btn);
|
|
pp_flipper_update_or_insert_u32(flipper_format, FF_CNT, instance->counter);
|
|
pp_flipper_update_or_insert_u32(flipper_format, FORD_V3_FF_VARIANT, instance->variant);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
subghz_protocol_decoder_ford_v3_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
|
|
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic, flipper_format, subghz_protocol_ford_v3_const.min_count_bit_for_found);
|
|
|
|
if(ret != SubGhzProtocolStatusOk) {
|
|
return ret;
|
|
}
|
|
|
|
const uint64_t d = instance->generic.data;
|
|
for(uint8_t i = 0; i < 8U; i++) {
|
|
instance->raw_bytes[i] = (uint8_t)(d >> (56 - i * 8));
|
|
}
|
|
memset(&instance->raw_bytes[8], 0, FORD_V3_DATA_BYTES - 8U);
|
|
|
|
flipper_format_rewind(flipper_format);
|
|
flipper_format_read_hex(flipper_format, "Raw", instance->raw_bytes, FORD_V3_DATA_BYTES);
|
|
|
|
uint32_t variant = UINT32_MAX;
|
|
if(!flipper_format_read_uint32(flipper_format, FORD_V3_FF_VARIANT, &variant, 1)) {
|
|
variant = UINT32_MAX;
|
|
}
|
|
instance->variant = ford_v3_variant_from_saved_or_raw(instance->raw_bytes, variant);
|
|
|
|
instance->manchester_bit_count = FORD_V3_DATA_BITS;
|
|
ford_v3_parse_fields(instance);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void subghz_protocol_decoder_ford_v3_get_string(void* context, FuriString* output) {
|
|
furi_check(context);
|
|
|
|
SubGhzProtocolDecoderFordV3* instance = context;
|
|
const uint8_t* k = instance->raw_bytes;
|
|
|
|
if(instance->variant == FORD_V3_VARIANT_US) {
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s US %dbit\r\n"
|
|
"Key:%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\r\n"
|
|
"Sn:%08lX Btn:%02X %s\r\n"
|
|
"Cnt:%04X Hop:%02X%02X%02X%02X\r\n",
|
|
instance->generic.protocol_name,
|
|
(int)instance->generic.data_count_bit,
|
|
k[0],
|
|
k[1],
|
|
k[2],
|
|
k[3],
|
|
k[4],
|
|
k[5],
|
|
k[6],
|
|
k[7],
|
|
k[8],
|
|
k[9],
|
|
k[10],
|
|
k[11],
|
|
k[12],
|
|
(unsigned long)instance->generic.serial,
|
|
instance->generic.btn,
|
|
ford_v3_button_name(instance->generic.btn, FORD_V3_VARIANT_US),
|
|
(unsigned)instance->counter,
|
|
k[9],
|
|
k[10],
|
|
k[11],
|
|
k[12]);
|
|
return;
|
|
}
|
|
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s %dbit\r\n"
|
|
"Key:%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\r\n"
|
|
"Sn:%08lX Btn:%02X %s\r\n"
|
|
"Cnt:%04X Hop:%02X%02X%02X%02X\r\n",
|
|
instance->generic.protocol_name,
|
|
(int)instance->generic.data_count_bit,
|
|
k[0],
|
|
k[1],
|
|
k[2],
|
|
k[3],
|
|
k[4],
|
|
k[5],
|
|
k[6],
|
|
k[7],
|
|
k[8],
|
|
k[9],
|
|
k[10],
|
|
k[11],
|
|
k[12],
|
|
(unsigned long)instance->generic.serial,
|
|
instance->generic.btn,
|
|
ford_v3_button_name(instance->generic.btn, FORD_V3_VARIANT_EU),
|
|
(unsigned)instance->counter,
|
|
k[9],
|
|
k[10],
|
|
k[11],
|
|
k[12]);
|
|
}
|
|
|
|
const SubGhzProtocolDecoder subghz_protocol_ford_v3_decoder = {
|
|
.alloc = subghz_protocol_decoder_ford_v3_alloc,
|
|
.free = pp_decoder_free_default,
|
|
.feed = subghz_protocol_decoder_ford_v3_feed,
|
|
.reset = subghz_protocol_decoder_ford_v3_reset,
|
|
.get_hash_data = subghz_protocol_decoder_ford_v3_get_hash_data,
|
|
.serialize = subghz_protocol_decoder_ford_v3_serialize,
|
|
.deserialize = subghz_protocol_decoder_ford_v3_deserialize,
|
|
.get_string = subghz_protocol_decoder_ford_v3_get_string,
|
|
};
|
|
|
|
const SubGhzProtocolEncoder subghz_protocol_ford_v3_encoder = {
|
|
.alloc = NULL,
|
|
.free = NULL,
|
|
.deserialize = NULL,
|
|
.stop = NULL,
|
|
.yield = NULL,
|
|
};
|
|
|
|
const SubGhzProtocol ford_protocol_v3 = {
|
|
.name = FORD_PROTOCOL_V3_NAME,
|
|
.type = SubGhzProtocolTypeDynamic,
|
|
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
|
|
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
|
SubGhzProtocolFlag_Save,
|
|
#if PROTOPIRATE_WITH_DECODER
|
|
.decoder = &subghz_protocol_ford_v3_decoder,
|
|
#else
|
|
.decoder = NULL,
|
|
#endif
|
|
#if PROTOPIRATE_WITH_ENCODER
|
|
.encoder = &subghz_protocol_ford_v3_encoder,
|
|
#else
|
|
.encoder = NULL,
|
|
#endif
|
|
};
|