This commit is contained in:
awe
2026-04-21 21:09:25 +03:00
parent d36fb23470
commit 4aded6604a

102
main.cpp
View File

@ -506,8 +506,9 @@ void print_help(const char* exe_name) {
<< " live_html/live_json -> live graph files updated as packets arrive outside tty+di1_group_avg fast mode\n" << " live_html/live_json -> live graph files updated as packets arrive outside tty+di1_group_avg fast mode\n"
<< " tty:/tmp/ttyADC_data -> write a continuous legacy 4-word CH1/CH2 stream; with channels:1, CH2 is 0\n" << " tty:/tmp/ttyADC_data -> write a continuous legacy 4-word CH1/CH2 stream; with channels:1, CH2 is 0\n"
<< " di1_group_avg -> with tty + di1:trace, emit one averaged 4-word step per constant DI1 run\n" << " di1_group_avg -> with tty + di1:trace, emit one averaged 4-word step per constant DI1 run\n"
<< " do1_toggle_per_frame -> packet-gated DO1 output: LOW,HIGH,LOW,HIGH per 2-channel frame inside packet\n" << " do1_toggle_per_frame -> packet-gated DO1 state machine in DI_SYN2 window:\n"
<< " outside packet windows DO1 is forced LOW\n" << " force LOW on packet start, then CH1->CH2->toggle DO1 per frame,\n"
<< " repeat until packet end, then force LOW with full DOUT flush\n"
<< " tty+di1_group_avg -> fast stream-only mode: skip CSV/SVG/live outputs and send only averaged tty data\n" << " tty+di1_group_avg -> fast stream-only mode: skip CSV/SVG/live outputs and send only averaged tty data\n"
<< " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\n" << " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\n"
<< "\n" << "\n"
@ -1460,7 +1461,8 @@ int run(const Config& cfg) {
<< ((cfg.channel_count >= 2U) ? phy_channel_name(cfg.mode, cfg.ch2) : std::string("disabled")) << "\n" << ((cfg.channel_count >= 2U) ? phy_channel_name(cfg.mode, cfg.ch2) : std::string("disabled")) << "\n"
<< " DI1 handling: " << di1_mode_to_string(cfg.di1_mode) << "\n" << " DI1 handling: " << di1_mode_to_string(cfg.di1_mode) << "\n"
<< " DO1 toggle per frame: " << " DO1 toggle per frame: "
<< (cfg.do1_toggle_per_frame ? std::string("enabled (packet-gated, LOW-first)") : std::string("disabled")) << "\n" << (cfg.do1_toggle_per_frame ? std::string("enabled (DI_SYN2-gated CH1->CH2->toggle, LOW at start/end)")
: std::string("disabled")) << "\n"
<< " DOUT rate: " << " DOUT rate: "
<< (cfg.do1_toggle_per_frame ? std::to_string(actual_dout_freq_hz) + " Hz" : std::string("disabled")) << "\n" << (cfg.do1_toggle_per_frame ? std::to_string(actual_dout_freq_hz) + " Hz" : std::string("disabled")) << "\n"
<< " ADC range: +/-" << range_to_volts(cfg.range) << " V\n" << " ADC range: +/-" << range_to_volts(cfg.range) << " V\n"
@ -1583,8 +1585,11 @@ int run(const Config& cfg) {
TickMs stats_window_start = capture_loop_start; TickMs stats_window_start = capture_loop_start;
TickMs last_stats_print = capture_loop_start; TickMs last_stats_print = capture_loop_start;
TickMs last_live_update = 0; TickMs last_live_update = 0;
bool do1_next_state_high = false; bool do1_output_high = false;
bool do1_last_queued_high = false; uint64_t do1_packet_toggle_count = 0;
uint64_t do1_packet_queued_words = 0;
uint64_t do1_packet_sent_words_baseline = 0;
uint64_t do1_total_sent_words = 0;
const std::size_t max_pending_dout_words = 1000000U; const std::size_t max_pending_dout_words = 1000000U;
std::vector<uint32_t> dout_prepare_input; std::vector<uint32_t> dout_prepare_input;
std::vector<uint32_t> dout_send_words; std::vector<uint32_t> dout_send_words;
@ -1757,6 +1762,7 @@ int run(const Config& cfg) {
for (std::size_t i = 0; i < sent_words; ++i) { for (std::size_t i = 0; i < sent_words; ++i) {
pending_dout.pop_front(); pending_dout.pop_front();
} }
do1_total_sent_words += static_cast<uint64_t>(sent_words);
if (sent_words < chunk_words) { if (sent_words < chunk_words) {
break; break;
@ -1768,6 +1774,55 @@ int run(const Config& cfg) {
} }
}; };
// Packet-scoped DO1 sequencing: LOW on packet start, toggle after each complete CH1+CH2 frame, LOW on packet end.
auto do1_queue_level = [&](bool do1_high,
uint32_t flush_timeout_ms,
bool require_full_flush,
bool count_for_packet) {
if (!cfg.do1_toggle_per_frame) {
return;
}
append_dout_word(do1_high);
do1_output_high = do1_high;
if (count_for_packet) {
++do1_packet_queued_words;
}
flush_dout_queue(flush_timeout_ms, require_full_flush);
};
auto do1_on_packet_start = [&]() {
if (!cfg.do1_toggle_per_frame) {
return;
}
do1_packet_toggle_count = 0;
do1_packet_queued_words = 0;
do1_packet_sent_words_baseline = do1_total_sent_words;
do1_queue_level(false, 0U, false, true);
};
auto do1_on_frame_complete = [&]() {
if (!cfg.do1_toggle_per_frame || !packet_active) {
return;
}
const bool next_state_high = !do1_output_high;
do1_queue_level(next_state_high, 0U, false, true);
++do1_packet_toggle_count;
};
auto do1_on_packet_end = [&](bool print_packet_stats) {
if (!cfg.do1_toggle_per_frame) {
return;
}
do1_queue_level(false, cfg.recv_timeout_ms, true, true);
if (print_packet_stats) {
const uint64_t packet_sent_words = do1_total_sent_words - do1_packet_sent_words_baseline;
std::cout << " DO1 stats: toggles=" << do1_packet_toggle_count
<< ", queued_words=" << do1_packet_queued_words
<< ", sent_words=" << packet_sent_words
<< ", final_state=" << (do1_output_high ? "HIGH" : "LOW") << "\n";
}
};
auto start_packet = [&]() { auto start_packet = [&]() {
if (packet_active) { if (packet_active) {
return; return;
@ -1784,13 +1839,7 @@ int run(const Config& cfg) {
tty_group_state.reset_packet(); tty_group_state.reset_packet();
append_tty_packet_start(); append_tty_packet_start();
} }
if (cfg.do1_toggle_per_frame) { do1_on_packet_start();
do1_next_state_high = false;
append_dout_word(do1_next_state_high);
do1_last_queued_high = do1_next_state_high;
do1_next_state_high = !do1_next_state_high;
flush_dout_queue(0U, false);
}
packet_active = true; packet_active = true;
}; };
@ -1913,12 +1962,7 @@ int run(const Config& cfg) {
std::cout << "\n"; std::cout << "\n";
} }
if (cfg.do1_toggle_per_frame && do1_last_queued_high) { do1_on_packet_end(frames != 0U);
append_dout_word(false);
do1_last_queued_high = false;
do1_next_state_high = false;
flush_dout_queue(0U, false);
}
packet_active = false; packet_active = false;
packet_avg_steps = 0; packet_avg_steps = 0;
fast_packet_frames = 0; fast_packet_frames = 0;
@ -2181,19 +2225,6 @@ int run(const Config& cfg) {
continue; continue;
} }
if (cfg.do1_toggle_per_frame && (lch == (cfg.channel_count - 1U))) {
if (packet_active) {
append_dout_word(do1_next_state_high);
do1_last_queued_high = do1_next_state_high;
do1_next_state_high = !do1_next_state_high;
flush_dout_queue(0U, false);
} else if (do1_last_queued_high) {
append_dout_word(false);
do1_last_queued_high = false;
flush_dout_queue(0U, false);
}
}
if (!packet_active) { if (!packet_active) {
continue; continue;
} }
@ -2253,6 +2284,10 @@ int run(const Config& cfg) {
} }
} }
if (cfg.do1_toggle_per_frame && (lch == (cfg.channel_count - 1U))) {
do1_on_frame_complete();
}
const std::size_t completed_frames = fast_tty_avg_stream_mode const std::size_t completed_frames = fast_tty_avg_stream_mode
? fast_packet_frames ? fast_packet_frames
: current_packet.frame_count(cfg.channel_count); : current_packet.frame_count(cfg.channel_count);
@ -2280,8 +2315,9 @@ int run(const Config& cfg) {
} }
} }
append_dout_word(false); if (cfg.do1_toggle_per_frame) {
flush_dout_queue(cfg.recv_timeout_ms, true); do1_queue_level(false, cfg.recv_timeout_ms, true, false);
}
expect_ok(api, api.StreamsStop(device.hnd), "Stop streams"); expect_ok(api, api.StreamsStop(device.hnd), "Stop streams");
device.streams_started = false; device.streams_started = false;