This commit is contained in:
awe
2026-04-20 15:06:44 +03:00
parent cc9b22127a
commit fe9b0f6165

View File

@ -37,15 +37,17 @@ namespace {
constexpr uint32_t kE502DiSyn2Mask = constexpr uint32_t kE502DiSyn2Mask =
(static_cast<uint32_t>(1U) << 13U) | (static_cast<uint32_t>(1U) << 17U); (static_cast<uint32_t>(1U) << 13U) | (static_cast<uint32_t>(1U) << 17U);
constexpr uint32_t kE502Digital1Mask = (static_cast<uint32_t>(1U) << 0U);
constexpr uint32_t kE502Digital2Mask = (static_cast<uint32_t>(1U) << 1U); constexpr uint32_t kE502Digital2Mask = (static_cast<uint32_t>(1U) << 1U);
constexpr uint32_t kInternalRefSetting = X502_REF_FREQ_2000KHZ;
constexpr uint32_t kInternalRefHz = 2000000U;
struct Config { struct Config {
std::string serial; std::string serial;
std::optional<uint32_t> ip_addr; std::optional<uint32_t> ip_addr;
uint32_t clock_mode = X502_SYNC_DI_SYN1_RISE;
double clock_hz = 2000000.0; double clock_hz = 2000000.0;
double duration_ms = 100.0; double duration_ms = 100.0; // Legacy-only option retained for backward compatibility warnings.
uint32_t windows = 1000; uint32_t windows = 1000;
uint32_t recv_block_words = 8192; uint32_t recv_block_words = 8192;
@ -55,9 +57,12 @@ struct Config {
uint32_t input_buffer_words = 262144; uint32_t input_buffer_words = 262144;
uint32_t input_step_words = 8192; uint32_t input_step_words = 8192;
bool pullup_syn1 = false;
bool pullup_syn2 = false; bool pullup_syn2 = false;
bool clean_start = false; bool legacy_clock_arg_used = false;
std::string legacy_clock_arg;
bool legacy_pullup_syn1 = false;
bool legacy_clean_start = false;
bool legacy_duration_ms = false;
}; };
struct LchmClockCount { struct LchmClockCount {
@ -81,6 +86,11 @@ struct LchmClockCount {
} }
}; };
struct Di1StepClockCount {
bool di1_level = false;
LchmClockCount count;
};
struct RunningStats { struct RunningStats {
uint64_t count = 0; uint64_t count = 0;
uint64_t clocks_min = std::numeric_limits<uint64_t>::max(); uint64_t clocks_min = std::numeric_limits<uint64_t>::max();
@ -172,52 +182,35 @@ std::string ipv4_to_string(uint32_t ip_addr) {
return out.str(); return out.str();
} }
uint32_t parse_clock_mode(const std::string& text) { std::string parse_legacy_clock_mode(const std::string& text) {
const std::string value = trim_copy(text); const std::string value = trim_copy(text);
if (value == "di_syn1_rise") { if ((value == "di_syn1_rise") || (value == "di_syn1_fall")) {
return X502_SYNC_DI_SYN1_RISE; return value;
}
if (value == "di_syn1_fall") {
return X502_SYNC_DI_SYN1_FALL;
}
fail("Only clock:di_syn1_rise or clock:di_syn1_fall are supported");
}
std::string clock_mode_to_string(uint32_t mode) {
switch (mode) {
case X502_SYNC_DI_SYN1_RISE:
return "di_syn1_rise";
case X502_SYNC_DI_SYN1_FALL:
return "di_syn1_fall";
default:
return "unknown";
} }
fail("Unsupported legacy clock mode: " + text + ". Use di_syn1_rise or di_syn1_fall.");
} }
void print_help(const char* exe_name) { void print_help(const char* exe_name) {
std::cout std::cout
<< "Usage:\n" << "Usage:\n"
<< " " << exe_name << " [serial:SN] [ip:192.168.0.10]\n" << " " << exe_name << " [serial:SN] [ip:192.168.0.10]\n"
<< " [clock:di_syn1_rise] [clock_hz:2000000]\n" << " [clock_hz:2000000] [windows:1000]\n"
<< " [duration_ms:100] [windows:1000]\n"
<< " [recv_block:8192] [recv_timeout_ms:50]\n" << " [recv_block:8192] [recv_timeout_ms:50]\n"
<< " [clock_wait_ms:5000] [lchm_wait_ms:5000]\n" << " [clock_wait_ms:5000] [lchm_wait_ms:5000]\n"
<< " [buffer_words:262144] [step_words:8192]\n" << " [buffer_words:262144] [step_words:8192]\n"
<< " [pullup_syn1] [pullup_syn2] [clean_start]\n" << " [pullup_syn2]\n"
<< "\n" << "\n"
<< "Fixed counting scheme:\n" << "Fixed counting scheme:\n"
<< " DI_SYN1 -> external clock; one synchronous DIN sample is one clock\n" << " clock -> internal only (fixed reference 2000000 Hz)\n"
<< " DI_SYN2 -> LCHM window; rising edge starts, falling edge stops\n" << " DI_SYN2 -> strict LCHM window; low->high starts, high->low stops\n"
<< " DI2 -> extra digital level split into high/low clock counts\n" << " DI1 -> step delimiter; both DI1 edges split LCHM into steps\n"
<< " DI2 -> high/low clocks counted per step and per full LCHM\n"
<< "\n" << "\n"
<< "By default, an initial HIGH level on DI_SYN2 starts counting immediately,\n" << "Legacy arguments are accepted but ignored with warning:\n"
<< "matching main.exe packet startup behavior. Use clean_start to skip that\n" << " clock:di_syn1_rise|di_syn1_fall, pullup_syn1, clean_start, duration_ms:...\n"
<< "partial first window until a clean low->high transition is observed.\n"
<< "duration_ms closes an active window if DI_SYN2 does not fall; use\n"
<< "duration_ms:0 to require a real DI_SYN2 falling edge.\n"
<< "\n" << "\n"
<< "Example:\n" << "Example:\n"
<< " " << exe_name << " clock:di_syn1_rise clock_hz:2000000 windows:1000\n"; << " " << exe_name << " clock_hz:2000000 windows:1000 pullup_syn2\n";
} }
Config parse_args(int argc, char** argv) { Config parse_args(int argc, char** argv) {
@ -230,7 +223,7 @@ Config parse_args(int argc, char** argv) {
std::exit(0); std::exit(0);
} }
if (arg == "pullup_syn1") { if (arg == "pullup_syn1") {
cfg.pullup_syn1 = true; cfg.legacy_pullup_syn1 = true;
continue; continue;
} }
if (arg == "pullup_syn2") { if (arg == "pullup_syn2") {
@ -238,7 +231,7 @@ Config parse_args(int argc, char** argv) {
continue; continue;
} }
if (arg == "clean_start") { if (arg == "clean_start") {
cfg.clean_start = true; cfg.legacy_clean_start = true;
continue; continue;
} }
if (starts_with(arg, "serial:")) { if (starts_with(arg, "serial:")) {
@ -250,7 +243,8 @@ Config parse_args(int argc, char** argv) {
continue; continue;
} }
if (starts_with(arg, "clock:")) { if (starts_with(arg, "clock:")) {
cfg.clock_mode = parse_clock_mode(arg.substr(6)); cfg.legacy_clock_arg_used = true;
cfg.legacy_clock_arg = parse_legacy_clock_mode(arg.substr(6));
continue; continue;
} }
if (starts_with(arg, "clock_hz:")) { if (starts_with(arg, "clock_hz:")) {
@ -263,6 +257,7 @@ Config parse_args(int argc, char** argv) {
} }
if (starts_with(arg, "duration_ms:")) { if (starts_with(arg, "duration_ms:")) {
cfg.duration_ms = parse_double(arg.substr(12), "duration_ms"); cfg.duration_ms = parse_double(arg.substr(12), "duration_ms");
cfg.legacy_duration_ms = true;
continue; continue;
} }
if (starts_with(arg, "windows:")) { if (starts_with(arg, "windows:")) {
@ -428,11 +423,11 @@ struct Api {
decltype(&X502_StreamsDisable) StreamsDisable = nullptr; decltype(&X502_StreamsDisable) StreamsDisable = nullptr;
decltype(&X502_SetSyncMode) SetSyncMode = nullptr; decltype(&X502_SetSyncMode) SetSyncMode = nullptr;
decltype(&X502_SetSyncStartMode) SetSyncStartMode = nullptr; decltype(&X502_SetSyncStartMode) SetSyncStartMode = nullptr;
decltype(&X502_SetDinFreqDivider) SetDinFreqDivider = nullptr; decltype(&X502_SetRefFreq) SetRefFreq = nullptr;
decltype(&X502_SetDinFreq) SetDinFreq = nullptr;
decltype(&X502_SetStreamBufSize) SetStreamBufSize = nullptr; decltype(&X502_SetStreamBufSize) SetStreamBufSize = nullptr;
decltype(&X502_SetStreamStep) SetStreamStep = nullptr; decltype(&X502_SetStreamStep) SetStreamStep = nullptr;
decltype(&X502_SetDigInPullup) SetDigInPullup = nullptr; decltype(&X502_SetDigInPullup) SetDigInPullup = nullptr;
decltype(&X502_SetExtRefFreqValue) SetExtRefFreqValue = nullptr;
decltype(&X502_Configure) Configure = nullptr; decltype(&X502_Configure) Configure = nullptr;
decltype(&X502_StreamsEnable) StreamsEnable = nullptr; decltype(&X502_StreamsEnable) StreamsEnable = nullptr;
decltype(&X502_StreamsStart) StreamsStart = nullptr; decltype(&X502_StreamsStart) StreamsStart = nullptr;
@ -473,11 +468,11 @@ struct Api {
StreamsDisable = load_symbol<decltype(StreamsDisable)>(x502_module, "X502_StreamsDisable"); StreamsDisable = load_symbol<decltype(StreamsDisable)>(x502_module, "X502_StreamsDisable");
SetSyncMode = load_symbol<decltype(SetSyncMode)>(x502_module, "X502_SetSyncMode"); SetSyncMode = load_symbol<decltype(SetSyncMode)>(x502_module, "X502_SetSyncMode");
SetSyncStartMode = load_symbol<decltype(SetSyncStartMode)>(x502_module, "X502_SetSyncStartMode"); SetSyncStartMode = load_symbol<decltype(SetSyncStartMode)>(x502_module, "X502_SetSyncStartMode");
SetDinFreqDivider = load_symbol<decltype(SetDinFreqDivider)>(x502_module, "X502_SetDinFreqDivider"); SetRefFreq = load_symbol<decltype(SetRefFreq)>(x502_module, "X502_SetRefFreq");
SetDinFreq = load_symbol<decltype(SetDinFreq)>(x502_module, "X502_SetDinFreq");
SetStreamBufSize = load_symbol<decltype(SetStreamBufSize)>(x502_module, "X502_SetStreamBufSize"); SetStreamBufSize = load_symbol<decltype(SetStreamBufSize)>(x502_module, "X502_SetStreamBufSize");
SetStreamStep = load_symbol<decltype(SetStreamStep)>(x502_module, "X502_SetStreamStep"); SetStreamStep = load_symbol<decltype(SetStreamStep)>(x502_module, "X502_SetStreamStep");
SetDigInPullup = load_symbol<decltype(SetDigInPullup)>(x502_module, "X502_SetDigInPullup"); SetDigInPullup = load_symbol<decltype(SetDigInPullup)>(x502_module, "X502_SetDigInPullup");
SetExtRefFreqValue = load_symbol<decltype(SetExtRefFreqValue)>(x502_module, "X502_SetExtRefFreqValue");
Configure = load_symbol<decltype(Configure)>(x502_module, "X502_Configure"); Configure = load_symbol<decltype(Configure)>(x502_module, "X502_Configure");
StreamsEnable = load_symbol<decltype(StreamsEnable)>(x502_module, "X502_StreamsEnable"); StreamsEnable = load_symbol<decltype(StreamsEnable)>(x502_module, "X502_StreamsEnable");
StreamsStart = load_symbol<decltype(StreamsStart)>(x502_module, "X502_StreamsStart"); StreamsStart = load_symbol<decltype(StreamsStart)>(x502_module, "X502_StreamsStart");
@ -638,6 +633,22 @@ void print_summary(const RunningStats& stats) {
} }
int run(const Config& cfg) { int run(const Config& cfg) {
if (cfg.legacy_clock_arg_used) {
std::cerr << "Warning: legacy argument clock:" << cfg.legacy_clock_arg
<< " is ignored. lchm_clock_counter uses internal clock only.\n";
}
if (cfg.legacy_pullup_syn1) {
std::cerr << "Warning: legacy argument pullup_syn1 is ignored in internal-only mode.\n";
}
if (cfg.legacy_clean_start) {
std::cerr << "Warning: legacy argument clean_start is ignored. "
<< "LCHM starts strictly on DI_SYN2 low->high.\n";
}
if (cfg.legacy_duration_ms) {
std::cerr << "Warning: legacy argument duration_ms:" << cfg.duration_ms
<< " is ignored. LCHM closes only on DI_SYN2 high->low.\n";
}
Api api; Api api;
DeviceHandle device(api); DeviceHandle device(api);
@ -666,30 +677,17 @@ int run(const Config& cfg) {
api.StreamsStop(device.hnd); api.StreamsStop(device.hnd);
api.StreamsDisable(device.hnd, X502_STREAM_ALL_IN | X502_STREAM_ALL_OUT); api.StreamsDisable(device.hnd, X502_STREAM_ALL_IN | X502_STREAM_ALL_OUT);
expect_ok(api, api.SetSyncMode(device.hnd, cfg.clock_mode), "Set external clock on DI_SYN1"); expect_ok(api, api.SetSyncMode(device.hnd, X502_SYNC_INTERNAL), "Set internal sync mode");
expect_ok(api, api.SetSyncStartMode(device.hnd, X502_SYNC_INTERNAL), "Set immediate stream start"); expect_ok(api, api.SetSyncStartMode(device.hnd, X502_SYNC_INTERNAL), "Set immediate stream start");
expect_ok(api, api.SetRefFreq(device.hnd, kInternalRefSetting), "Set internal reference frequency");
const int32_t ext_ref_err = api.SetExtRefFreqValue(device.hnd, cfg.clock_hz); double actual_din_freq_hz = cfg.clock_hz;
if (ext_ref_err != X502_ERR_OK) { expect_ok(api, api.SetDinFreq(device.hnd, &actual_din_freq_hz), "Set DIN frequency");
if (cfg.clock_hz <= 1500000.0) {
expect_ok(api, ext_ref_err, "Set external reference frequency");
} else {
std::cerr << "Warning: X502_SetExtRefFreqValue(" << cfg.clock_hz
<< ") failed, continuing with DIN divider 1: "
<< x502_error(api, ext_ref_err) << "\n";
}
}
expect_ok(api, api.SetDinFreqDivider(device.hnd, 1), "Set DIN frequency divider to one clock");
expect_ok(api, api.SetStreamBufSize(device.hnd, X502_STREAM_CH_IN, cfg.input_buffer_words), expect_ok(api, api.SetStreamBufSize(device.hnd, X502_STREAM_CH_IN, cfg.input_buffer_words),
"Set input buffer size"); "Set input buffer size");
expect_ok(api, api.SetStreamStep(device.hnd, X502_STREAM_CH_IN, cfg.input_step_words), expect_ok(api, api.SetStreamStep(device.hnd, X502_STREAM_CH_IN, cfg.input_step_words),
"Set input stream step"); "Set input stream step");
uint32_t pullups = 0; uint32_t pullups = 0;
if (cfg.pullup_syn1) {
pullups |= X502_PULLUPS_DI_SYN1;
}
if (cfg.pullup_syn2) { if (cfg.pullup_syn2) {
pullups |= X502_PULLUPS_DI_SYN2; pullups |= X502_PULLUPS_DI_SYN2;
} }
@ -699,21 +697,23 @@ int run(const Config& cfg) {
expect_ok(api, api.StreamsEnable(device.hnd, X502_STREAM_DIN), "Enable DIN stream"); expect_ok(api, api.StreamsEnable(device.hnd, X502_STREAM_DIN), "Enable DIN stream");
std::cout << "LCHM clock counter settings:\n" std::cout << "LCHM clock counter settings:\n"
<< " clock source: " << clock_mode_to_string(cfg.clock_mode) << "\n" << " clock source: internal\n"
<< " nominal clock: " << cfg.clock_hz << " Hz\n" << " internal ref: " << kInternalRefHz << " Hz\n"
<< " duration limit: " << " requested DIN clock: " << cfg.clock_hz << " Hz\n"
<< ((cfg.duration_ms == 0.0) ? std::string("disabled") << " effective DIN clock: " << actual_din_freq_hz << " Hz\n"
: std::to_string(cfg.duration_ms) + " ms") << " LCHM gate: strict DI_SYN2 low->high start, high->low stop\n"
<< "\n" << " DI1 step segmentation: both edges\n"
<< " LCHM gate: DI_SYN2 high window\n" << " DI2 split: enabled per step and per full LCHM\n"
<< " DI2 split: enabled\n" << " duration limit: disabled (legacy duration_ms ignored)"
<< " initial high DI_SYN2: " << (cfg.clean_start ? "skip until next low->high"
: "count immediately")
<< "\n" << "\n"
<< " target windows: " << cfg.windows << "\n" << " target windows: " << cfg.windows << "\n"
<< " recv block words: " << cfg.recv_block_words << "\n" << " recv block words: " << cfg.recv_block_words << "\n"
<< " input step words: " << cfg.input_step_words << "\n" << " input step words: " << cfg.input_step_words << "\n"
<< " input buffer words: " << cfg.input_buffer_words << "\n"; << " input buffer words: " << cfg.input_buffer_words << "\n";
if (std::fabs(actual_din_freq_hz - cfg.clock_hz) > std::max(0.5, cfg.clock_hz * 1e-6)) {
std::cerr << "Warning: effective DIN clock differs from requested value: requested="
<< cfg.clock_hz << " Hz, effective=" << actual_din_freq_hz << " Hz\n";
}
ConsoleCtrlGuard console_guard; ConsoleCtrlGuard console_guard;
if (!console_guard.installed) { if (!console_guard.installed) {
@ -725,17 +725,16 @@ int run(const Config& cfg) {
std::vector<uint32_t> raw(cfg.recv_block_words); std::vector<uint32_t> raw(cfg.recv_block_words);
std::vector<uint32_t> din_buffer(cfg.recv_block_words); std::vector<uint32_t> din_buffer(cfg.recv_block_words);
const uint64_t duration_limit_clocks = (cfg.duration_ms == 0.0)
? 0U
: std::max<uint64_t>(
1U,
static_cast<uint64_t>(std::llround((cfg.duration_ms / 1000.0) * cfg.clock_hz)));
bool gate_initialized = false; bool gate_initialized = false;
bool last_gate = false; bool last_gate = false;
bool saw_low_before_capture = false; bool di1_initialized = false;
bool last_di1_level = false;
bool in_lchm = false; bool in_lchm = false;
LchmClockCount current; LchmClockCount current;
LchmClockCount current_step;
bool current_step_level = false;
std::vector<Di1StepClockCount> current_steps;
RunningStats stats; RunningStats stats;
const TickMs session_start = tick_count_ms(); const TickMs session_start = tick_count_ms();
@ -748,12 +747,28 @@ int run(const Config& cfg) {
uint64_t total_din_words = 0; uint64_t total_din_words = 0;
uint64_t total_gate_edges = 0; uint64_t total_gate_edges = 0;
auto start_lchm = [&]() { auto start_lchm = [&](bool di1_level) {
in_lchm = true; in_lchm = true;
current.clear(); current.clear();
current_step.clear();
current_step_level = di1_level;
current_steps.clear();
};
auto finalize_current_step = [&]() {
if (current_step.clocks == 0U) {
return;
}
Di1StepClockCount step;
step.di1_level = current_step_level;
step.count = current_step;
current_steps.push_back(step);
current_step.clear();
}; };
auto finalize_lchm = [&](const char* close_reason) { auto finalize_lchm = [&](const char* close_reason) {
finalize_current_step();
if (current.clocks != (current.di2_high_clocks + current.di2_low_clocks)) { if (current.clocks != (current.di2_high_clocks + current.di2_low_clocks)) {
std::ostringstream message; std::ostringstream message;
message << "DI2 clock split invariant failed: clocks=" << current.clocks message << "DI2 clock split invariant failed: clocks=" << current.clocks
@ -762,6 +777,25 @@ int run(const Config& cfg) {
fail(message.str()); fail(message.str());
} }
uint64_t step_sum_clocks = 0;
uint64_t step_sum_high = 0;
uint64_t step_sum_low = 0;
for (const auto& step : current_steps) {
step_sum_clocks += step.count.clocks;
step_sum_high += step.count.di2_high_clocks;
step_sum_low += step.count.di2_low_clocks;
}
if ((step_sum_clocks != current.clocks) ||
(step_sum_high != current.di2_high_clocks) ||
(step_sum_low != current.di2_low_clocks)) {
std::ostringstream message;
message << "DI1 step split invariant failed: total clocks/high/low="
<< current.clocks << "/" << current.di2_high_clocks << "/" << current.di2_low_clocks
<< ", step sum clocks/high/low="
<< step_sum_clocks << "/" << step_sum_high << "/" << step_sum_low;
fail(message.str());
}
if (current.clocks != 0U) { if (current.clocks != 0U) {
const uint64_t lchm_index = stats.count + 1U; const uint64_t lchm_index = stats.count + 1U;
stats.add(current); stats.add(current);
@ -771,9 +805,20 @@ int run(const Config& cfg) {
<< ", di2_low_clocks=" << current.di2_low_clocks << ", di2_low_clocks=" << current.di2_low_clocks
<< ", close_reason=" << close_reason << ", close_reason=" << close_reason
<< "\n"; << "\n";
for (std::size_t step_index = 0; step_index < current_steps.size(); ++step_index) {
const auto& step = current_steps[step_index];
std::cout << " step " << (step_index + 1U)
<< ": di1_level=" << (step.di1_level ? "HIGH" : "LOW")
<< ", clocks=" << step.count.clocks
<< ", di2_high_clocks=" << step.count.di2_high_clocks
<< ", di2_low_clocks=" << step.count.di2_low_clocks
<< "\n";
}
last_lchm_complete = tick_count_ms(); last_lchm_complete = tick_count_ms();
} }
current.clear(); current.clear();
current_step.clear();
current_steps.clear();
}; };
auto fail_waiting_for_lchm = [&](TickMs now) { auto fail_waiting_for_lchm = [&](TickMs now) {
@ -819,8 +864,8 @@ int run(const Config& cfg) {
const TickMs now = tick_count_ms(); const TickMs now = tick_count_ms();
if (recvd == 0) { if (recvd == 0) {
if ((now - last_stream_activity) >= cfg.clock_wait_ms) { if ((now - last_stream_activity) >= cfg.clock_wait_ms) {
fail("Timeout waiting for external clock on DI_SYN1. " fail("Timeout waiting for DIN stream data in internal clock mode. "
"The stream starts immediately, so this usually means there is no valid clock on DI_SYN1."); "Check device state and DIN stream configuration.");
} }
if ((now - last_lchm_complete) >= cfg.lchm_wait_ms) { if ((now - last_lchm_complete) >= cfg.lchm_wait_ms) {
fail_waiting_for_lchm(now); fail_waiting_for_lchm(now);
@ -851,18 +896,22 @@ int run(const Config& cfg) {
for (uint32_t i = 0; (i < din_count) && (stats.count < cfg.windows); ++i) { for (uint32_t i = 0; (i < din_count) && (stats.count < cfg.windows); ++i) {
const uint32_t din_value = din_buffer[i]; const uint32_t din_value = din_buffer[i];
const bool gate = (din_value & kE502DiSyn2Mask) != 0U; const bool gate = (din_value & kE502DiSyn2Mask) != 0U;
const bool di1_level = (din_value & kE502Digital1Mask) != 0U;
const bool di2_high = (din_value & kE502Digital2Mask) != 0U; const bool di2_high = (din_value & kE502Digital2Mask) != 0U;
bool di1_changed = false;
if (!di1_initialized) {
di1_initialized = true;
last_di1_level = di1_level;
} else if (di1_level != last_di1_level) {
di1_changed = true;
last_di1_level = di1_level;
}
if (!gate_initialized) { if (!gate_initialized) {
gate_initialized = true; gate_initialized = true;
last_gate = gate; last_gate = gate;
saw_low_before_capture = !gate;
last_gate_edge = now; last_gate_edge = now;
if (gate && !cfg.clean_start) {
start_lchm();
}
} else if (!saw_low_before_capture && !gate) {
saw_low_before_capture = true;
} }
if (gate_initialized && (gate != last_gate)) { if (gate_initialized && (gate != last_gate)) {
@ -870,25 +919,22 @@ int run(const Config& cfg) {
last_gate_edge = now; last_gate_edge = now;
} }
if (!in_lchm && saw_low_before_capture && gate_initialized && !last_gate && gate) { if (!in_lchm && gate_initialized && !last_gate && gate) {
start_lchm(); start_lchm(di1_level);
} }
if (in_lchm && !gate) { if (in_lchm && last_gate && !gate) {
finalize_lchm("di_syn2_fall"); finalize_lchm("di_syn2_fall");
in_lchm = false; in_lchm = false;
} }
if (in_lchm && gate) { if (in_lchm && gate) {
current.add(di2_high); if (di1_changed && (current_step.clocks != 0U)) {
} finalize_current_step();
current_step_level = di1_level;
if (in_lchm && (duration_limit_clocks != 0U) && (current.clocks >= duration_limit_clocks)) {
finalize_lchm("duration_limit");
in_lchm = false;
if (gate && (stats.count < cfg.windows)) {
start_lchm();
} }
current.add(di2_high);
current_step.add(di2_high);
} }
last_gate = gate; last_gate = gate;