add new test start variant
This commit is contained in:
156
main.cpp
156
main.cpp
@ -115,6 +115,7 @@ struct Config {
|
||||
bool di1_group_average = false;
|
||||
bool do1_toggle_per_frame = false;
|
||||
bool do1_noise_subtract = false;
|
||||
bool do1_raw_tty_marked = false;
|
||||
std::optional<uint32_t> noise_avg_steps;
|
||||
};
|
||||
|
||||
@ -473,7 +474,7 @@ void print_help(const char* exe_name) {
|
||||
<< " [di1:zero|trace|ignore]\n"
|
||||
<< " [duration_ms:100] [packet_limit:0] [csv:capture.csv] [svg:capture.svg]\n"
|
||||
<< " [live_html:live_plot.html] [live_json:live_plot.json] [tty:/tmp/ttyADC_data] [di1_group_avg]\n"
|
||||
<< " [do1_toggle_per_frame] [do1_noise_subtract] [noise_avg_steps:N]\n"
|
||||
<< " [do1_toggle_per_frame] [do1_noise_subtract] [do1_raw_tty_marked] [noise_avg_steps:N]\n"
|
||||
<< " [recv_block:32768] [stats_period_ms:1000] [live_update_period_ms:1000] [svg_history_packets:50] [start_wait_ms:10000]\n"
|
||||
<< " [buffer_words:8388608] [step_words:32768]\n"
|
||||
<< " [pullup_syn1] [pullup_syn2] [pulldown_conv_in] [pulldown_start_in]\n"
|
||||
@ -515,9 +516,13 @@ void print_help(const char* exe_name) {
|
||||
<< " (expected DO1->DI1 loopback) to take previous DI1=HIGH step as baseline and subtract it\n"
|
||||
<< " from useful steps before tty output\n"
|
||||
<< " (only corrected DO1=LOW steps are sent to tty)\n"
|
||||
<< " do1_raw_tty_marked -> with tty + do1_toggle_per_frame + phase profile, emit raw per-step averages\n"
|
||||
<< " from both phase channels, tagged by DI1 level from synchronous DIN:\n"
|
||||
<< " word0=0x00A3 for DO1/DI1 LOW, word0=0x00A4 for DO1/DI1 HIGH\n"
|
||||
<< " no baseline subtraction is applied in this mode\n"
|
||||
<< " noise_avg_steps:N -> kept for compatibility in do1_noise_subtract mode (current baseline is previous HIGH step)\n"
|
||||
<< " (still required when do1_noise_subtract is enabled)\n"
|
||||
<< " tty fast stream-only modes -> di1_group_avg or do1_noise_subtract; skip CSV/SVG/live outputs\n"
|
||||
<< " tty fast stream-only modes -> di1_group_avg, do1_noise_subtract, do1_raw_tty_marked; skip CSV/SVG/live outputs\n"
|
||||
<< " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\n"
|
||||
<< "\n"
|
||||
<< "Profiles:\n"
|
||||
@ -546,6 +551,8 @@ void print_help(const char* exe_name) {
|
||||
<< "per constant DI1 run while keeping the current ring-buffered binary frame format.\n"
|
||||
<< "For DO1 subtraction with loopback, use do1_toggle_per_frame + do1_noise_subtract + noise_avg_steps:N;\n"
|
||||
<< "DI1 from synchronous DIN is used as the actual DO1 state marker, and each LOW step subtracts previous HIGH step.\n"
|
||||
<< "For raw loopback diagnostics, use do1_toggle_per_frame + do1_raw_tty_marked in phase profile;\n"
|
||||
<< "steps are tagged in word0 as 0x00A3 (LOW) / 0x00A4 (HIGH) with raw per-step channel averages.\n"
|
||||
<< "Amplitude mode keeps the raw AD8317 voltage as captured on X1; no inversion is applied.\n"
|
||||
<< "\n"
|
||||
<< "Phase profile example:\n"
|
||||
@ -568,6 +575,12 @@ void print_help(const char* exe_name) {
|
||||
<< " " << exe_name
|
||||
<< " profile:amplitude clock:internal internal_ref_hz:2000000 start:di_syn2_rise stop:di_syn2_fall"
|
||||
<< " sample_clock_hz:max range:0.2 do1_toggle_per_frame do1_noise_subtract noise_avg_steps:16"
|
||||
<< " duration_ms:100 packet_limit:0 tty:/tmp/ttyADC_data\n"
|
||||
<< "\n"
|
||||
<< "Phase TTY raw DO1-level tagged example:\n"
|
||||
<< " " << exe_name
|
||||
<< " profile:phase clock:internal internal_ref_hz:2000000 start:di_syn2_rise stop:di_syn2_fall"
|
||||
<< " sample_clock_hz:max do1_toggle_per_frame do1_raw_tty_marked"
|
||||
<< " duration_ms:100 packet_limit:0 tty:/tmp/ttyADC_data\n";
|
||||
}
|
||||
|
||||
@ -656,6 +669,10 @@ Config parse_args(int argc, char** argv) {
|
||||
cfg.do1_noise_subtract = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "do1_raw_tty_marked") {
|
||||
cfg.do1_raw_tty_marked = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "do1_toggle_per_frame") {
|
||||
cfg.do1_toggle_per_frame = true;
|
||||
continue;
|
||||
@ -775,7 +792,7 @@ Config parse_args(int argc, char** argv) {
|
||||
cfg.live_update_period_ms = std::max<uint32_t>(cfg.live_update_period_ms, 1000U);
|
||||
}
|
||||
}
|
||||
if (cfg.tty_path && (cfg.di1_group_average || cfg.do1_noise_subtract)) {
|
||||
if (cfg.tty_path && (cfg.di1_group_average || cfg.do1_noise_subtract || cfg.do1_raw_tty_marked)) {
|
||||
if (!cfg.recv_block_specified) {
|
||||
cfg.recv_block_words = std::max<uint32_t>(cfg.recv_block_words, 65536U);
|
||||
}
|
||||
@ -798,6 +815,9 @@ Config parse_args(int argc, char** argv) {
|
||||
if (cfg.di1_group_average && (cfg.di1_mode != Di1Mode::Trace)) {
|
||||
fail("di1_group_avg requires di1:trace");
|
||||
}
|
||||
if (cfg.di1_group_average && cfg.do1_raw_tty_marked) {
|
||||
fail("do1_raw_tty_marked is incompatible with di1_group_avg");
|
||||
}
|
||||
if (cfg.do1_noise_subtract) {
|
||||
if (!cfg.tty_path.has_value()) {
|
||||
fail("do1_noise_subtract requires tty:<path>");
|
||||
@ -808,6 +828,9 @@ Config parse_args(int argc, char** argv) {
|
||||
if (cfg.di1_group_average) {
|
||||
fail("do1_noise_subtract is incompatible with di1_group_avg");
|
||||
}
|
||||
if (cfg.do1_raw_tty_marked) {
|
||||
fail("do1_noise_subtract is incompatible with do1_raw_tty_marked");
|
||||
}
|
||||
if (!cfg.noise_avg_steps.has_value()) {
|
||||
fail("do1_noise_subtract requires noise_avg_steps:N");
|
||||
}
|
||||
@ -817,6 +840,20 @@ Config parse_args(int argc, char** argv) {
|
||||
} else if (cfg.noise_avg_steps.has_value()) {
|
||||
fail("noise_avg_steps:N can only be used together with do1_noise_subtract");
|
||||
}
|
||||
if (cfg.do1_raw_tty_marked) {
|
||||
if (!cfg.tty_path.has_value()) {
|
||||
fail("do1_raw_tty_marked requires tty:<path>");
|
||||
}
|
||||
if (!cfg.do1_toggle_per_frame) {
|
||||
fail("do1_raw_tty_marked requires do1_toggle_per_frame");
|
||||
}
|
||||
if (cfg.mode != X502_LCH_MODE_DIFF) {
|
||||
fail("do1_raw_tty_marked requires phase configuration: mode:diff channels:2 ch1:2 ch2:3");
|
||||
}
|
||||
if ((cfg.channel_count != 2U) || (cfg.ch1 != 2U) || (cfg.ch2 != 3U)) {
|
||||
fail("do1_raw_tty_marked requires phase configuration: mode:diff channels:2 ch1:2 ch2:3");
|
||||
}
|
||||
}
|
||||
if (sync_uses_di_syn1(cfg.sync_mode) && sync_uses_di_syn1(cfg.sync_start_mode)) {
|
||||
fail("clock and start cannot both use DI_SYN1; use start_in or immediate start");
|
||||
}
|
||||
@ -1231,6 +1268,51 @@ struct TtyGroupAverageState {
|
||||
}
|
||||
};
|
||||
|
||||
struct Do1RawTaggedState {
|
||||
std::array<double, 2> step_sum {};
|
||||
std::array<uint32_t, 2> step_count {};
|
||||
bool step_level_initialized = false;
|
||||
bool step_di1_high = false;
|
||||
uint16_t next_index = 1;
|
||||
|
||||
void clear_step() {
|
||||
step_sum = {};
|
||||
step_count = {};
|
||||
}
|
||||
|
||||
void reset_packet() {
|
||||
clear_step();
|
||||
step_level_initialized = false;
|
||||
step_di1_high = false;
|
||||
next_index = 1;
|
||||
}
|
||||
|
||||
void start_new_step(bool di1_high) {
|
||||
clear_step();
|
||||
step_di1_high = di1_high;
|
||||
step_level_initialized = true;
|
||||
}
|
||||
|
||||
void add_sample(uint32_t lch, double raw_code) {
|
||||
if (lch >= step_sum.size()) {
|
||||
return;
|
||||
}
|
||||
step_sum[lch] += raw_code;
|
||||
++step_count[lch];
|
||||
}
|
||||
|
||||
bool has_complete_step(uint32_t channel_count) const {
|
||||
return (step_count[0] != 0U) && ((channel_count <= 1U) || (step_count[1] != 0U));
|
||||
}
|
||||
|
||||
double step_average(uint32_t lch) const {
|
||||
if ((lch >= step_sum.size()) || (step_count[lch] == 0U)) {
|
||||
return 0.0;
|
||||
}
|
||||
return step_sum[lch] / static_cast<double>(step_count[lch]);
|
||||
}
|
||||
};
|
||||
|
||||
struct Do1NoiseSubtractState {
|
||||
std::array<double, 2> step_sum {};
|
||||
std::array<uint32_t, 2> step_count {};
|
||||
@ -1605,13 +1687,16 @@ int run(const Config& cfg) {
|
||||
std::unique_ptr<TtyProtocolWriter> tty_writer;
|
||||
const bool tty_di1_group_average = cfg.tty_path.has_value() && cfg.di1_group_average;
|
||||
const bool tty_do1_noise_subtract = cfg.tty_path.has_value() && cfg.do1_noise_subtract;
|
||||
const bool fast_tty_avg_stream_mode = tty_di1_group_average || tty_do1_noise_subtract;
|
||||
const bool tty_do1_raw_tty_marked = cfg.tty_path.has_value() && cfg.do1_raw_tty_marked;
|
||||
const bool fast_tty_avg_stream_mode =
|
||||
tty_di1_group_average || tty_do1_noise_subtract || tty_do1_raw_tty_marked;
|
||||
const uint16_t tty_packet_start_marker =
|
||||
(cfg.profile == CaptureProfile::Amplitude) ? 0x001AU : 0x000AU;
|
||||
const std::string fast_tty_mode_name =
|
||||
tty_do1_noise_subtract ? std::string("do1_noise_subtract")
|
||||
: (tty_di1_group_average ? std::string("di1_group_avg")
|
||||
: std::string("none"));
|
||||
const std::string fast_tty_mode_name = tty_do1_noise_subtract
|
||||
? std::string("do1_noise_subtract")
|
||||
: (tty_do1_raw_tty_marked ? std::string("do1_raw_tty_marked")
|
||||
: (tty_di1_group_average ? std::string("di1_group_avg")
|
||||
: std::string("none")));
|
||||
if (cfg.tty_path) {
|
||||
const uint64_t typical_tty_batch_frames = (static_cast<uint64_t>(read_capacity_words) + 1U) / 2U;
|
||||
const uint64_t typical_tty_batch_bytes = typical_tty_batch_frames * sizeof(uint16_t) * 4U;
|
||||
@ -1622,7 +1707,7 @@ int run(const Config& cfg) {
|
||||
}
|
||||
tty_ring_capacity_bytes = static_cast<std::size_t>(tty_ring_bytes_u64);
|
||||
tty_writer = std::make_unique<TtyProtocolWriter>(*cfg.tty_path, tty_ring_capacity_bytes);
|
||||
if (!tty_di1_group_average && !tty_do1_noise_subtract) {
|
||||
if (!tty_di1_group_average && !tty_do1_noise_subtract && !tty_do1_raw_tty_marked) {
|
||||
tty_writer->emit_packet_start(tty_packet_start_marker);
|
||||
}
|
||||
}
|
||||
@ -1641,8 +1726,11 @@ int run(const Config& cfg) {
|
||||
<< " input buffer words: " << cfg.input_buffer_words << "\n"
|
||||
<< " tty di1_group_avg: " << (tty_di1_group_average ? "enabled" : "disabled") << "\n"
|
||||
<< " tty do1_noise_subtract: " << (tty_do1_noise_subtract ? "enabled" : "disabled") << "\n"
|
||||
<< " tty do1_raw_tty_marked: " << (tty_do1_raw_tty_marked ? "enabled" : "disabled") << "\n"
|
||||
<< " do1_noise_subtract marker: "
|
||||
<< (tty_do1_noise_subtract ? "DI1 from DIN (DO1->DI1 loopback expected)" : "n/a") << "\n"
|
||||
<< " do1_raw_tty_marked markers: "
|
||||
<< (tty_do1_raw_tty_marked ? "0x00A3=LOW, 0x00A4=HIGH (DI1 from DIN)" : "n/a") << "\n"
|
||||
<< " noise_avg_steps: "
|
||||
<< (tty_do1_noise_subtract ? std::to_string(*cfg.noise_avg_steps) : std::string("n/a")) << "\n"
|
||||
<< " stream-only note: ignoring csv/svg/live_html/live_json/live_update_period_ms/svg_history_packets\n";
|
||||
@ -1665,6 +1753,11 @@ int run(const Config& cfg) {
|
||||
: (cfg.do1_noise_subtract ? std::string("requested, tty disabled")
|
||||
: std::string("disabled")))
|
||||
<< "\n"
|
||||
<< " tty do1_raw_tty_marked: "
|
||||
<< (tty_do1_raw_tty_marked ? std::string("enabled")
|
||||
: (cfg.do1_raw_tty_marked ? std::string("requested, tty disabled")
|
||||
: std::string("disabled")))
|
||||
<< "\n"
|
||||
<< " SVG history packets kept in RAM: "
|
||||
<< ((cfg.svg_history_packets == 0U) ? std::string("all (unbounded)") : std::to_string(cfg.svg_history_packets))
|
||||
<< "\n";
|
||||
@ -1731,6 +1824,7 @@ int run(const Config& cfg) {
|
||||
PacketAccumulator current_packet;
|
||||
TtyContinuousState tty_state;
|
||||
TtyGroupAverageState tty_group_state;
|
||||
Do1RawTaggedState tty_do1_raw_state;
|
||||
Do1NoiseSubtractState tty_do1_noise_state;
|
||||
std::vector<uint16_t> tty_frame_words;
|
||||
tty_frame_words.reserve(static_cast<std::size_t>(read_capacity_words) * 2U + 16U);
|
||||
@ -1864,6 +1958,33 @@ int run(const Config& cfg) {
|
||||
++packet_avg_steps;
|
||||
};
|
||||
|
||||
auto append_tty_do1_raw_marked_step = [&]() {
|
||||
if (!tty_do1_raw_tty_marked) {
|
||||
return;
|
||||
}
|
||||
if (!tty_do1_raw_state.step_level_initialized) {
|
||||
return;
|
||||
}
|
||||
if (!tty_do1_raw_state.has_complete_step(cfg.channel_count)) {
|
||||
tty_do1_raw_state.clear_step();
|
||||
return;
|
||||
}
|
||||
if (tty_do1_raw_state.next_index >= 0xFFFFU) {
|
||||
fail("TTY protocol step index overflow within packet");
|
||||
}
|
||||
|
||||
const double ch1_avg = tty_do1_raw_state.step_average(0U);
|
||||
const double ch2_avg = (cfg.channel_count <= 1U) ? 0.0 : tty_do1_raw_state.step_average(1U);
|
||||
const uint16_t marker = tty_do1_raw_state.step_di1_high ? 0x00A4U : 0x00A3U;
|
||||
append_tty_frame(marker,
|
||||
tty_do1_raw_state.next_index,
|
||||
static_cast<uint16_t>(pack_raw_code_to_int16(ch1_avg)),
|
||||
static_cast<uint16_t>(pack_raw_code_to_int16(ch2_avg)));
|
||||
++tty_do1_raw_state.next_index;
|
||||
++packet_avg_steps;
|
||||
tty_do1_raw_state.clear_step();
|
||||
};
|
||||
|
||||
auto append_tty_do1_subtracted_step = [&]() {
|
||||
if (!tty_do1_noise_subtract) {
|
||||
return;
|
||||
@ -1950,6 +2071,9 @@ int run(const Config& cfg) {
|
||||
if (tty_di1_group_average) {
|
||||
tty_group_state.reset_packet();
|
||||
append_tty_packet_start();
|
||||
} else if (tty_do1_raw_tty_marked) {
|
||||
tty_do1_raw_state.reset_packet();
|
||||
append_tty_packet_start();
|
||||
} else if (tty_do1_noise_subtract) {
|
||||
tty_do1_noise_state.reset_packet();
|
||||
append_tty_packet_start();
|
||||
@ -1969,6 +2093,9 @@ int run(const Config& cfg) {
|
||||
if (tty_di1_group_average) {
|
||||
append_tty_group_step();
|
||||
tty_group_state.clear_step();
|
||||
} else if (tty_do1_raw_tty_marked) {
|
||||
append_tty_do1_raw_marked_step();
|
||||
tty_do1_raw_state.clear_step();
|
||||
} else if (tty_do1_noise_subtract) {
|
||||
// Finalize the last DI1 run in packet, drop only incomplete channel tuples.
|
||||
append_tty_do1_subtracted_step();
|
||||
@ -2091,6 +2218,8 @@ int run(const Config& cfg) {
|
||||
}
|
||||
if (tty_di1_group_average) {
|
||||
tty_group_state.clear_step();
|
||||
} else if (tty_do1_raw_tty_marked) {
|
||||
tty_do1_raw_state.clear_step();
|
||||
} else if (tty_do1_noise_subtract) {
|
||||
tty_do1_noise_state.clear_step();
|
||||
}
|
||||
@ -2354,6 +2483,13 @@ int run(const Config& cfg) {
|
||||
if (tty_di1_group_average && di1_changed) {
|
||||
append_tty_group_step();
|
||||
tty_group_state.clear_step();
|
||||
} else if (tty_do1_raw_tty_marked) {
|
||||
if (!tty_do1_raw_state.step_level_initialized) {
|
||||
tty_do1_raw_state.start_new_step(di1_level);
|
||||
} else if (di1_changed) {
|
||||
append_tty_do1_raw_marked_step();
|
||||
tty_do1_raw_state.start_new_step(di1_level);
|
||||
}
|
||||
} else if (tty_do1_noise_subtract) {
|
||||
if (!tty_do1_noise_state.step_level_initialized) {
|
||||
tty_do1_noise_state.start_new_step(di1_level);
|
||||
@ -2374,6 +2510,8 @@ int run(const Config& cfg) {
|
||||
if (fast_packet_frames < target_frames) {
|
||||
if (tty_do1_noise_subtract) {
|
||||
tty_do1_noise_state.add_sample(lch, adc_raw_value);
|
||||
} else if (tty_do1_raw_tty_marked) {
|
||||
tty_do1_raw_state.add_sample(lch, adc_raw_value);
|
||||
} else {
|
||||
tty_group_state.add_sample(lch, adc_raw_value);
|
||||
}
|
||||
|
||||
33
run_do1_raw_tty_marked.sh
Executable file
33
run_do1_raw_tty_marked.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
BIN="${BIN:-./main.exe}"
|
||||
TTY_PATH="${TTY_PATH:-/tmp/ttyADC_data}"
|
||||
LIB_DIR="${LIB_DIR:-$HOME/.local/lib}"
|
||||
|
||||
if [[ ! -x "$BIN" ]]; then
|
||||
echo "Binary '$BIN' not found or not executable. Run ./build_main.sh first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$LIB_DIR" ]]; then
|
||||
export LD_LIBRARY_PATH="${LIB_DIR}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
fi
|
||||
|
||||
exec "$BIN" \
|
||||
profile:phase \
|
||||
clock:internal \
|
||||
internal_ref_hz:2000000 \
|
||||
start:di_syn2_rise \
|
||||
stop:di_syn2_fall \
|
||||
sample_clock_hz:max \
|
||||
range:5 \
|
||||
duration_ms:100 \
|
||||
packet_limit:0 \
|
||||
do1_toggle_per_frame \
|
||||
do1_raw_tty_marked \
|
||||
"tty:${TTY_PATH}" \
|
||||
"$@"
|
||||
@ -77,6 +77,7 @@ namespace {
|
||||
|
||||
constexpr std::size_t kFrameWordCount = 4U;
|
||||
constexpr std::size_t kFrameByteCount = kFrameWordCount * sizeof(uint16_t);
|
||||
constexpr std::size_t kWriteBatchFrames = 256U;
|
||||
|
||||
using EncodedFrame = std::array<std::uint8_t, kFrameByteCount>;
|
||||
|
||||
@ -377,7 +378,8 @@ void TtyProtocolWriter::enqueue_frame(uint16_t word0, uint16_t word1, uint16_t w
|
||||
|
||||
void TtyProtocolWriter::worker_loop() {
|
||||
for (;;) {
|
||||
EncodedFrame frame {};
|
||||
std::array<std::uint8_t, kWriteBatchFrames * kFrameByteCount> write_batch;
|
||||
std::size_t batch_count = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(impl_->mutex);
|
||||
impl_->data_ready_cv.wait(lock, [this]() {
|
||||
@ -388,13 +390,17 @@ void TtyProtocolWriter::worker_loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
frame = impl_->ring[impl_->head];
|
||||
impl_->head = (impl_->head + 1U) % impl_->capacity_frames;
|
||||
--impl_->size;
|
||||
batch_count = std::min<std::size_t>(impl_->size, kWriteBatchFrames);
|
||||
for (std::size_t i = 0; i < batch_count; ++i) {
|
||||
const EncodedFrame& frame = impl_->ring[impl_->head];
|
||||
std::memcpy(write_batch.data() + (i * kFrameByteCount), frame.data(), kFrameByteCount);
|
||||
impl_->head = (impl_->head + 1U) % impl_->capacity_frames;
|
||||
--impl_->size;
|
||||
}
|
||||
}
|
||||
|
||||
const std::uint8_t* bytes = frame.data();
|
||||
std::size_t remaining = frame.size();
|
||||
const std::uint8_t* bytes = write_batch.data();
|
||||
std::size_t remaining = batch_count * kFrameByteCount;
|
||||
while (remaining != 0U) {
|
||||
const ssize_t written = ::write(impl_->fd, bytes, remaining);
|
||||
if (written < 0) {
|
||||
@ -436,7 +442,7 @@ void TtyProtocolWriter::worker_loop() {
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(impl_->mutex);
|
||||
++impl_->stats.frames_written;
|
||||
impl_->stats.frames_written += static_cast<std::uint64_t>(batch_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user