fix lchm
This commit is contained in:
@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user