sync during ir_recv dev
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s

This commit is contained in:
2026-02-16 14:59:42 +01:00
parent 0113edb816
commit f5b1849ada
3 changed files with 118 additions and 57 deletions

View File

@@ -8,6 +8,7 @@ menuconfig IR_RECV
if IR_RECV
config IR_RECV_SIMULATOR
bool "Enable IR receiver simulator"
select ENTROPY_GENERATOR
help
Replaces real ADC/Hardware input with a software-based sample generator
for protocol testing.

View File

@@ -12,8 +12,18 @@
int ir_recv_init(void);
#ifdef CONFIG_IR_RECV_SIMULATOR
/* Helper to trigger a simulation from main.c or shell */
void ir_recv_sim_send_packet(ir_packet_t *packet);
/* Configuration for injecting signal errors */
typedef struct {
uint8_t noise_flips_per_8; /* Noise: number of inverted samples per block of 8 (0, 1, 2) */
uint8_t jitter_mark; /* Jitter for mark pulse: +/- samples (e.g. 2) */
uint8_t jitter_space_0; /* Jitter for space0: +/- samples (e.g. 4) */
uint8_t jitter_space_1; /* Jitter for space1: +/- samples (e.g. 2) */
} ir_sim_error_t;
/* err can be NULL to send a perfect signal */
void ir_recv_sim_send_packet(ir_packet_t *packet, ir_sim_error_t *err);
#endif
#endif /* IR_RECV_H */

View File

@@ -23,71 +23,119 @@ static struct k_sem adc_sem;
/* --- Enhanced Simulator --- */
#ifdef CONFIG_IR_RECV_SIMULATOR
static ir_packet_t sim_packet;
#include <zephyr/random/random.h>
#define SIM_MAX_SAMPLES 1000
static bool sim_buffer[SIM_MAX_SAMPLES];
static uint32_t sim_total_samples = 0;
static bool sim_trigger = false;
static uint32_t sim_sample_pos = 0;
void ir_recv_sim_send_packet(ir_packet_t *packet)
/* Hilfsfunktion für den Jitter: Liefert einen Zufallswert zwischen -max und +max */
static int get_jitter(uint8_t max_jitter)
{
memcpy(&sim_packet, packet, sizeof(ir_packet_t));
// Auto-calculate CRC for the simulator
sim_packet.data.fields.crc = lastertag_crc8(sim_packet.data.bytes, 2);
sim_sample_pos = 0;
sim_trigger = true;
LOG_DBG("Simulator: Packet queued (Type: %u, CRC: 0x%02X, Bytes: 0x%02X%02X)",
sim_packet.data.fields.type, sim_packet.data.fields.crc, sim_packet.data.bytes[0], sim_packet.data.bytes[1]);
if (max_jitter == 0) return 0;
return (int)(sys_rand32_get() % (max_jitter * 2 + 1)) - max_jitter;
}
static bool get_sim_level(uint32_t pos)
void ir_recv_sim_send_packet(ir_packet_t *packet, ir_sim_error_t *err)
{
if (pos < 32)
return 1; // Header Mark
if (pos < 40)
return 0; // Header Gap
/* Blockieren, bis vorheriges Paket abgearbeitet ist */
while (sim_trigger) {
k_msleep(5);
}
uint32_t p = pos - 40;
for (int i = 0; i < 24; i++)
{
// Extract bit from struct (LSB first to match our new right-shift receiver)
bool bit = (sim_packet.data.bytes[i / 8] >> (i % 8)) & 1;
uint32_t bit_len = bit ? 24 : 16;
uint32_t space_len = bit ? 16 : 8;
ir_packet_t pkt;
memcpy(&pkt, packet, sizeof(ir_packet_t));
pkt.data.fields.crc = lastertag_crc8(pkt.data.bytes, 2);
if (p < bit_len)
return (p >= space_len);
p -= bit_len;
}
return 0;
ir_sim_error_t default_err = {0};
if (err == NULL) {
err = &default_err;
}
sim_total_samples = 0;
/* 1. Header Mark */
int m_len = 32 + get_jitter(err->jitter_mark);
for (int i = 0; i < m_len && sim_total_samples < SIM_MAX_SAMPLES; i++) {
sim_buffer[sim_total_samples++] = 1;
}
/* 2. Header Gap */
int g_len = 8 + get_jitter(err->jitter_space_0);
for (int i = 0; i < g_len && sim_total_samples < SIM_MAX_SAMPLES; i++) {
sim_buffer[sim_total_samples++] = 0;
}
/* 3. Payload Bits (24 Bit) */
for (int i = 0; i < 24; i++) {
bool bit = (pkt.data.bytes[i / 8] >> (i % 8)) & 1;
/* Space Phase */
int s_base = bit ? 16 : 8;
int s_jit = bit ? get_jitter(err->jitter_space_1) : get_jitter(err->jitter_space_0);
int s_len = s_base + s_jit;
for (int j = 0; j < s_len && sim_total_samples < SIM_MAX_SAMPLES; j++) {
sim_buffer[sim_total_samples++] = 0;
}
/* Mark Phase */
int bm_len = 8 + get_jitter(err->jitter_mark);
for (int j = 0; j < bm_len && sim_total_samples < SIM_MAX_SAMPLES; j++) {
sim_buffer[sim_total_samples++] = 1;
}
}
/* 4. Rauschen injizieren (Bit Flips) */
if (err->noise_flips_per_8 > 0) {
for (uint32_t i = 0; i < sim_total_samples; i += 8) {
for (int f = 0; f < err->noise_flips_per_8; f++) {
uint32_t flip_idx = i + (sys_rand32_get() % 8);
if (flip_idx < sim_total_samples) {
sim_buffer[flip_idx] = !sim_buffer[flip_idx]; /* Bit kippen */
}
}
}
}
sim_sample_pos = 0;
sim_trigger = true;
LOG_DBG("Simulator: Queued (Type: %u, CRC: 0x%02X), Total Samples: %u",
pkt.data.fields.type, pkt.data.fields.crc, sim_total_samples);
}
void ir_recv_sim_thread(void *p1, void *p2, void *p3)
{
while (1)
{
k_usleep(75 * SAMPLES_PER_BUFFER);
if (!sim_trigger)
continue;
while (1)
{
k_usleep(75 * SAMPLES_PER_BUFFER);
if (!sim_trigger) continue;
int16_t *buf = adc_buffers[write_idx];
for (int i = 0; i < SAMPLES_PER_BUFFER; i++)
{
bool level = get_sim_level(sim_sample_pos++);
bool active = IS_ENABLED(CONFIG_IR_RECV_INVERT_SIGNAL) ? !level : level;
buf[i * ADC_CHANNELS] = active ? 0x0FFF : 0x0000;
// Clear other channels
buf[i * ADC_CHANNELS + 3] = 2400;
}
write_idx = (write_idx + 1) % BUFFER_COUNT;
k_sem_give(&adc_sem);
int16_t *buf = adc_buffers[write_idx];
for (int i = 0; i < SAMPLES_PER_BUFFER; i++)
{
bool level = 0;
if (sim_sample_pos < sim_total_samples) {
level = sim_buffer[sim_sample_pos++];
}
bool active = IS_ENABLED(CONFIG_IR_RECV_INVERT_SIGNAL) ? !level : level;
buf[i * ADC_CHANNELS] = active ? 0x0FFF : 0x0000;
buf[i * ADC_CHANNELS + 3] = 2400; // VBat Dummy
}
write_idx = (write_idx + 1) % BUFFER_COUNT;
k_sem_give(&adc_sem);
if (sim_sample_pos > 600)
{ // Safety stop
sim_trigger = false;
LOG_INF("Simulation finished.");
}
}
/* Nach dem Puffer noch eine kurze Pause einhalten, um das Paket abzuschließen */
if (sim_sample_pos >= sim_total_samples + 50)
{
sim_trigger = false;
LOG_INF("Simulation sequence finished.");
}
}
}
K_THREAD_DEFINE(sim_tid, 1024, ir_recv_sim_thread, NULL, NULL, NULL, 5, 0, 0);
#endif
@@ -136,6 +184,7 @@ static void process_ir_sample(ir_ctx_t *ctx, int16_t raw)
{
ctx->state = IR_STATE_HEADER_SYNC;
ctx->timer = 0;
LOG_DBG("Header detected. Energy: %u", energy_32);
}
break;
@@ -151,6 +200,7 @@ static void process_ir_sample(ir_ctx_t *ctx, int16_t raw)
else if (ctx->timer > 50)
{
ctx->state = IR_STATE_IDLE; /* Timeout */
LOG_DBG("Header sync timeout. Energy: %u", energy_8);
}
break;
@@ -218,12 +268,12 @@ static void process_ir_sample(ir_ctx_t *ctx, int16_t raw)
p.data.bytes[2] = (ctx->bit_acc >> 16) & 0xFF;
if (lastertag_crc8(p.data.bytes, 2) == p.data.fields.crc)
{
LOG_INF("VALID: Type %u, ID %u, Val %u",
p.data.fields.type,
p.data.fields.heal.healer_id,
p.data.fields.heal.amount);
}
{
LOG_INF("VALID: Type %u, ID %u, Val %u",
p.data.fields.type,
p.data.fields.id,
p.data.fields.value);
}
else
{
LOG_WRN("CRC Error! Acc: 0x%06X", ctx->bit_acc);