diff --git a/main.cpp b/main.cpp index 530388e..de578e0 100644 --- a/main.cpp +++ b/main.cpp @@ -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" << " 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" - << " do1_toggle_per_frame -> packet-gated DO1 output: LOW,HIGH,LOW,HIGH per 2-channel frame inside packet\n" - << " outside packet windows DO1 is forced LOW\n" + << " do1_toggle_per_frame -> packet-gated DO1 state machine in DI_SYN2 window:\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" << " If sample_clock_hz is omitted together with clock:internal, the maximum ADC speed is used\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" << " DI1 handling: " << di1_mode_to_string(cfg.di1_mode) << "\n" << " 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: " << (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" @@ -1583,8 +1585,11 @@ int run(const Config& cfg) { TickMs stats_window_start = capture_loop_start; TickMs last_stats_print = capture_loop_start; TickMs last_live_update = 0; - bool do1_next_state_high = false; - bool do1_last_queued_high = false; + bool do1_output_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; std::vector dout_prepare_input; std::vector dout_send_words; @@ -1757,6 +1762,7 @@ int run(const Config& cfg) { for (std::size_t i = 0; i < sent_words; ++i) { pending_dout.pop_front(); } + do1_total_sent_words += static_cast(sent_words); if (sent_words < chunk_words) { 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 = [&]() { if (packet_active) { return; @@ -1784,13 +1839,7 @@ int run(const Config& cfg) { tty_group_state.reset_packet(); append_tty_packet_start(); } - if (cfg.do1_toggle_per_frame) { - 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); - } + do1_on_packet_start(); packet_active = true; }; @@ -1913,12 +1962,7 @@ int run(const Config& cfg) { std::cout << "\n"; } - if (cfg.do1_toggle_per_frame && do1_last_queued_high) { - append_dout_word(false); - do1_last_queued_high = false; - do1_next_state_high = false; - flush_dout_queue(0U, false); - } + do1_on_packet_end(frames != 0U); packet_active = false; packet_avg_steps = 0; fast_packet_frames = 0; @@ -2181,19 +2225,6 @@ int run(const Config& cfg) { 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) { 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 ? fast_packet_frames : current_packet.frame_count(cfg.channel_count); @@ -2280,8 +2315,9 @@ int run(const Config& cfg) { } } - append_dout_word(false); - flush_dout_queue(cfg.recv_timeout_ms, true); + if (cfg.do1_toggle_per_frame) { + do1_queue_level(false, cfg.recv_timeout_ms, true, false); + } expect_ok(api, api.StreamsStop(device.hnd), "Stop streams"); device.streams_started = false;