mirror of
https://github.com/Show-maket/IR-protocol.git
synced 2026-04-28 03:08:08 +00:00
281 lines
7.3 KiB
C++
281 lines
7.3 KiB
C++
#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;
|
||
}
|