fix
This commit is contained in:
@ -45,6 +45,7 @@ struct Config {
|
|||||||
|
|
||||||
uint32_t clock_mode = X502_SYNC_DI_SYN1_RISE;
|
uint32_t clock_mode = X502_SYNC_DI_SYN1_RISE;
|
||||||
double clock_hz = 2000000.0;
|
double clock_hz = 2000000.0;
|
||||||
|
double duration_ms = 100.0;
|
||||||
uint32_t windows = 1000;
|
uint32_t windows = 1000;
|
||||||
|
|
||||||
uint32_t recv_block_words = 8192;
|
uint32_t recv_block_words = 8192;
|
||||||
@ -56,6 +57,7 @@ struct Config {
|
|||||||
|
|
||||||
bool pullup_syn1 = false;
|
bool pullup_syn1 = false;
|
||||||
bool pullup_syn2 = false;
|
bool pullup_syn2 = false;
|
||||||
|
bool clean_start = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LchmClockCount {
|
struct LchmClockCount {
|
||||||
@ -197,19 +199,22 @@ void print_help(const char* exe_name) {
|
|||||||
<< "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:di_syn1_rise] [clock_hz:2000000]\n"
|
||||||
<< " [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]\n"
|
<< " [pullup_syn1] [pullup_syn2] [clean_start]\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Fixed counting scheme:\n"
|
<< "Fixed counting scheme:\n"
|
||||||
<< " DI_SYN1 -> external clock; one synchronous DIN sample is one clock\n"
|
<< " DI_SYN1 -> external clock; one synchronous DIN sample is one clock\n"
|
||||||
<< " DI_SYN2 -> LCHM window; rising edge starts, falling edge stops\n"
|
<< " DI_SYN2 -> LCHM window; rising edge starts, falling edge stops\n"
|
||||||
<< " DI2 -> extra digital level split into high/low clock counts\n"
|
<< " DI2 -> extra digital level split into high/low clock counts\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "If DI_SYN2 is already high at startup, the first partial LCHM is skipped\n"
|
<< "By default, an initial HIGH level on DI_SYN2 starts counting immediately,\n"
|
||||||
<< "until a clean low->high transition is observed.\n"
|
<< "matching main.exe packet startup behavior. Use clean_start to skip that\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:di_syn1_rise clock_hz:2000000 windows:1000\n";
|
||||||
@ -232,6 +237,10 @@ Config parse_args(int argc, char** argv) {
|
|||||||
cfg.pullup_syn2 = true;
|
cfg.pullup_syn2 = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "clean_start") {
|
||||||
|
cfg.clean_start = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (starts_with(arg, "serial:")) {
|
if (starts_with(arg, "serial:")) {
|
||||||
cfg.serial = arg.substr(7);
|
cfg.serial = arg.substr(7);
|
||||||
continue;
|
continue;
|
||||||
@ -252,6 +261,10 @@ Config parse_args(int argc, char** argv) {
|
|||||||
cfg.clock_hz = parse_double(arg.substr(16), "sample_clock_hz");
|
cfg.clock_hz = parse_double(arg.substr(16), "sample_clock_hz");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (starts_with(arg, "duration_ms:")) {
|
||||||
|
cfg.duration_ms = parse_double(arg.substr(12), "duration_ms");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (starts_with(arg, "windows:")) {
|
if (starts_with(arg, "windows:")) {
|
||||||
cfg.windows = parse_u32(arg.substr(8), "windows");
|
cfg.windows = parse_u32(arg.substr(8), "windows");
|
||||||
continue;
|
continue;
|
||||||
@ -290,6 +303,9 @@ Config parse_args(int argc, char** argv) {
|
|||||||
if (cfg.clock_hz <= 0.0) {
|
if (cfg.clock_hz <= 0.0) {
|
||||||
fail("clock_hz must be > 0");
|
fail("clock_hz must be > 0");
|
||||||
}
|
}
|
||||||
|
if (cfg.duration_ms < 0.0) {
|
||||||
|
fail("duration_ms must be >= 0");
|
||||||
|
}
|
||||||
if (cfg.windows == 0U) {
|
if (cfg.windows == 0U) {
|
||||||
fail("windows must be > 0");
|
fail("windows must be > 0");
|
||||||
}
|
}
|
||||||
@ -685,8 +701,15 @@ int run(const Config& cfg) {
|
|||||||
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: " << clock_mode_to_string(cfg.clock_mode) << "\n"
|
||||||
<< " nominal clock: " << cfg.clock_hz << " Hz\n"
|
<< " nominal clock: " << cfg.clock_hz << " Hz\n"
|
||||||
|
<< " duration limit: "
|
||||||
|
<< ((cfg.duration_ms == 0.0) ? std::string("disabled")
|
||||||
|
: std::to_string(cfg.duration_ms) + " ms")
|
||||||
|
<< "\n"
|
||||||
<< " LCHM gate: DI_SYN2 high window\n"
|
<< " LCHM gate: DI_SYN2 high window\n"
|
||||||
<< " DI2 split: enabled\n"
|
<< " DI2 split: enabled\n"
|
||||||
|
<< " initial high DI_SYN2: " << (cfg.clean_start ? "skip until next low->high"
|
||||||
|
: "count immediately")
|
||||||
|
<< "\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"
|
||||||
@ -702,6 +725,11 @@ 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;
|
||||||
@ -720,7 +748,12 @@ 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 finalize_lchm = [&]() {
|
auto start_lchm = [&]() {
|
||||||
|
in_lchm = true;
|
||||||
|
current.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto finalize_lchm = [&](const char* close_reason) {
|
||||||
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
|
||||||
@ -736,6 +769,7 @@ int run(const Config& cfg) {
|
|||||||
<< ": clocks=" << current.clocks
|
<< ": clocks=" << current.clocks
|
||||||
<< ", di2_high_clocks=" << current.di2_high_clocks
|
<< ", di2_high_clocks=" << current.di2_high_clocks
|
||||||
<< ", di2_low_clocks=" << current.di2_low_clocks
|
<< ", di2_low_clocks=" << current.di2_low_clocks
|
||||||
|
<< ", close_reason=" << close_reason
|
||||||
<< "\n";
|
<< "\n";
|
||||||
last_lchm_complete = tick_count_ms();
|
last_lchm_complete = tick_count_ms();
|
||||||
}
|
}
|
||||||
@ -824,6 +858,9 @@ int run(const Config& cfg) {
|
|||||||
last_gate = gate;
|
last_gate = gate;
|
||||||
saw_low_before_capture = !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) {
|
} else if (!saw_low_before_capture && !gate) {
|
||||||
saw_low_before_capture = true;
|
saw_low_before_capture = true;
|
||||||
}
|
}
|
||||||
@ -834,12 +871,11 @@ int run(const Config& cfg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!in_lchm && saw_low_before_capture && gate_initialized && !last_gate && gate) {
|
if (!in_lchm && saw_low_before_capture && gate_initialized && !last_gate && gate) {
|
||||||
in_lchm = true;
|
start_lchm();
|
||||||
current.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_lchm && !gate) {
|
if (in_lchm && !gate) {
|
||||||
finalize_lchm();
|
finalize_lchm("di_syn2_fall");
|
||||||
in_lchm = false;
|
in_lchm = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,6 +883,14 @@ int run(const Config& cfg) {
|
|||||||
current.add(di2_high);
|
current.add(di2_high);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
last_gate = gate;
|
last_gate = gate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -856,7 +900,7 @@ int run(const Config& cfg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (console_stop_requested() && in_lchm) {
|
if (console_stop_requested() && in_lchm) {
|
||||||
finalize_lchm();
|
finalize_lchm("user_stop");
|
||||||
}
|
}
|
||||||
|
|
||||||
expect_ok(api, api.StreamsStop(device.hnd), "Stop streams");
|
expect_ok(api, api.StreamsStop(device.hnd), "Stop streams");
|
||||||
|
|||||||
Reference in New Issue
Block a user