From 94df44239561bfe10953cf90c0b18991b70b0dfd Mon Sep 17 00:00:00 2001 From: awe Date: Tue, 21 Apr 2026 22:55:20 +0300 Subject: [PATCH] fix --- main.cpp | 238 +++++++++++++------------------------------------------ 1 file changed, 54 insertions(+), 184 deletions(-) diff --git a/main.cpp b/main.cpp index a90672b..6fdbf23 100644 --- a/main.cpp +++ b/main.cpp @@ -506,9 +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 -> DI_SYN2 level-gated DO1 modulation:\n" - << " DO1 is forced LOW when DI_SYN2 is LOW,\n" - << " while DI_SYN2 is HIGH, DO1 toggles every 3 ADC ticks\n" + << " do1_toggle_per_frame -> hardware cyclic DO1 pattern in module memory:\n" + << " DO1 outputs 000111... continuously (toggle every 3 ADC ticks)\n" + << " without per-tick DOUT updates from PC\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" @@ -762,17 +762,6 @@ Config parse_args(int argc, char** argv) { cfg.input_buffer_words = std::max(cfg.input_buffer_words, 16U * 1024U * 1024U); } } - if (cfg.do1_toggle_per_frame) { - if (!cfg.recv_block_specified) { - cfg.recv_block_words = 32U; - } - if (!cfg.input_step_specified) { - cfg.input_step_words = 32U; - } - if (!cfg.input_buffer_specified) { - cfg.input_buffer_words = std::max(cfg.input_buffer_words, 256U * 1024U); - } - } if (cfg.recv_block_words == 0) { fail("recv_block must be > 0"); } @@ -785,9 +774,6 @@ 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.do1_toggle_per_frame && (cfg.channel_count != 2U)) { - fail("do1_toggle_per_frame requires channels:2"); - } 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"); } @@ -938,6 +924,7 @@ struct Api { decltype(&X502_GetAdcFreq) GetAdcFreq = nullptr; decltype(&X502_SetDinFreq) SetDinFreq = nullptr; decltype(&X502_SetOutFreq) SetOutFreq = nullptr; + decltype(&X502_AsyncOutDig) AsyncOutDig = nullptr; decltype(&X502_SetRefFreq) SetRefFreq = nullptr; decltype(&X502_SetStreamBufSize) SetStreamBufSize = nullptr; decltype(&X502_SetStreamStep) SetStreamStep = nullptr; @@ -948,7 +935,9 @@ struct Api { decltype(&X502_StreamsStart) StreamsStart = nullptr; decltype(&X502_GetRecvReadyCount) GetRecvReadyCount = nullptr; decltype(&X502_Recv) Recv = nullptr; - decltype(&X502_PreloadStart) PreloadStart = nullptr; + decltype(&X502_OutCycleLoadStart) OutCycleLoadStart = nullptr; + decltype(&X502_OutCycleSetup) OutCycleSetup = nullptr; + decltype(&X502_OutCycleStop) OutCycleStop = nullptr; decltype(&X502_Send) Send = nullptr; decltype(&X502_PrepareData) PrepareData = nullptr; decltype(&X502_ProcessData) ProcessData = nullptr; @@ -995,6 +984,7 @@ struct Api { GetAdcFreq = load_symbol(x502_module, "X502_GetAdcFreq"); SetDinFreq = load_symbol(x502_module, "X502_SetDinFreq"); SetOutFreq = load_symbol(x502_module, "X502_SetOutFreq"); + AsyncOutDig = load_symbol(x502_module, "X502_AsyncOutDig"); SetRefFreq = load_symbol(x502_module, "X502_SetRefFreq"); SetStreamBufSize = load_symbol(x502_module, "X502_SetStreamBufSize"); SetStreamStep = load_symbol(x502_module, "X502_SetStreamStep"); @@ -1005,7 +995,9 @@ struct Api { StreamsStart = load_symbol(x502_module, "X502_StreamsStart"); GetRecvReadyCount = load_symbol(x502_module, "X502_GetRecvReadyCount"); Recv = load_symbol(x502_module, "X502_Recv"); - PreloadStart = load_symbol(x502_module, "X502_PreloadStart"); + OutCycleLoadStart = try_load_symbol(x502_module, "X502_OutCycleLoadStart"); + OutCycleSetup = try_load_symbol(x502_module, "X502_OutCycleSetup"); + OutCycleStop = try_load_symbol(x502_module, "X502_OutCycleStop"); Send = load_symbol(x502_module, "X502_Send"); PrepareData = load_symbol(x502_module, "X502_PrepareData"); ProcessData = load_symbol(x502_module, "X502_ProcessData"); @@ -1042,6 +1034,7 @@ constexpr uint32_t kE502Digital1Mask = (static_cast(1U) << 0U); constexpr uint32_t kE502Digital2Mask = (static_cast(1U) << 1U); constexpr uint32_t kE502Do1Mask = (static_cast(1U) << 0U); constexpr uint32_t kDo1TogglePeriodTicks = 3U; +constexpr uint32_t kDo1CyclePatternWords = kDo1TogglePeriodTicks * 2U; constexpr uint32_t kStreamInputAdcFlag = 0x80000000U; constexpr uint32_t kStreamInputCalibratedAdcFlag = 0x40000000U; @@ -1463,7 +1456,7 @@ 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 (DI_SYN2 level-gated, toggle each 3 ADC ticks, LOW outside HIGH)") + << (cfg.do1_toggle_per_frame ? std::string("enabled (hardware cyclic 000111..., toggle every 3 ADC ticks)") : std::string("disabled")) << "\n" << " DOUT rate: " << (cfg.do1_toggle_per_frame ? std::to_string(actual_dout_freq_hz) + " Hz" : std::string("disabled")) << "\n" @@ -1530,20 +1523,46 @@ int run(const Config& cfg) { std::cerr << "Warning: Ctrl+C handler could not be installed; continuous mode may stop abruptly.\n"; } + std::array do1_cycle_pattern {}; + std::array do1_cycle_encoded {}; if (cfg.do1_toggle_per_frame) { - expect_ok(api, api.PreloadStart(device.hnd), "Start DOUT preload"); - const uint32_t preload_low_word = 0U; - uint32_t preload_encoded_word = 0U; + if ((api.OutCycleLoadStart == nullptr) || (api.OutCycleSetup == nullptr) || (api.OutCycleStop == nullptr)) { + fail("do1_toggle_per_frame requires X502_OutCycle* API support (cyclic output mode)"); + } + + for (std::size_t i = 0; i < do1_cycle_pattern.size(); ++i) { + do1_cycle_pattern[i] = (i < kDo1TogglePeriodTicks) ? 0U : kE502Do1Mask; + } + expect_ok(api, - api.PrepareData(device.hnd, nullptr, nullptr, &preload_low_word, 1U, 0, &preload_encoded_word), - "Prepare initial DOUT LOW"); - const int32_t preload_sent = api.Send(device.hnd, &preload_encoded_word, 1U, cfg.recv_timeout_ms); - if (preload_sent < 0) { - fail("Send initial DOUT LOW: " + x502_error(api, preload_sent)); - } - if (preload_sent != 1) { - fail("Send initial DOUT LOW did not transmit exactly one word"); + api.OutCycleLoadStart(device.hnd, static_cast(do1_cycle_pattern.size())), + "Allocate cyclic DOUT buffer"); + expect_ok(api, + api.PrepareData(device.hnd, + nullptr, + nullptr, + do1_cycle_pattern.data(), + static_cast(do1_cycle_pattern.size()), + 0, + do1_cycle_encoded.data()), + "Prepare cyclic DOUT pattern"); + + std::size_t sent_cycle_words = 0; + while (sent_cycle_words < do1_cycle_encoded.size()) { + const int32_t sent = api.Send(device.hnd, + do1_cycle_encoded.data() + sent_cycle_words, + static_cast(do1_cycle_encoded.size() - sent_cycle_words), + cfg.recv_timeout_ms); + if (sent < 0) { + fail("Send cyclic DOUT pattern: " + x502_error(api, sent)); + } + if (sent == 0) { + fail("Send cyclic DOUT pattern timed out before all words were queued"); + } + sent_cycle_words += static_cast(sent); } + + expect_ok(api, api.OutCycleSetup(device.hnd, X502_OUT_CYCLE_FLAGS_WAIT_DONE), "Activate cyclic DOUT pattern"); } expect_ok(api, api.StreamsStart(device.hnd), "Start streams"); @@ -1556,7 +1575,6 @@ int run(const Config& cfg) { std::deque pending_adc; std::deque pending_adc_raw; std::deque pending_din; - std::deque pending_dout; std::deque packets; PacketAccumulator current_packet; TtyContinuousState tty_state; @@ -1587,20 +1605,6 @@ 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_output_high = false; - bool do1_gate_active = false; - uint32_t do1_ticks_since_toggle = 0; - 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; - if (cfg.do1_toggle_per_frame) { - dout_prepare_input.resize(read_capacity_words); - dout_send_words.resize(read_capacity_words); - } uint64_t total_raw_words = 0; uint64_t total_adc_samples = 0; @@ -1717,132 +1721,6 @@ int run(const Config& cfg) { } }; - auto append_dout_word = [&](bool do1_high) { - if (!cfg.do1_toggle_per_frame) { - return; - } - pending_dout.push_back(do1_high ? kE502Do1Mask : 0U); - if (pending_dout.size() > max_pending_dout_words) { - fail("Internal DOUT backlog grew too large"); - } - }; - - auto flush_dout_queue = [&](uint32_t send_timeout_ms, bool require_full_flush) { - if (!cfg.do1_toggle_per_frame || pending_dout.empty()) { - return; - } - - while (!pending_dout.empty()) { - const std::size_t chunk_words = std::min(pending_dout.size(), dout_prepare_input.size()); - for (std::size_t i = 0; i < chunk_words; ++i) { - dout_prepare_input[i] = pending_dout[i]; - } - - expect_ok(api, - api.PrepareData(device.hnd, - nullptr, - nullptr, - dout_prepare_input.data(), - static_cast(chunk_words), - 0, - dout_send_words.data()), - "Prepare DOUT data"); - - std::size_t sent_words = 0; - while (sent_words < chunk_words) { - const int32_t sent = api.Send(device.hnd, - dout_send_words.data() + sent_words, - static_cast(chunk_words - sent_words), - send_timeout_ms); - if (sent < 0) { - fail("Send DOUT data: " + x502_error(api, sent)); - } - if (sent == 0) { - break; - } - sent_words += static_cast(sent); - } - - 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; - } - } - - if (require_full_flush && !pending_dout.empty()) { - fail("DOUT queue could not be fully flushed before shutdown"); - } - }; - - auto do1_queue_level = [&](bool do1_high, bool count_for_packet) { - if (!cfg.do1_toggle_per_frame) { - return; - } - append_dout_word(do1_high); - if (count_for_packet) { - ++do1_packet_queued_words; - } - if (do1_output_high != do1_high) { - if (count_for_packet) { - ++do1_packet_toggle_count; - } - do1_output_high = do1_high; - } else { - do1_output_high = do1_high; - } - }; - - 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; - }; - - auto do1_on_tick = [&](bool di_syn2_high) { - if (!cfg.do1_toggle_per_frame) { - return; - } - bool next_state_high = false; - if (di_syn2_high) { - if (!do1_gate_active) { - do1_gate_active = true; - do1_ticks_since_toggle = 0U; - next_state_high = true; - } else if ((do1_ticks_since_toggle + 1U) >= kDo1TogglePeriodTicks) { - do1_ticks_since_toggle = 0U; - next_state_high = !do1_output_high; - } else { - ++do1_ticks_since_toggle; - next_state_high = do1_output_high; - } - } else { - do1_gate_active = false; - do1_ticks_since_toggle = 0U; - next_state_high = false; - } - do1_queue_level(next_state_high, packet_active); - }; - - auto do1_on_packet_end = [&](bool print_packet_stats) { - if (!cfg.do1_toggle_per_frame) { - return; - } - if (print_packet_stats) { - const uint64_t packet_sent_words = do1_total_sent_words - do1_packet_sent_words_baseline; - std::cout << " DO1 stats: transitions=" << do1_packet_toggle_count - << ", queued_words=" << do1_packet_queued_words - << ", sent_words=" << packet_sent_words - << ", state=" << (do1_output_high ? "HIGH" : "LOW") << "\n"; - } - }; - auto start_packet = [&]() { if (packet_active) { return; @@ -1859,7 +1737,6 @@ int run(const Config& cfg) { tty_group_state.reset_packet(); append_tty_packet_start(); } - do1_on_packet_start(); packet_active = true; }; @@ -1982,7 +1859,6 @@ int run(const Config& cfg) { std::cout << "\n"; } - do1_on_packet_end(frames != 0U); packet_active = false; packet_avg_steps = 0; fast_packet_frames = 0; @@ -2016,9 +1892,6 @@ int run(const Config& cfg) { uint32_t recv_request_words = cfg.recv_block_words; uint32_t recv_timeout_ms = cfg.recv_timeout_ms; - if (cfg.do1_toggle_per_frame) { - recv_timeout_ms = 0U; - } uint32_t ready_words = 0; const int32_t ready_err = api.GetRecvReadyCount(device.hnd, &ready_words); if ((ready_err == X502_ERR_OK) && (ready_words != 0U)) { @@ -2221,10 +2094,6 @@ int run(const Config& cfg) { } trigger_prev_level = di_syn2_level; } - if (cfg.do1_toggle_per_frame) { - do1_on_tick(di_syn2_level); - } - if (!packet_active && (cfg.sync_start_mode == X502_SYNC_INTERNAL)) { start_packet(); } @@ -2318,7 +2187,6 @@ int run(const Config& cfg) { } } - flush_dout_queue(0U, false); flush_tty_frames(); if (console_stop_requested()) { @@ -2335,12 +2203,14 @@ int run(const Config& cfg) { } if (cfg.do1_toggle_per_frame) { - do1_queue_level(false, false); - flush_dout_queue(cfg.recv_timeout_ms, true); + expect_ok(api, api.OutCycleStop(device.hnd, X502_OUT_CYCLE_FLAGS_WAIT_DONE), "Stop cyclic DOUT pattern"); } expect_ok(api, api.StreamsStop(device.hnd), "Stop streams"); device.streams_started = false; + if (cfg.do1_toggle_per_frame) { + expect_ok(api, api.AsyncOutDig(device.hnd, 0U, ~kE502Do1Mask), "Force DO1 LOW after cyclic DOUT stop"); + } if ((cfg.stats_period_ms != 0U) && ((stats_raw_words != 0U) || (stats_adc_samples != 0U) || (stats_din_samples != 0U) || (stats_stored_adc_samples != 0U) || (stats_zeroed_samples != 0U) ||