mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 11:18:15 +00:00
Analyzer
This commit is contained in:
@ -3,9 +3,11 @@
|
||||
#include "IrFoxDecoder.h"
|
||||
#include <AnalyzerChannelData.h>
|
||||
#include <AnalyzerResults.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
IrFoxAnalyzer::IrFoxAnalyzer()
|
||||
: Analyzer2(),
|
||||
@ -72,6 +74,45 @@ void IrFoxAnalyzer::WorkerThread()
|
||||
IrFoxDecoder decoder;
|
||||
decoder.reset();
|
||||
|
||||
/** Потоковый фильтр: убирает импульсы короче kMinFilteredPulseUs (иголки/дребезг в сэмплах). */
|
||||
const U64 min_seg_samples =
|
||||
std::max<U64>(1ULL, static_cast<U64>((static_cast<double>(irfox::kMinFilteredPulseUs) * 1e-6) * static_cast<double>(fs) + 0.5));
|
||||
struct RawEdge
|
||||
{
|
||||
U64 sample;
|
||||
bool rising;
|
||||
};
|
||||
std::vector<RawEdge> pending;
|
||||
U64 last_dec_edge_sample = 0;
|
||||
bool last_dec_edge_valid = false;
|
||||
|
||||
auto collapse_short_pairs = [&]() {
|
||||
for (size_t i = 0; i + 1 < pending.size();)
|
||||
{
|
||||
if (pending[i + 1].sample - pending[i].sample < min_seg_samples)
|
||||
{
|
||||
pending.erase(pending.begin() + static_cast<std::ptrdiff_t>(i),
|
||||
pending.begin() + static_cast<std::ptrdiff_t>(i + 2));
|
||||
if (i > 0)
|
||||
--i;
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
};
|
||||
|
||||
auto strip_vs_last_decoder = [&]() {
|
||||
for (;;)
|
||||
{
|
||||
collapse_short_pairs();
|
||||
if (!last_dec_edge_valid || pending.empty())
|
||||
return;
|
||||
if (pending[0].sample - last_dec_edge_sample >= min_seg_samples)
|
||||
return;
|
||||
pending.erase(pending.begin());
|
||||
}
|
||||
};
|
||||
|
||||
U32 frames_since_commit = 0;
|
||||
const U32 kCommitBatch = 256;
|
||||
|
||||
@ -130,6 +171,43 @@ void IrFoxAnalyzer::WorkerThread()
|
||||
}
|
||||
};
|
||||
|
||||
auto emit_confirmed_edges = [&]() {
|
||||
for (;;)
|
||||
{
|
||||
collapse_short_pairs();
|
||||
strip_vs_last_decoder();
|
||||
if (pending.size() < 2)
|
||||
return;
|
||||
if (pending[1].sample - pending[0].sample < min_seg_samples)
|
||||
continue;
|
||||
decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
|
||||
last_dec_edge_sample = pending[0].sample;
|
||||
last_dec_edge_valid = true;
|
||||
pending.erase(pending.begin());
|
||||
}
|
||||
};
|
||||
|
||||
auto flush_pending_tail = [&]() {
|
||||
collapse_short_pairs();
|
||||
strip_vs_last_decoder();
|
||||
while (pending.size() >= 2 && pending[1].sample - pending[0].sample >= min_seg_samples)
|
||||
{
|
||||
decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
|
||||
last_dec_edge_sample = pending[0].sample;
|
||||
last_dec_edge_valid = true;
|
||||
pending.erase(pending.begin());
|
||||
collapse_short_pairs();
|
||||
strip_vs_last_decoder();
|
||||
}
|
||||
if (pending.size() == 1)
|
||||
{
|
||||
decoder.processEdge(pending[0].sample, pending[0].rising, fs, on_bit, on_pkt);
|
||||
last_dec_edge_sample = pending[0].sample;
|
||||
last_dec_edge_valid = true;
|
||||
pending.clear();
|
||||
}
|
||||
};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CheckIfThreadShouldExit();
|
||||
@ -146,10 +224,12 @@ void IrFoxAnalyzer::WorkerThread()
|
||||
const BitState new_level = mIr->GetBitState();
|
||||
const bool rising = (new_level == BIT_HIGH);
|
||||
|
||||
decoder.processEdge(edge_sample, rising, fs, on_bit, on_pkt);
|
||||
pending.push_back(RawEdge{edge_sample, rising});
|
||||
emit_confirmed_edges();
|
||||
ReportProgress(edge_sample);
|
||||
}
|
||||
|
||||
flush_pending_tail();
|
||||
decoder.flushEnd(mIr->GetSampleNumber(), fs, on_bit, on_pkt);
|
||||
|
||||
if (frames_since_commit != 0)
|
||||
|
||||
@ -262,7 +262,7 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
|
||||
const uint32_t rise_max_us = rise_sync_time_us + irfox::kToleranceUs;
|
||||
|
||||
/** Начало PRE-бабла: при первом подъёме пузыря метка ИК (активный низ) начинается со спада — берём prev_fall, если он близок. */
|
||||
/** Визуализация: начало PRE с ближайшего спада в пределах ~3 битовых периодов (ИК-метка). */
|
||||
auto new_bubble_preamble_start = [&](uint64_t edge_s, bool is_rising) -> uint64_t {
|
||||
if (!is_rising)
|
||||
return edge_s;
|
||||
@ -278,9 +278,11 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
|
||||
if (rising)
|
||||
{
|
||||
const uint32_t cand_rp = static_cast<uint32_t>(t_us - prev_rise_us);
|
||||
const double delta_rp = t_us - prev_rise_us;
|
||||
const uint32_t cand_rp = static_cast<uint32_t>(delta_rp);
|
||||
const uint32_t cand_ht = static_cast<uint32_t>(t_us - prev_fall_us);
|
||||
const uint32_t cand_lt = static_cast<uint32_t>(prev_fall_us - prev_rise_us);
|
||||
|
||||
#if IRFOX_SHORT_LOW_GLITCH_REJECT
|
||||
const bool short_low_glitch =
|
||||
is_recive && !is_preamb && cand_ht < (rise_min_us / 8U) && cand_lt >= rise_min_us &&
|
||||
@ -288,9 +290,7 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
if (short_low_glitch)
|
||||
{
|
||||
err_other++;
|
||||
#if IRFOX_GLITCH_REJECT_PHASE_NUDGE
|
||||
irfox::irfoxGlitchPhaseNudgeUs(t_us, rise_sync_time_us, prev_rise_us);
|
||||
#endif
|
||||
last_processed_edge_us = t_us;
|
||||
have_last_processed = true;
|
||||
return;
|
||||
@ -304,9 +304,7 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
if (micro_gap_rise)
|
||||
{
|
||||
err_other++;
|
||||
#if IRFOX_GLITCH_REJECT_PHASE_NUDGE
|
||||
irfox::irfoxGlitchPhaseNudgeUs(t_us, rise_sync_time_us, prev_rise_us);
|
||||
#endif
|
||||
last_processed_edge_us = t_us;
|
||||
have_last_processed = true;
|
||||
return;
|
||||
@ -319,31 +317,32 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
have_last_processed = true;
|
||||
return;
|
||||
}
|
||||
// Как IR_DecoderRaw::tick: до prev_rise_us = t_us, иначе ниже (t_us - prev_rise_us) на подъёме == 0.
|
||||
|
||||
// Визуализация PRE: длинная пауза, первый подъём — якорь от спада метки (декод как STM32DMA).
|
||||
if (cand_rp > irmax * 2U && !is_recive_raw)
|
||||
{
|
||||
first_rx();
|
||||
preamb_front_counter = static_cast<int8_t>(irfox::kPreambFronts - 1U);
|
||||
is_preamb = true;
|
||||
is_recive = true;
|
||||
is_recive_raw = true;
|
||||
is_wrong_pack = false;
|
||||
preamble_bubble_start_sample_ = new_bubble_preamble_start(sample, true);
|
||||
preamble_bubble_start_valid_ = true;
|
||||
}
|
||||
rise_period_anchor_sample_ = prev_rise_sample;
|
||||
rise_period_us = cand_rp;
|
||||
high_time_us = cand_ht;
|
||||
low_time_us = cand_lt;
|
||||
prev_rise_us = t_us;
|
||||
prev_rise_sample = sample;
|
||||
|
||||
const bool accept_rise_timing =
|
||||
(delta_rp > static_cast<double>(rise_max_us) / 4.0) || high_count != 0 || low_count != 0;
|
||||
if (accept_rise_timing)
|
||||
{
|
||||
rise_period_anchor_sample_ = prev_rise_sample;
|
||||
rise_period_us = cand_rp;
|
||||
high_time_us = cand_ht;
|
||||
low_time_us = cand_lt;
|
||||
prev_rise_us = t_us;
|
||||
prev_rise_sample = sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
err_other++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if IRFOX_IN_MARK_DOUBLE_FALL_IGNORE
|
||||
const double hi_since_rise = t_us - prev_rise_us;
|
||||
const uint32_t mark_end_min_us = (rise_min_us * irfox::kBitActiveTakts) / irfox::kBitTakts;
|
||||
#endif
|
||||
if (t_us - prev_fall_us > rise_min_us / 4.0)
|
||||
{
|
||||
prev_fall_us = t_us;
|
||||
@ -351,26 +350,23 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
}
|
||||
else
|
||||
{
|
||||
#if IRFOX_IN_MARK_DOUBLE_FALL_IGNORE
|
||||
if (!(is_recive && !is_preamb && hi_since_rise < static_cast<double>(mark_end_min_us)))
|
||||
#endif
|
||||
{
|
||||
err_other++;
|
||||
}
|
||||
err_other++;
|
||||
}
|
||||
}
|
||||
|
||||
// Первый фронт после длинной тишины на спаде; на подъёме — cand_rp выше (до обновления prev_rise_us).
|
||||
// IR_DecoderRaw::tick (STM32DMA): без firstRX(); старт сырого приёма только этим блоком.
|
||||
if (t_us > prev_rise_us && (t_us - prev_rise_us) > irmax * 2.0 && !is_recive_raw)
|
||||
{
|
||||
first_rx();
|
||||
preamb_front_counter = static_cast<int8_t>(irfox::kPreambFronts - 1U);
|
||||
preamb_front_counter = static_cast<int8_t>(irfox::kPreambFronts - 1);
|
||||
is_preamb = true;
|
||||
is_recive = true;
|
||||
is_recive_raw = true;
|
||||
is_wrong_pack = false;
|
||||
preamble_bubble_start_sample_ = new_bubble_preamble_start(sample, rising);
|
||||
preamble_bubble_start_valid_ = true;
|
||||
if (!preamble_bubble_start_valid_)
|
||||
{
|
||||
preamble_bubble_start_sample_ = new_bubble_preamble_start(sample, rising);
|
||||
preamble_bubble_start_valid_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (preamb_front_counter)
|
||||
@ -390,10 +386,10 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
if (is_preamb)
|
||||
{
|
||||
is_preamb = false;
|
||||
// IR_DecoderRaw: prevRise += risePeriod / 2 — prevRise на этом тике ещё время последнего подъёма
|
||||
// (на спаде не обновлялся). В сэмплах: якорь = последний подъём + полпериода, не «спад + полпериод».
|
||||
const uint64_t preamble_bubble_end_sample =
|
||||
rising ? rise_period_anchor_sample_ : prev_rise_sample;
|
||||
// IR_DecoderRaw: prevRise += risePeriod / 2 — фаза как в прошивке.
|
||||
// Бабл PRE: до текущего фронта (sample−1), чтобы охватить все kPreambPulse периодов (3 импульса),
|
||||
// а не только до предыдущего подъёма (~2 периода).
|
||||
const uint64_t preamble_bubble_end_sample = sample > 0 ? sample - 1 : sample;
|
||||
prev_rise_us += rise_period_us / 2.0;
|
||||
{
|
||||
const double half_us = 0.5 * static_cast<double>(rise_period_us);
|
||||
@ -460,16 +456,6 @@ void IrFoxDecoder::processEdge(uint64_t sample, bool rising, uint32_t fs, const
|
||||
else
|
||||
write_to_buffer(false, false, cell_start_s, cell_end_s, on_bit, on_pkt, IrFoxEmitBitMode::WithBubble);
|
||||
}
|
||||
#if IRFOX_RISE_GRAY_SINGLE_BIT_FALLBACK
|
||||
else if (irfox::riseGraySingleBitFallback(rise_period_us, rise_sync_time_us))
|
||||
{
|
||||
err_other++;
|
||||
if (high_time_us > low_time_us)
|
||||
write_to_buffer(true, false, cell_start_s, cell_end_s, on_bit, on_pkt, IrFoxEmitBitMode::WithBubble);
|
||||
else
|
||||
write_to_buffer(false, false, cell_start_s, cell_end_s, on_bit, on_pkt, IrFoxEmitBitMode::WithBubble);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
uint16_t hc = ceil_div_u16(static_cast<uint16_t>(high_time_us > 0xFFFF ? 0xFFFF : high_time_us),
|
||||
|
||||
@ -11,25 +11,8 @@ constexpr uint32_t kBitTakts = kBitActiveTakts + kBitPauseTakts;
|
||||
constexpr uint32_t kBitTimeUs = kBitTakts * kCarrierPeriodUs;
|
||||
constexpr uint32_t kToleranceUs = 300U;
|
||||
|
||||
/** Синхронно с IR_config.h: IR_SHORT_LOW_GLITCH_REJECT (1 = включено, как на железе). */
|
||||
#ifndef IRFOX_SHORT_LOW_GLITCH_REJECT
|
||||
#define IRFOX_SHORT_LOW_GLITCH_REJECT 1
|
||||
#endif
|
||||
#ifndef IRFOX_RISE_INCLUSIVE_AROUND
|
||||
#define IRFOX_RISE_INCLUSIVE_AROUND 1
|
||||
#endif
|
||||
#ifndef IRFOX_RISE_GRAY_SINGLE_BIT_FALLBACK
|
||||
#define IRFOX_RISE_GRAY_SINGLE_BIT_FALLBACK 0
|
||||
#endif
|
||||
#ifndef IRFOX_GLITCH_REJECT_PHASE_NUDGE
|
||||
#define IRFOX_GLITCH_REJECT_PHASE_NUDGE 1
|
||||
#endif
|
||||
#ifndef IRFOX_MICRO_GAP_RISE_REJECT
|
||||
#define IRFOX_MICRO_GAP_RISE_REJECT 1
|
||||
#endif
|
||||
#ifndef IRFOX_IN_MARK_DOUBLE_FALL_IGNORE
|
||||
#define IRFOX_IN_MARK_DOUBLE_FALL_IGNORE 0
|
||||
#endif
|
||||
/** Мин. длительность плато (мкс) для потокового анти-глитча в анализаторе; согласовано с IR_INPUT_MIN_PULSE_US. */
|
||||
constexpr uint32_t kMinFilteredPulseUs = 10U;
|
||||
|
||||
constexpr uint8_t kBitPerByte = 8U;
|
||||
constexpr uint8_t kMsgBytes = 1;
|
||||
@ -45,32 +28,31 @@ constexpr uint8_t kDataByteSizeMax =
|
||||
constexpr uint8_t kPreambPulse = 3;
|
||||
constexpr uint8_t kPreambFronts = kPreambPulse * 2U;
|
||||
|
||||
/** Отброс ложного подъёма после микро-LOW в паузе (как расширенный IR_DecoderRaw::tick). */
|
||||
#ifndef IRFOX_SHORT_LOW_GLITCH_REJECT
|
||||
#define IRFOX_SHORT_LOW_GLITCH_REJECT 1
|
||||
#endif
|
||||
#ifndef IRFOX_GLITCH_REJECT_PHASE_NUDGE
|
||||
#define IRFOX_GLITCH_REJECT_PHASE_NUDGE 1
|
||||
#endif
|
||||
#ifndef IRFOX_MICRO_GAP_RISE_REJECT
|
||||
#define IRFOX_MICRO_GAP_RISE_REJECT 1
|
||||
#endif
|
||||
|
||||
inline uint32_t irTimeoutUs(uint32_t riseSyncTimeUs)
|
||||
{
|
||||
const uint32_t riseMax = riseSyncTimeUs + kToleranceUs;
|
||||
return riseMax * (8U + kSyncBits + 1U);
|
||||
}
|
||||
|
||||
/** Как IR_DecoderRaw.h: aroundRise(t) → riseTimeMin < t && t < riseTimeMax (ветка STM32DMA). */
|
||||
inline bool aroundRisePeriod(uint32_t periodUs, uint32_t riseSyncTimeUs)
|
||||
{
|
||||
const uint32_t lo = riseSyncTimeUs > kToleranceUs ? riseSyncTimeUs - kToleranceUs : 0U;
|
||||
const uint32_t hi = riseSyncTimeUs + kToleranceUs;
|
||||
#if IRFOX_RISE_INCLUSIVE_AROUND
|
||||
return lo <= periodUs && periodUs <= hi;
|
||||
#else
|
||||
return lo < periodUs && periodUs < hi;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Синхронно с IR_config.h: IR_RISE_GRAY_SINGLE_BIT_FALLBACK. */
|
||||
inline bool riseGraySingleBitFallback(uint32_t periodUs, uint32_t riseSyncTimeUs)
|
||||
{
|
||||
const uint32_t hi = riseSyncTimeUs + kToleranceUs;
|
||||
const uint32_t twice = riseSyncTimeUs * 2U;
|
||||
return periodUs > hi && periodUs <= twice;
|
||||
}
|
||||
|
||||
/** Синхронно с IR_config.h: IR_GLITCH_REJECT_PHASE_NUDGE. */
|
||||
inline void irfoxGlitchPhaseNudgeUs(double edge_us, uint32_t rise_sync_us, double& prev_rise_us)
|
||||
{
|
||||
#if IRFOX_GLITCH_REJECT_PHASE_NUDGE
|
||||
|
||||
Reference in New Issue
Block a user