diff --git a/firmware/libs/ir/recv/Kconfig b/firmware/libs/ir/recv/Kconfig index 21bfcd8..5303d41 100644 --- a/firmware/libs/ir/recv/Kconfig +++ b/firmware/libs/ir/recv/Kconfig @@ -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. diff --git a/firmware/libs/ir/recv/include/ir_recv.h b/firmware/libs/ir/recv/include/ir_recv.h index ff58ac6..b7ec5a9 100644 --- a/firmware/libs/ir/recv/include/ir_recv.h +++ b/firmware/libs/ir/recv/include/ir_recv.h @@ -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 */ diff --git a/firmware/libs/ir/recv/src/ir_recv.c b/firmware/libs/ir/recv/src/ir_recv.c index 0bdd454..707d644 100644 --- a/firmware/libs/ir/recv/src/ir_recv.c +++ b/firmware/libs/ir/recv/src/ir_recv.c @@ -23,71 +23,119 @@ static struct k_sem adc_sem; /* --- Enhanced Simulator --- */ #ifdef CONFIG_IR_RECV_SIMULATOR -static ir_packet_t sim_packet; +#include + +#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);