Files
IR-protocol/Analyzer/raw/IR_Fox/src/IrFoxAnalyzer.cpp
2026-04-07 15:08:02 +03:00

281 lines
7.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "IrFoxAnalyzer.h"
#include "IrFoxAnalyzerSettings.h"
#include "IrFoxDecoder.h"
#include <AnalyzerChannelData.h>
#include <AnalyzerResults.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
IrFoxAnalyzer::IrFoxAnalyzer()
: Analyzer2(),
mSettings(),
mSimulationInitilized(false)
{
SetAnalyzerSettings(&mSettings);
UseFrameV2();
}
IrFoxAnalyzer::~IrFoxAnalyzer()
{
KillThread();
}
void IrFoxAnalyzer::SetupResults()
{
m_packet_hex_by_frame.clear();
mResults.reset(new IrFoxAnalyzerResults(this, &mSettings));
SetAnalyzerResults(mResults.get());
mResults->AddChannelBubblesWillAppearOn(mSettings.mInputChannel);
}
static void append_hex(std::string& s, const uint8_t* p, size_t n, size_t max_bytes = 64)
{
static const char* hd = "0123456789abcdef";
const size_t m = n < max_bytes ? n : max_bytes;
for (size_t i = 0; i < m; i++)
{
s.push_back(hd[p[i] >> 4]);
s.push_back(hd[p[i] & 0xFu]);
if (i + 1 < m)
s.push_back(' ');
}
if (n > max_bytes)
s += "...";
}
const char* IrFoxAnalyzer::PacketHexForFrame(U64 frame_id)
{
auto it = m_packet_hex_by_frame.find(frame_id);
if (it == m_packet_hex_by_frame.end())
return "";
m_hex_scratch = it->second;
return m_hex_scratch.c_str();
}
const char* IrFoxAnalyzer::BubbleTextForFrame(U64 frame_id) const
{
auto it = m_bubble_text_by_frame.find(frame_id);
if (it == m_bubble_text_by_frame.end())
return "";
m_bubble_scratch = it->second;
return m_bubble_scratch.c_str();
}
void IrFoxAnalyzer::WorkerThread()
{
mIr = GetAnalyzerChannelData(mSettings.mInputChannel);
m_packet_hex_by_frame.clear();
m_bubble_text_by_frame.clear();
const U32 fs = GetSampleRate();
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;
IrFoxOnBit on_bit = [&](const IrFoxEmitBit& e) {
Frame frame;
frame.mStartingSampleInclusive = static_cast<S64>(e.start_sample);
frame.mEndingSampleInclusive = static_cast<S64>(e.end_sample);
frame.mType = e.frame_type;
frame.mData1 = e.bit_value;
frame.mData2 = e.bit_index | (U64(e.err_low) << 16) | (U64(e.err_high) << 24) | (U64(e.err_other) << 32);
frame.mFlags = e.mflags;
// В SDK только ERROR/WARNING меняют цвет бабла; sync выделяем янтарным (как warning), данные — обычные.
if (e.frame_type == IRF_FT_SYNC_BIT)
frame.mFlags |= DISPLAY_AS_WARNING_FLAG;
const U64 fid = mResults->AddFrame(frame);
if (e.bubble_text[0] != '\0')
m_bubble_text_by_frame[fid] = e.bubble_text;
if (++frames_since_commit >= kCommitBatch)
{
mResults->CommitResults();
frames_since_commit = 0;
}
};
IrFoxOnPacket on_pkt = [&](const IrFoxEmitPacket& p) {
Frame frame;
frame.mStartingSampleInclusive = static_cast<S64>(p.start_sample);
frame.mEndingSampleInclusive = static_cast<S64>(p.end_sample);
frame.mType = p.crc_ok ? IRF_FT_PACKET_OK : IRF_FT_PACKET_CRC_FAIL;
frame.mData1 = p.pack_size;
frame.mData2 = (U64(p.err_low) << 0) | (U64(p.err_high) << 8) | (U64(p.err_other) << 16);
if (!p.crc_ok)
frame.mFlags |= DISPLAY_AS_ERROR_FLAG;
const U64 fid = mResults->AddFrame(frame);
std::string hx;
append_hex(hx, p.data_bytes, p.pack_size);
m_packet_hex_by_frame[fid] = std::move(hx);
FrameV2 fv2;
fv2.AddBoolean("crc_ok", p.crc_ok);
fv2.AddInteger("len", static_cast<S64>(p.pack_size));
fv2.AddInteger("err_low", static_cast<S64>(p.err_low));
fv2.AddInteger("err_high", static_cast<S64>(p.err_high));
fv2.AddInteger("err_other", static_cast<S64>(p.err_other));
fv2.AddByteArray("data", p.data_bytes, p.pack_size);
mResults->AddFrameV2(fv2, p.crc_ok ? "packet_ok" : "packet_bad", static_cast<U64>(p.start_sample),
static_cast<U64>(p.end_sample));
if (++frames_since_commit >= kCommitBatch)
{
mResults->CommitResults();
frames_since_commit = 0;
}
};
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();
const U64 segment_start = mIr->GetSampleNumber();
const BitState level = mIr->GetBitState();
mIr->AdvanceToNextEdge();
const U64 edge_sample = mIr->GetSampleNumber();
if (edge_sample == segment_start)
break;
const BitState new_level = mIr->GetBitState();
const bool rising = (new_level == BIT_HIGH);
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)
mResults->CommitResults();
}
bool IrFoxAnalyzer::NeedsRerun()
{
return false;
}
U32 IrFoxAnalyzer::GenerateSimulationData(U64 minimum_sample_index, U32 device_sample_rate,
SimulationChannelDescriptor** simulation_channels)
{
if (mSimulationInitilized == false)
{
mSimulationDataGenerator.Initialize(GetSimulationSampleRate(), &mSettings);
mSimulationInitilized = true;
}
return mSimulationDataGenerator.GenerateSimulationData(minimum_sample_index, device_sample_rate,
simulation_channels);
}
U32 IrFoxAnalyzer::GetMinimumSampleRateHz()
{
return 200000;
}
const char* IrFoxAnalyzer::GetAnalyzerName() const
{
return "IR Fox";
}
const char* GetAnalyzerName()
{
return "IR Fox";
}
Analyzer* CreateAnalyzer()
{
return new IrFoxAnalyzer();
}
void DestroyAnalyzer(Analyzer* analyzer)
{
delete analyzer;
}