Compare commits

...

8 Commits

Author SHA1 Message Date
Andrea Santaniello
32a96e580d Update psa.c
All checks were successful
Build Dev Firmware / build (push) Successful in 7m5s
2026-04-17 17:34:55 +02:00
Andrea Santaniello
54f03a39c2 Update psa.c
All checks were successful
Build Dev Firmware / build (push) Successful in 7m3s
2026-04-17 16:36:37 +02:00
d4rks1d33
a55189e2a4 some updates
All checks were successful
Build Dev Firmware / build (push) Successful in 7m9s
2026-04-16 20:02:54 -03:00
Andrea Santaniello
14d10c0794 Merge branch 'main' of https://github.com/D4C1-Labs/Flipper-ARF
All checks were successful
Build Dev Firmware / build (push) Successful in 6m42s
2026-04-07 13:22:35 +02:00
Andrea Santaniello
27818ccb1f Re-enabling GangQi / Disabling Honda until fix. 2026-04-07 13:22:32 +02:00
d4rks1d33
0ebf26eff4 Update Honda, should be less false positives
All checks were successful
Build Dev Firmware / build (push) Successful in 6m32s
2026-04-05 20:12:04 -03:00
Andrea Santaniello
ac620e2b0e Fix rolling increment on upload
All checks were successful
Build Dev Firmware / build (push) Successful in 6m30s
2026-04-05 18:42:44 +02:00
Andrea Santaniello
46115cdf6c Updated Readme and set flag for 315Mhz 2026-04-05 18:40:05 +02:00
9 changed files with 2583 additions and 619 deletions

View File

@@ -66,6 +66,7 @@ This project may incorporate, adapt, or build upon **other open-source projects*
| Scher-Khan | Scher-Khan | 433 MHz | FM | Yes | Yes | No |
| Scher-Khan | Magic Code PRO1/PRO2 | 433 MHz | FM | Yes | Yes | Yes |
| Sheriff | Sheriff CFM (ZX-750/930) | 433 MHz | AM | Yes | Yes | No |
| Chrysler/Dodge/Jeep | FOBIK GQ43VT | 315/433 MHz | AM | Yes | Yes | No |
### Gate / Access Protocols

View File

@@ -97,7 +97,7 @@ const SubGhzProtocolEncoder subghz_protocol_chrysler_encoder = {
const SubGhzProtocol subghz_protocol_chrysler = {
.name = CHRYSLER_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_chrysler_decoder,
@@ -333,6 +333,9 @@ LevelDuration subghz_protocol_encoder_chrysler_yield(void* context) {
instance->encoder.repeat--;
}
instance->encoder.front = 0;
chrysler_advance_rolling(instance->raw_data);
chrysler_encoder_rebuild(instance);
chrysler_encoder_get_upload(instance);
}
return ret;

File diff suppressed because it is too large Load Diff

View File

@@ -7,27 +7,56 @@
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
/* ── Protocol name ─────────────────────────────────────────────────────── */
#define SUBGHZ_PROTOCOL_HONDA_NAME "Honda"
#define HONDA_TE_SHORT 63u
#define HONDA_TE_LONG 126u
#define HONDA_TE_DELTA 35u
#define HONDA_GUARD_TIME_US 700u
#define HONDA_TE_SHORT 250u
#define HONDA_TE_LONG 480u
#define HONDA_TE_DELTA 62u
#define HONDA_MIN_PREAMBLE_COUNT 20u
#define HONDA_PREAMBLE_CYCLES 312u
/* ── FSK adaptive decoder parameters ──────────────────────────────────── */
#define HONDA_FRAME_BITS 64u
#define HONDA_FRAME_BITS_B 68u
#define HONDA_MIN_BITS HONDA_FRAME_BITS
#define HONDA_FSK_DUR_MIN_US 35u
#define HONDA_FSK_DUR_MAX_US 2000u
#define HONDA_BTN_LOCK 0x01u
#define HONDA_BTN_UNLOCK 0x02u
#define HONDA_BTN_TRUNK 0x04u
#define HONDA_BTN_PANIC 0x08u
#define HONDA_BTN_RSTART 0x05u
#define HONDA_BTN_LOCK2PRESS 0x09u
//#define HONDA_FSK_GAP_US 3000u
#define HONDA_FSK_GAP_US 400u
#define HONDA_FSK_COLLECT_N 32u
#define HONDA_FSK_TOL_PCT 40u
#define HONDA_RAW_EDGE_BUF 512u
#define HONDA_MAN_BIT_BUF 256u
#define HONDA_FSK_MIN_PREAMBLE_BITS 16u
#define HONDA_FRAME_BITS_M1 88u
#define HONDA_FRAME_BITS_M2 66u
#define HONDA_MIN_BITS HONDA_FRAME_BITS_M2
/* ── Mode 2 OOK parameters ────────────────────── */
#define HONDA_TE_M2 400u
#define HONDA_TE_M2_DELTA 100u
#define HONDA_MIN_PREAMBLE_PULSES_M1 280u
#define HONDA_MIN_PREAMBLE_PULSES_M2 18u
#define HONDA_PREAMBLE_CYCLES_M1 312u
#define HONDA_PREAMBLE_CYCLES_M2 23u
#define HONDA_GAP_M1_MIN_US 500u
#define HONDA_GAP_M1_MAX_US 1100u
#define HONDA_GAP_M2_MIN_US 3000u
#define HONDA_GAP_M2_MAX_US 5500u
#define HONDA_GUARD_TIME_US 1000u
/* ── Button codes ──────────────────────────────────────────────────────── */
#define HONDA_BTN_LOCK 0x01u
#define HONDA_BTN_UNLOCK 0x02u
#define HONDA_BTN_TRUNK 0x04u
#define HONDA_BTN_PANIC 0x08u
#define HONDA_BTN_RSTART 0x05u
#define HONDA_BTN_LOCK2PRESS 0x09u
#define HONDA_CUSTOM_BTN_MAX 5u
/* ── Lookup tables ─────────────────────────────────────────── */
#define HONDA_TABLE_A \
{0x02,0x06,0x00,0x04,0x0B,0x0F,0x09,0x0D,0x06,0x02,0x04,0x00,0x0F,0x0B,0x0D,0x09}, \
{0x08,0x0C,0x0A,0x0E,0x01,0x05,0x03,0x07,0x0C,0x08,0x0E,0x0A,0x05,0x01,0x07,0x03}, \
@@ -118,6 +147,7 @@
{0x08,0x09,0x0C,0x0D,0x02,0x0A,0x06,0x07,0x0D,0x0C,0x09,0x08,0x07,0x06,0x03,0x02}, \
{0x06,0x07,0x02,0x03,0x0C,0x0D,0x08,0x09,0x03,0x02,0x07,0x06,0x09,0x08,0x0D,0x0C}
/* ── CC1101 FSK preset ──────────────────────────────────────────────────── */
#define HONDA_CC1101_PRESET_DATA \
0x02, 0x0D, \
0x0B, 0x06, \
@@ -140,16 +170,15 @@
0x00, 0x00, \
0xC0, 0x00
#define HONDA_CUSTOM_BTN_MAX 5
/* ── External declarations ──────────────────────────────────────────────── */
extern const SubGhzProtocolDecoder subghz_protocol_honda_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_honda_encoder;
extern const SubGhzProtocol subghz_protocol_honda;
extern const SubGhzProtocol subghz_protocol_honda;
void* subghz_protocol_decoder_honda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_honda_free(void* context);
void subghz_protocol_decoder_honda_reset(void* context);
void subghz_protocol_decoder_honda_feed(void* context, bool level, uint32_t duration);
void* subghz_protocol_decoder_honda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_honda_free(void* context);
void subghz_protocol_decoder_honda_reset(void* context);
void subghz_protocol_decoder_honda_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_honda_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_honda_serialize(
void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset);
@@ -157,9 +186,9 @@ SubGhzProtocolStatus subghz_protocol_decoder_honda_deserialize(
void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_honda_get_string(void* context, FuriString* output);
void* subghz_protocol_encoder_honda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_honda_free(void* context);
void subghz_protocol_encoder_honda_stop(void* context);
void* subghz_protocol_encoder_honda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_honda_free(void* context);
void subghz_protocol_encoder_honda_stop(void* context);
LevelDuration subghz_protocol_encoder_honda_yield(void* context);
SubGhzProtocolStatus subghz_protocol_encoder_honda_deserialize(
void* context, FlipperFormat* flipper_format);

View File

@@ -44,7 +44,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
//&subghz_protocol_honeywell,
//&subghz_protocol_legrand,
&subghz_protocol_dickert_mahs,
//&subghz_protocol_gangqi,
&subghz_protocol_gangqi,
&subghz_protocol_marantec24,
//&subghz_protocol_hollarm,
&subghz_protocol_hay21,
@@ -76,8 +76,9 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&subghz_protocol_star_line,
&subghz_protocol_scher_khan,
&subghz_protocol_sheriff_cfm,
&subghz_protocol_honda,
// until fix &subghz_protocol_honda,
&subghz_protocol_chrysler,
&subghz_protocol_psa2,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {

View File

@@ -80,3 +80,4 @@
#include "sheriff_cfm.h"
#include "honda.h"
#include "chrysler.h"
#include "psa2.h"

View File

@@ -125,6 +125,10 @@ struct SubGhzProtocolDecoderPSA {
uint32_t last_key1_low;
uint32_t last_key1_high;
uint32_t te_sum;
uint16_t te_count;
uint32_t te_detected;
};
struct SubGhzProtocolEncoderPSA {
@@ -670,6 +674,9 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
instance->pattern_counter = 0;
instance->decode_count_bit = 0;
instance->mode_serialize = 0;
instance->te_sum = duration;
instance->te_count = 1;
instance->te_detected = 0;
instance->prev_duration = duration;
manchester_advance(instance->manchester_state, ManchesterEventReset,
&instance->manchester_state, NULL);
@@ -935,39 +942,35 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
return;
}
if(duration < PSA_TE_SHORT_125) {
tolerance = PSA_TE_SHORT_125 - duration;
if(tolerance < PSA_TOLERANCE_50) {
uint32_t prev_diff = psa_abs_diff(prev_dur, PSA_TE_SHORT_125);
if(prev_diff <= PSA_TOLERANCE_49) {
instance->pattern_counter++;
} else {
instance->pattern_counter = 0;
}
instance->prev_duration = duration;
return;
// Adaptive AM preamble: accept 76-174us, average to detect actual TE
if(duration >= 76 && duration <= 174) {
if(prev_dur >= 76 && prev_dur <= 174) {
instance->pattern_counter++;
instance->te_sum += duration;
instance->te_count++;
} else {
instance->pattern_counter = 0;
instance->te_sum = duration;
instance->te_count = 1;
}
instance->prev_duration = duration;
return;
} else {
tolerance = duration - PSA_TE_SHORT_125;
if(tolerance < PSA_TOLERANCE_50) {
uint32_t prev_diff = psa_abs_diff(prev_dur, PSA_TE_SHORT_125);
if(prev_diff <= PSA_TOLERANCE_49) {
instance->pattern_counter++;
} else {
instance->pattern_counter = 0;
}
instance->prev_duration = duration;
return;
} else if(duration >= PSA_TE_LONG_250 && duration < 0x12c) {
if(instance->pattern_counter > PSA_PATTERN_THRESHOLD_2) {
new_state = PSADecoderState4;
instance->decode_data_low = 0;
instance->decode_data_high = 0;
instance->decode_count_bit = 0;
manchester_advance(instance->manchester_state, ManchesterEventReset,
&instance->manchester_state, NULL);
instance->state = new_state;
}
// Check if this is the preamble-to-data transition (2x detected TE)
uint32_t te_avg = (instance->te_count > 0) ?
(instance->te_sum / instance->te_count) : PSA_TE_SHORT_125;
uint32_t te_long_expected = te_avg * 2;
uint32_t long_diff = psa_abs_diff(duration, te_long_expected);
if(long_diff <= te_avg && instance->pattern_counter > PSA_PATTERN_THRESHOLD_2) {
instance->te_detected = te_avg;
new_state = PSADecoderState4;
instance->decode_data_low = 0;
instance->decode_data_high = 0;
instance->decode_count_bit = 0;
manchester_advance(instance->manchester_state, ManchesterEventReset,
&instance->manchester_state, NULL);
instance->state = new_state;
instance->pattern_counter = 0;
instance->prev_duration = duration;
return;
@@ -975,67 +978,25 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
}
new_state = PSADecoderState0;
instance->pattern_counter = 0;
break;
case PSADecoderState4:
case PSADecoderState4: {
if(instance->decode_count_bit >= PSA_MAX_BITS) {
new_state = PSADecoderState0;
break;
}
if(!level) {
uint8_t manchester_input;
bool decoded_bit = false;
if(duration < PSA_TE_SHORT_125) {
tolerance = PSA_TE_SHORT_125 - duration;
if(tolerance > PSA_TOLERANCE_49) {
return;
}
manchester_input = ((level ^ 1) & 0x7f) << 1;
} else {
tolerance = duration - PSA_TE_SHORT_125;
if(tolerance < PSA_TOLERANCE_50) {
manchester_input = ((level ^ 1) & 0x7f) << 1;
} else if(duration >= PSA_TE_LONG_250 && duration < 0x12c) {
if(level == 0) {
manchester_input = 6;
} else {
manchester_input = 4;
}
} else {
return;
}
}
if(manchester_advance(instance->manchester_state,
(ManchesterEvent)manchester_input,
&instance->manchester_state,
&decoded_bit)) {
uint32_t carry = (instance->decode_data_low >> 31) & 1;
instance->decode_data_low = (instance->decode_data_low << 1) | (decoded_bit ? 1 : 0);
instance->decode_data_high = (instance->decode_data_high << 1) | carry;
instance->decode_count_bit++;
if(instance->decode_count_bit == PSA_KEY1_BITS) {
instance->key1_low = instance->decode_data_low;
instance->key1_high = instance->decode_data_high;
instance->decode_data_low = 0;
instance->decode_data_high = 0;
}
}
} else if(level) {
uint32_t end_diff;
if(duration < PSA_TE_END_500) {
end_diff = PSA_TE_END_500 - duration;
} else {
end_diff = duration - PSA_TE_END_500;
}
if(end_diff <= 99) {
if(instance->decode_count_bit != PSA_KEY2_BITS) {
return;
}
uint32_t te_s = instance->te_detected ? instance->te_detected : PSA_TE_SHORT_125;
uint32_t te_l = te_s * 2;
uint32_t te_tol = te_s / 2;
uint32_t midpoint = (te_s + te_l) / 2;
// End marker check: HIGH pulse beyond long range at 80 bits
if(level && instance->decode_count_bit == PSA_KEY2_BITS && duration > midpoint) {
uint32_t end_expected = te_s * 4;
uint32_t end_diff = psa_abs_diff(duration, end_expected);
if(end_diff <= te_s * 2) {
instance->validation_field = (uint16_t)(instance->decode_data_low & 0xFFFF);
instance->key2_low = instance->decode_data_low;
instance->key2_high = instance->decode_data_high;
@@ -1052,7 +1013,6 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
instance->mode_serialize = 0x36;
}
// Only fire callback if decrypted or validation nibble matches
if(instance->decrypted != 0x50 &&
(instance->validation_field & 0xf) != 0xa) {
instance->decode_data_low = 0;
@@ -1076,12 +1036,56 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
new_state = PSADecoderState0;
instance->state = new_state;
return;
} else {
}
}
// Manchester decode: process BOTH high and low pulses (unlike original AM path)
if(duration > te_l + te_tol) {
if(duration > 10000) {
new_state = PSADecoderState0;
break;
}
return;
}
uint8_t manchester_input;
bool decoded_bit = false;
if(duration <= midpoint) {
if(psa_abs_diff(duration, te_s) > te_tol) {
return;
}
manchester_input = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
} else {
if(psa_abs_diff(duration, te_l) > te_tol) {
return;
}
manchester_input = level ? ManchesterEventLongLow : ManchesterEventLongHigh;
}
if(instance->decode_count_bit < PSA_KEY2_BITS) {
if(manchester_advance(instance->manchester_state,
(ManchesterEvent)manchester_input,
&instance->manchester_state,
&decoded_bit)) {
uint32_t carry = (instance->decode_data_low >> 31) & 1;
// PSA AM uses inverted Manchester convention
decoded_bit = !decoded_bit;
instance->decode_data_low = (instance->decode_data_low << 1) | (decoded_bit ? 1 : 0);
instance->decode_data_high = (instance->decode_data_high << 1) | carry;
instance->decode_count_bit++;
if(instance->decode_count_bit == PSA_KEY1_BITS) {
instance->key1_low = instance->decode_data_low;
instance->key1_high = instance->decode_data_high;
instance->decode_data_low = 0;
instance->decode_data_high = 0;
}
}
}
break;
}
}
instance->state = new_state;
instance->prev_duration = duration;

1538
lib/subghz/protocols/psa2.c Normal file

File diff suppressed because it is too large Load Diff

160
lib/subghz/protocols/psa2.h Normal file
View File

@@ -0,0 +1,160 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#ifdef __cplusplus
extern "C" {
#endif
/* =========================================================
* PROTOCOL NAME
* ========================================================= */
#define SUBGHZ_PROTOCOL_PSA2_NAME "PSA OLD"
/* =========================================================
* FORWARD DECLARATIONS — opaque handles
* ========================================================= */
typedef struct SubGhzProtocolDecoderPSA SubGhzProtocolDecoderPSA;
typedef struct SubGhzProtocolEncoderPSA SubGhzProtocolEncoderPSA;
/* =========================================================
* PROTOCOL DESCRIPTORS — exported singletons
* ========================================================= */
extern const SubGhzProtocolDecoder subghz_protocol_psa_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_psa_encoder;
extern const SubGhzProtocol subghz_protocol_psa2;
/* =========================================================
* DECODER API
* ========================================================= */
/**
* Allocate a PSA decoder instance.
*
* @param environment SubGHz environment (may be NULL / unused)
* @return Opaque pointer to SubGhzProtocolDecoderPSA
*/
void* subghz_protocol_decoder_psa2_alloc(SubGhzEnvironment* environment);
/**
* Free a PSA decoder instance.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
*/
void subghz_protocol_decoder_psa2_free(void* context);
/**
* Reset the decoder state machine to its initial state.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
*/
void subghz_protocol_decoder_psa2_reset(void* context);
/**
* Feed one pulse/gap sample into the decoder state machine.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
* @param level true = RF high (pulse), false = RF low (gap)
* @param duration Duration of this level in microseconds
*/
void subghz_protocol_decoder_psa2_feed(void* context, bool level, uint32_t duration);
/**
* Return a one-byte hash of the most recently decoded packet.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
* @return Hash byte
*/
uint8_t subghz_protocol_decoder_psa2_get_hash_data(void* context);
/**
* Serialize the most recently decoded packet into a FlipperFormat stream.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
* @param ff Open FlipperFormat file handle
* @param preset Radio preset in use
* @return SubGhzProtocolStatusOk on success
*/
SubGhzProtocolStatus subghz_protocol_decoder_psa2_serialize(
void* context,
FlipperFormat* ff,
SubGhzRadioPreset* preset);
/**
* Deserialize a previously saved packet from a FlipperFormat stream
* into the decoder instance.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
* @param ff Open FlipperFormat file handle (positioned at start)
* @return SubGhzProtocolStatusOk on success
*/
SubGhzProtocolStatus subghz_protocol_decoder_psa2_deserialize(
void* context,
FlipperFormat* ff);
/**
* Build a human-readable description of the most recently decoded packet.
*
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
* @param output FuriString to write the description into (cleared first)
*/
void subghz_protocol_decoder_psa2_get_string(void* context, FuriString* output);
/* =========================================================
* ENCODER API
* ========================================================= */
/**
* Allocate a PSA encoder instance.
*
* @param environment SubGHz environment (may be NULL / unused)
* @return Opaque pointer to SubGhzProtocolEncoderPSA
*/
void* subghz_protocol_encoder_psa2_alloc(SubGhzEnvironment* environment);
/**
* Free a PSA encoder instance.
*
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
*/
void subghz_protocol_encoder_psa2_free(void* context);
/**
* Load transmit data from a FlipperFormat stream into the encoder.
* Rebuilds the upload buffer ready for transmission.
*
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
* @param ff Open FlipperFormat file handle (will be rewound internally)
* @return SubGhzProtocolStatusOk on success
*/
SubGhzProtocolStatus subghz_protocol_encoder_psa2_deserialize(
void* context,
FlipperFormat* ff);
/**
* Stop an in-progress transmission immediately.
*
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
*/
void subghz_protocol_encoder_psa2_stop(void* context);
/**
* Yield the next LevelDuration sample from the upload buffer.
* Called repeatedly by the SubGHz radio driver during transmission.
*
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
* @return Next LevelDuration, or level_duration_reset() when done
*/
LevelDuration subghz_protocol_encoder_psa2_yield(void* context);
#ifdef __cplusplus
}
#endif