Add single-channel capture mode
This commit is contained in:
@ -11,13 +11,23 @@
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t kLivePlotMaxColumns = 800;
|
||||
|
||||
struct PlotPoint {
|
||||
std::size_t sample_index = 0;
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
std::size_t packet_channel_count(const CapturePacket& packet) {
|
||||
return (packet.channel_count <= 1U) ? 1U : 2U;
|
||||
}
|
||||
|
||||
bool packet_has_ch2(const CapturePacket& packet) {
|
||||
return packet_channel_count(packet) >= 2U;
|
||||
}
|
||||
|
||||
std::size_t packet_frame_count(const CapturePacket& packet) {
|
||||
return std::min(packet.ch1.size(), packet.ch2.size());
|
||||
return packet_has_ch2(packet) ? std::min(packet.ch1.size(), packet.ch2.size()) : packet.ch1.size();
|
||||
}
|
||||
|
||||
std::vector<PlotPoint> build_min_max_trace(const std::vector<double>& data, std::size_t max_columns) {
|
||||
@ -156,8 +166,8 @@ void write_live_html_document(const std::string& path, const std::string& data_j
|
||||
<< " <div class=\"status\" id=\"statusText\">Open this page once and leave it open. It reloads itself to pick up new packets.</div>\n"
|
||||
<< " <canvas id=\"plot\" width=\"1200\" height=\"620\"></canvas>\n"
|
||||
<< " <div class=\"legend\">\n"
|
||||
<< " <span><span class=\"sw\" style=\"background:#005bbb\"></span>CH1</span>\n"
|
||||
<< " <span><span class=\"sw\" style=\"background:#d62828\"></span>CH2</span>\n"
|
||||
<< " <span id=\"legendCh1\"><span class=\"sw\" style=\"background:#005bbb\"></span>CH1</span>\n"
|
||||
<< " <span id=\"legendCh2\"><span class=\"sw\" style=\"background:#d62828\"></span>CH2</span>\n"
|
||||
<< " </div>\n"
|
||||
<< " </div>\n"
|
||||
<< " </div>\n"
|
||||
@ -171,6 +181,7 @@ void write_live_html_document(const std::string& path, const std::string& data_j
|
||||
<< " const zeroInfo = document.getElementById('zeroInfo');\n"
|
||||
<< " const packetRateInfo = document.getElementById('packetRateInfo');\n"
|
||||
<< " const updateInfo = document.getElementById('updateInfo');\n"
|
||||
<< " const legendCh2 = document.getElementById('legendCh2');\n"
|
||||
<< " function drawAxes(minY, maxY, maxT) {\n"
|
||||
<< " const left = 78, top = 24, width = canvas.width - 112, height = canvas.height - 92;\n"
|
||||
<< " ctx.clearRect(0, 0, canvas.width, canvas.height);\n"
|
||||
@ -212,6 +223,7 @@ void write_live_html_document(const std::string& path, const std::string& data_j
|
||||
<< " timingInfo.textContent = '';\n"
|
||||
<< " zeroInfo.textContent = '';\n"
|
||||
<< " packetRateInfo.textContent = '';\n"
|
||||
<< " legendCh2.style.display = '';\n"
|
||||
<< " updateInfo.textContent = 'Auto-refresh every 500 ms';\n"
|
||||
<< " statusText.textContent = message;\n"
|
||||
<< " ctx.clearRect(0, 0, canvas.width, canvas.height);\n"
|
||||
@ -221,9 +233,10 @@ void write_live_html_document(const std::string& path, const std::string& data_j
|
||||
<< " function renderPacket(data) {\n"
|
||||
<< " const ch1 = data.ch1 || [];\n"
|
||||
<< " const ch2 = data.ch2 || [];\n"
|
||||
<< " const hasCh2 = (data.channelCount || 2) > 1 && ch2.length > 0;\n"
|
||||
<< " const values = [];\n"
|
||||
<< " ch1.forEach(p => values.push(p[1]));\n"
|
||||
<< " ch2.forEach(p => values.push(p[1]));\n"
|
||||
<< " if (hasCh2) ch2.forEach(p => values.push(p[1]));\n"
|
||||
<< " let minY = Math.min(...values);\n"
|
||||
<< " let maxY = Math.max(...values);\n"
|
||||
<< " if (!Number.isFinite(minY) || !Number.isFinite(maxY) || minY === maxY) {\n"
|
||||
@ -235,8 +248,9 @@ void write_live_html_document(const std::string& path, const std::string& data_j
|
||||
<< " const maxT = Math.max(data.durationMs / 1000.0, 1e-9);\n"
|
||||
<< " const box = drawAxes(minY, maxY, maxT);\n"
|
||||
<< " drawTrace(ch1, '#005bbb', box, minY, maxY, maxT);\n"
|
||||
<< " drawTrace(ch2, '#d62828', box, minY, maxY, maxT);\n"
|
||||
<< " packetInfo.textContent = 'Packet #' + data.packetIndex + ' of ' + data.packetsSeen;\n"
|
||||
<< " if (hasCh2) drawTrace(ch2, '#d62828', box, minY, maxY, maxT);\n"
|
||||
<< " legendCh2.style.display = hasCh2 ? '' : 'none';\n"
|
||||
<< " packetInfo.textContent = 'Packet #' + data.packetIndex + ' of ' + data.packetsSeen + ', channels: ' + (data.channelCount || 2);\n"
|
||||
<< " timingInfo.textContent = 'Frames/ch: ' + data.framesPerChannel + ', duration: ' + data.durationMs.toFixed(3) + ' ms';\n"
|
||||
<< " packetRateInfo.textContent = 'Packets/s: ' + data.packetsPerSecond.toFixed(3);\n"
|
||||
<< " zeroInfo.textContent = 'Zeroed on DI1 change: ' + data.zeroedPercent.toFixed(3) + '% (' + data.zeroedSamples + '/' + data.storedSamples + ')';\n"
|
||||
@ -306,14 +320,15 @@ void CaptureFileWriter::update_live_plot(const CapturePacket& packet,
|
||||
const double zeroed_percent = (stored_samples == 0U)
|
||||
? 0.0
|
||||
: (100.0 * static_cast<double>(zeroed_samples) / static_cast<double>(stored_samples));
|
||||
const auto trace1 = build_min_max_trace(packet.ch1, 1800);
|
||||
const auto trace2 = build_min_max_trace(packet.ch2, 1800);
|
||||
const auto trace1 = build_min_max_trace(packet.ch1, kLivePlotMaxColumns);
|
||||
const auto trace2 = build_min_max_trace(packet.ch2, kLivePlotMaxColumns);
|
||||
|
||||
std::ostringstream json;
|
||||
json << std::fixed << std::setprecision(9);
|
||||
json << "{\n"
|
||||
<< " \"status\": \"packet\",\n"
|
||||
<< " \"packetIndex\": " << packet.packet_index << ",\n"
|
||||
<< " \"channelCount\": " << packet_channel_count(packet) << ",\n"
|
||||
<< " \"packetsSeen\": " << packets_seen << ",\n"
|
||||
<< " \"packetsPerSecond\": " << packets_per_second << ",\n"
|
||||
<< " \"framesPerChannel\": " << frames << ",\n"
|
||||
@ -339,7 +354,12 @@ void CaptureFileWriter::write_csv(const std::vector<CapturePacket>& packets,
|
||||
fail_write("Cannot open CSV for writing: " + csv_path_);
|
||||
}
|
||||
|
||||
file << "packet_index,frame_index,global_frame_index,time_s,packet_time_s,ch1_v,ch2_v\n";
|
||||
const std::size_t channel_count = packets.empty() ? 2U : packet_channel_count(packets.front());
|
||||
file << "packet_index,frame_index,global_frame_index,time_s,packet_time_s,ch1_v";
|
||||
if (channel_count >= 2U) {
|
||||
file << ",ch2_v";
|
||||
}
|
||||
file << "\n";
|
||||
file << std::fixed << std::setprecision(9);
|
||||
|
||||
std::size_t global_frame_index = 0;
|
||||
@ -349,7 +369,11 @@ void CaptureFileWriter::write_csv(const std::vector<CapturePacket>& packets,
|
||||
const double time_s = static_cast<double>(global_frame_index) / frame_freq_hz;
|
||||
const double packet_time_s = static_cast<double>(i) / frame_freq_hz;
|
||||
file << packet.packet_index << "," << i << "," << global_frame_index << ","
|
||||
<< time_s << "," << packet_time_s << "," << packet.ch1[i] << "," << packet.ch2[i] << "\n";
|
||||
<< time_s << "," << packet_time_s << "," << packet.ch1[i];
|
||||
if (channel_count >= 2U) {
|
||||
file << "," << packet.ch2[i];
|
||||
}
|
||||
file << "\n";
|
||||
++global_frame_index;
|
||||
}
|
||||
}
|
||||
@ -366,14 +390,17 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
|
||||
std::size_t total_frames = 0;
|
||||
double min_y = std::numeric_limits<double>::infinity();
|
||||
double max_y = -std::numeric_limits<double>::infinity();
|
||||
const std::size_t channel_count = packets.empty() ? 2U : packet_channel_count(packets.front());
|
||||
for (const auto& packet : packets) {
|
||||
const std::size_t frames = packet_frame_count(packet);
|
||||
total_frames += frames;
|
||||
for (std::size_t i = 0; i < frames; ++i) {
|
||||
min_y = std::min(min_y, packet.ch1[i]);
|
||||
max_y = std::max(max_y, packet.ch1[i]);
|
||||
min_y = std::min(min_y, packet.ch2[i]);
|
||||
max_y = std::max(max_y, packet.ch2[i]);
|
||||
if (packet_has_ch2(packet)) {
|
||||
min_y = std::min(min_y, packet.ch2[i]);
|
||||
max_y = std::max(max_y, packet.ch2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,9 +471,11 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
|
||||
file << " <polyline fill=\"none\" stroke=\"#005bbb\" stroke-width=\"1.2\" points=\""
|
||||
<< polyline_points(trace1, frame_offset, total_frames, min_y, max_y, left, top, plot_w, plot_h)
|
||||
<< "\"/>\n";
|
||||
file << " <polyline fill=\"none\" stroke=\"#d62828\" stroke-width=\"1.2\" points=\""
|
||||
<< polyline_points(trace2, frame_offset, total_frames, min_y, max_y, left, top, plot_w, plot_h)
|
||||
<< "\"/>\n";
|
||||
if (packet_has_ch2(packet)) {
|
||||
file << " <polyline fill=\"none\" stroke=\"#d62828\" stroke-width=\"1.2\" points=\""
|
||||
<< polyline_points(trace2, frame_offset, total_frames, min_y, max_y, left, top, plot_w, plot_h)
|
||||
<< "\"/>\n";
|
||||
}
|
||||
|
||||
if (packets.size() <= 24U) {
|
||||
file << " <text x=\"" << (x0 + 6.0) << "\" y=\"" << (top + 18.0)
|
||||
@ -458,7 +487,8 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
|
||||
}
|
||||
|
||||
file << " <text x=\"" << left << "\" y=\"24\" font-size=\"22\" font-family=\"Segoe UI, Arial, sans-serif\""
|
||||
<< " fill=\"#203040\">E-502 capture: " << packets.size() << " packet(s), CH1 and CH2</text>\n";
|
||||
<< " fill=\"#203040\">E-502 capture: " << packets.size() << " packet(s), "
|
||||
<< ((channel_count >= 2U) ? "CH1 and CH2" : "CH1 only") << "</text>\n";
|
||||
file << " <text x=\"" << left << "\" y=\"" << (height - 22)
|
||||
<< "\" font-size=\"16\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#425466\">time, s</text>\n";
|
||||
file << " <text x=\"18\" y=\"" << (top + 16)
|
||||
@ -485,10 +515,12 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
|
||||
<< "\" stroke=\"#005bbb\" stroke-width=\"3\"/>\n";
|
||||
file << " <text x=\"" << (width - 220) << "\" y=\"" << (legend_y + 4)
|
||||
<< "\" font-size=\"14\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#203040\">CH1</text>\n";
|
||||
file << " <line x1=\"" << (width - 160) << "\" y1=\"" << legend_y
|
||||
<< "\" x2=\"" << (width - 120) << "\" y2=\"" << legend_y
|
||||
<< "\" stroke=\"#d62828\" stroke-width=\"3\"/>\n";
|
||||
file << " <text x=\"" << (width - 110) << "\" y=\"" << (legend_y + 4)
|
||||
<< "\" font-size=\"14\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#203040\">CH2</text>\n";
|
||||
if (channel_count >= 2U) {
|
||||
file << " <line x1=\"" << (width - 160) << "\" y1=\"" << legend_y
|
||||
<< "\" x2=\"" << (width - 120) << "\" y2=\"" << legend_y
|
||||
<< "\" stroke=\"#d62828\" stroke-width=\"3\"/>\n";
|
||||
file << " <text x=\"" << (width - 110) << "\" y=\"" << (legend_y + 4)
|
||||
<< "\" font-size=\"14\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#203040\">CH2</text>\n";
|
||||
}
|
||||
file << "</svg>\n";
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
struct CapturePacket {
|
||||
std::size_t packet_index = 0;
|
||||
std::size_t channel_count = 2;
|
||||
std::vector<double> ch1;
|
||||
std::vector<double> ch2;
|
||||
};
|
||||
|
||||
165
main.cpp
165
main.cpp
@ -44,8 +44,9 @@ struct Config {
|
||||
std::string serial;
|
||||
std::optional<uint32_t> ip_addr;
|
||||
|
||||
uint32_t channel_count = 2;
|
||||
uint32_t mode = X502_LCH_MODE_DIFF;
|
||||
uint32_t range = X502_ADC_RANGE_5;
|
||||
uint32_t range = X502_ADC_RANGE_02;
|
||||
uint32_t ch1 = 2;
|
||||
uint32_t ch2 = 3;
|
||||
|
||||
@ -60,12 +61,13 @@ struct Config {
|
||||
uint32_t sync_start_mode = X502_SYNC_DI_SYN2_RISE;
|
||||
StopMode stop_mode = StopMode::DiSyn2Fall;
|
||||
|
||||
uint32_t recv_block_words = 8192;
|
||||
uint32_t recv_timeout_ms = 100;
|
||||
uint32_t recv_block_words = 4096;
|
||||
uint32_t recv_timeout_ms = 50;
|
||||
uint32_t stats_period_ms = 1000;
|
||||
uint32_t start_wait_ms = 10000;
|
||||
uint32_t input_buffer_words = 262144;
|
||||
uint32_t input_step_words = 8192;
|
||||
uint32_t input_buffer_words = 4 * 1024 * 1024;
|
||||
uint32_t input_step_words = 4096;
|
||||
uint32_t live_update_period_ms = 500;
|
||||
|
||||
bool pullup_syn1 = false;
|
||||
bool pullup_syn2 = false;
|
||||
@ -207,6 +209,14 @@ uint32_t parse_internal_ref_freq(const std::string& text) {
|
||||
fail("Unsupported internal_ref_hz value: " + text + ". Use 1500000 or 2000000");
|
||||
}
|
||||
|
||||
uint32_t parse_channel_count(const std::string& text) {
|
||||
const uint32_t value = parse_u32(text, "channels");
|
||||
if ((value == 1U) || (value == 2U)) {
|
||||
return value;
|
||||
}
|
||||
fail("channels must be 1 or 2");
|
||||
}
|
||||
|
||||
std::string ref_freq_to_string(uint32_t freq) {
|
||||
switch (freq) {
|
||||
case X502_REF_FREQ_2000KHZ:
|
||||
@ -315,19 +325,22 @@ std::string phy_channel_name(uint32_t mode, uint32_t phy_ch) {
|
||||
void print_help(const char* exe_name) {
|
||||
std::cout
|
||||
<< "Usage:\n"
|
||||
<< " " << exe_name << " [serial:SN] [ip:192.168.0.10] [ch1:2] [ch2:3]\n"
|
||||
<< " [mode:diff|comm] [range:5] [clock:di_syn1_rise]\n"
|
||||
<< " " << exe_name << " [serial:SN] [ip:192.168.0.10] [channels:2] [ch1:2] [ch2:3]\n"
|
||||
<< " [mode:diff|comm] [range:0.2] [clock:di_syn1_rise]\n"
|
||||
<< " [start:di_syn2_rise] [stop:di_syn2_fall] [sample_clock_hz:125000|max]\n"
|
||||
<< " [internal_ref_hz:2000000]\n"
|
||||
<< " [duration_ms:100] [packet_limit:0] [csv:capture.csv] [svg:capture.svg]\n"
|
||||
<< " [live_html:live_plot.html] [live_json:live_plot.json]\n"
|
||||
<< " [recv_block:8192] [stats_period_ms:1000] [start_wait_ms:10000]\n"
|
||||
<< " [recv_block:4096] [stats_period_ms:1000] [live_update_period_ms:500] [start_wait_ms:10000]\n"
|
||||
<< " [pullup_syn1] [pullup_syn2] [pulldown_conv_in] [pulldown_start_in]\n"
|
||||
<< "\n"
|
||||
<< "Defaults for E-502:\n"
|
||||
<< " channels:2 -> capture CH1 and CH2\n"
|
||||
<< " ch1:2, ch2:3 -> X3-Y3 and X4-Y4\n"
|
||||
<< " mode:diff -> differential measurement\n"
|
||||
<< " range:5 -> +/-5 V range\n"
|
||||
<< " range:0.2 -> +/-0.2 V range\n"
|
||||
<< " supported ranges -> 10, 5, 2, 1, 0.5, 0.2 V\n"
|
||||
<< " channels:1 -> capture only CH1, CH2 is ignored\n"
|
||||
<< " clock:di_syn1_rise-> external sample clock on DI_SYN1 rising edge\n"
|
||||
<< " clock:internal -> module generates its own clock\n"
|
||||
<< " start:di_syn2_rise-> packet starts on DI_SYN2 rising edge\n"
|
||||
@ -336,6 +349,7 @@ void print_help(const char* exe_name) {
|
||||
<< " sample_clock_hz:max -> with clock:internal, use the maximum ADC speed\n"
|
||||
<< " internal_ref_hz:2000000 -> internal base clock for clock:internal (1500000 or 2000000)\n"
|
||||
<< " stats_period_ms:1000 -> print online transfer/capture statistics every 1000 ms (0 disables)\n"
|
||||
<< " live_update_period_ms:500 -> refresh live HTML/JSON no more than twice per second\n"
|
||||
<< " duration_ms:100 -> max packet length if stop edge does not arrive\n"
|
||||
<< " packet_limit:0 -> 0 means continuous until Ctrl+C, N means stop after N packets\n"
|
||||
<< " live_html/live_json -> live graph files updated as packets arrive\n"
|
||||
@ -406,6 +420,10 @@ Config parse_args(int argc, char** argv) {
|
||||
cfg.mode = parse_mode(arg.substr(5));
|
||||
continue;
|
||||
}
|
||||
if (starts_with(arg, "channels:")) {
|
||||
cfg.channel_count = parse_channel_count(arg.substr(9));
|
||||
continue;
|
||||
}
|
||||
if (starts_with(arg, "range:")) {
|
||||
cfg.range = parse_range(arg.substr(6));
|
||||
continue;
|
||||
@ -465,6 +483,10 @@ Config parse_args(int argc, char** argv) {
|
||||
cfg.stats_period_ms = parse_u32(arg.substr(16), "stats_period_ms");
|
||||
continue;
|
||||
}
|
||||
if (starts_with(arg, "live_update_period_ms:")) {
|
||||
cfg.live_update_period_ms = parse_u32(arg.substr(22), "live_update_period_ms");
|
||||
continue;
|
||||
}
|
||||
if (starts_with(arg, "start_wait_ms:")) {
|
||||
cfg.start_wait_ms = parse_u32(arg.substr(14), "start_wait_ms");
|
||||
continue;
|
||||
@ -531,12 +553,18 @@ Config parse_args(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (cfg.mode == X502_LCH_MODE_DIFF) {
|
||||
if ((cfg.ch1 >= X502_ADC_DIFF_CH_CNT) || (cfg.ch2 >= X502_ADC_DIFF_CH_CNT)) {
|
||||
fail("For differential mode E-502 channels must be in range 0..15");
|
||||
if (cfg.ch1 >= X502_ADC_DIFF_CH_CNT) {
|
||||
fail("For differential mode E-502 ch1 must be in range 0..15");
|
||||
}
|
||||
if ((cfg.channel_count >= 2U) && (cfg.ch2 >= X502_ADC_DIFF_CH_CNT)) {
|
||||
fail("For differential mode E-502 ch2 must be in range 0..15");
|
||||
}
|
||||
} else {
|
||||
if ((cfg.ch1 >= X502_ADC_COMM_CH_CNT) || (cfg.ch2 >= X502_ADC_COMM_CH_CNT)) {
|
||||
fail("For common-ground mode E-502 channels must be in range 0..31");
|
||||
if (cfg.ch1 >= X502_ADC_COMM_CH_CNT) {
|
||||
fail("For common-ground mode E-502 ch1 must be in range 0..31");
|
||||
}
|
||||
if ((cfg.channel_count >= 2U) && (cfg.ch2 >= X502_ADC_COMM_CH_CNT)) {
|
||||
fail("For common-ground mode E-502 ch2 must be in range 0..31");
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,17 +747,20 @@ struct PacketAccumulator {
|
||||
std::size_t zeroed_samples = 0;
|
||||
std::size_t stored_samples = 0;
|
||||
|
||||
void reset(std::size_t reserve_frames) {
|
||||
for (auto& channel : channels) {
|
||||
void reset(std::size_t reserve_frames, std::size_t channel_count) {
|
||||
for (std::size_t i = 0; i < channels.size(); ++i) {
|
||||
auto& channel = channels[i];
|
||||
channel.clear();
|
||||
channel.reserve(reserve_frames);
|
||||
if (i < channel_count) {
|
||||
channel.reserve(reserve_frames);
|
||||
}
|
||||
}
|
||||
zeroed_samples = 0;
|
||||
stored_samples = 0;
|
||||
}
|
||||
|
||||
std::size_t frame_count() const {
|
||||
return std::min(channels[0].size(), channels[1].size());
|
||||
std::size_t frame_count(std::size_t channel_count) const {
|
||||
return (channel_count <= 1U) ? channels[0].size() : std::min(channels[0].size(), channels[1].size());
|
||||
}
|
||||
};
|
||||
|
||||
@ -824,9 +855,11 @@ int run(const Config& cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
expect_ok(api, api.SetLChannelCount(device.hnd, 2), "Set logical channel count");
|
||||
expect_ok(api, api.SetLChannelCount(device.hnd, cfg.channel_count), "Set logical channel count");
|
||||
expect_ok(api, api.SetLChannel(device.hnd, 0, cfg.ch1, cfg.mode, cfg.range, 1), "Set logical channel 0");
|
||||
expect_ok(api, api.SetLChannel(device.hnd, 1, cfg.ch2, cfg.mode, cfg.range, 1), "Set logical channel 1");
|
||||
if (cfg.channel_count >= 2U) {
|
||||
expect_ok(api, api.SetLChannel(device.hnd, 1, cfg.ch2, cfg.mode, cfg.range, 1), "Set logical channel 1");
|
||||
}
|
||||
|
||||
const bool internal_max_clock = use_internal_max_clock(cfg);
|
||||
double actual_sample_clock_hz = cfg.sample_clock_hz;
|
||||
@ -887,13 +920,18 @@ int run(const Config& cfg) {
|
||||
}
|
||||
std::cout << "\n"
|
||||
<< " DIN clock: " << actual_din_freq_hz << " Hz\n"
|
||||
<< " ADC logical channels: " << cfg.channel_count << "\n"
|
||||
<< " per-channel frame rate: " << actual_frame_freq_hz << " Hz\n"
|
||||
<< " duration: " << cfg.duration_ms << " ms\n"
|
||||
<< " packet limit: "
|
||||
<< ((cfg.packet_limit == 0U) ? std::string("continuous until Ctrl+C")
|
||||
: std::to_string(cfg.packet_limit) + " packet(s)") << "\n"
|
||||
<< " live update period: "
|
||||
<< ((cfg.live_update_period_ms == 0U) ? std::string("every packet")
|
||||
: std::to_string(cfg.live_update_period_ms) + " ms") << "\n"
|
||||
<< " channel 1: " << phy_channel_name(cfg.mode, cfg.ch1) << "\n"
|
||||
<< " channel 2: " << phy_channel_name(cfg.mode, cfg.ch2) << "\n"
|
||||
<< " channel 2: "
|
||||
<< ((cfg.channel_count >= 2U) ? phy_channel_name(cfg.mode, cfg.ch2) : std::string("disabled")) << "\n"
|
||||
<< " ADC range: +/-" << range_to_volts(cfg.range) << " V\n"
|
||||
<< " max frames per packet per channel: " << target_frames << "\n";
|
||||
|
||||
@ -920,7 +958,7 @@ int run(const Config& cfg) {
|
||||
packets.reserve(cfg.packet_limit);
|
||||
}
|
||||
PacketAccumulator current_packet;
|
||||
current_packet.reset(target_frames);
|
||||
current_packet.reset(target_frames, cfg.channel_count);
|
||||
|
||||
bool capture_started = false;
|
||||
bool stop_loop_requested = false;
|
||||
@ -935,6 +973,7 @@ int run(const Config& cfg) {
|
||||
const ULONGLONG capture_loop_start = GetTickCount64();
|
||||
ULONGLONG stats_window_start = capture_loop_start;
|
||||
ULONGLONG last_stats_print = capture_loop_start;
|
||||
ULONGLONG last_live_update = 0;
|
||||
|
||||
uint64_t total_raw_words = 0;
|
||||
uint64_t total_adc_samples = 0;
|
||||
@ -990,18 +1029,23 @@ int run(const Config& cfg) {
|
||||
if (packet_active) {
|
||||
return;
|
||||
}
|
||||
current_packet.reset(target_frames);
|
||||
current_packet.reset(target_frames, cfg.channel_count);
|
||||
packet_active = true;
|
||||
};
|
||||
|
||||
auto finish_packet = [&](PacketCloseReason reason) {
|
||||
const std::size_t frames = current_packet.frame_count();
|
||||
const std::size_t frames = current_packet.frame_count(cfg.channel_count);
|
||||
if (frames != 0U) {
|
||||
current_packet.channels[0].resize(frames);
|
||||
current_packet.channels[1].resize(frames);
|
||||
if (cfg.channel_count >= 2U) {
|
||||
current_packet.channels[1].resize(frames);
|
||||
} else {
|
||||
current_packet.channels[1].clear();
|
||||
}
|
||||
|
||||
CapturePacket packet;
|
||||
packet.packet_index = packets.size() + 1U;
|
||||
packet.channel_count = cfg.channel_count;
|
||||
packet.ch1 = std::move(current_packet.channels[0]);
|
||||
packet.ch2 = std::move(current_packet.channels[1]);
|
||||
|
||||
@ -1024,20 +1068,35 @@ int run(const Config& cfg) {
|
||||
++stats_completed_packets;
|
||||
const double elapsed_capture_s =
|
||||
std::max(1e-9, static_cast<double>(GetTickCount64() - capture_loop_start) / 1000.0);
|
||||
const double packets_per_second = static_cast<double>(total_completed_packets) / elapsed_capture_s;
|
||||
writer.update_live_plot(packets.back(),
|
||||
packets.size(),
|
||||
packets_per_second,
|
||||
actual_frame_freq_hz,
|
||||
packet_close_reason_to_string(reason),
|
||||
current_packet.zeroed_samples,
|
||||
current_packet.stored_samples);
|
||||
const double packets_per_second =
|
||||
(elapsed_capture_s >= 0.1)
|
||||
? (static_cast<double>(total_completed_packets) / elapsed_capture_s)
|
||||
: 0.0;
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
const bool should_update_live =
|
||||
(cfg.live_update_period_ms == 0U) ||
|
||||
(last_live_update == 0U) ||
|
||||
((now - last_live_update) >= cfg.live_update_period_ms);
|
||||
if (should_update_live) {
|
||||
writer.update_live_plot(packets.back(),
|
||||
packets.size(),
|
||||
packets_per_second,
|
||||
actual_frame_freq_hz,
|
||||
packet_close_reason_to_string(reason),
|
||||
current_packet.zeroed_samples,
|
||||
current_packet.stored_samples);
|
||||
last_live_update = now;
|
||||
}
|
||||
std::cout << std::fixed << std::setprecision(3)
|
||||
<< " packets/s(avg)=" << packets_per_second << "\n";
|
||||
<< " packets/s(avg)=" << packets_per_second;
|
||||
if (elapsed_capture_s < 0.1) {
|
||||
std::cout << " (warming up)";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
packet_active = false;
|
||||
current_packet.reset(target_frames);
|
||||
current_packet.reset(target_frames, cfg.channel_count);
|
||||
};
|
||||
|
||||
while (!stop_loop_requested) {
|
||||
@ -1088,16 +1147,22 @@ int run(const Config& cfg) {
|
||||
|
||||
uint32_t adc_count = static_cast<uint32_t>(adc_buffer.size());
|
||||
uint32_t din_count = static_cast<uint32_t>(din_buffer.size());
|
||||
expect_ok(api,
|
||||
api.ProcessData(device.hnd,
|
||||
raw.data(),
|
||||
static_cast<uint32_t>(recvd),
|
||||
X502_PROC_FLAGS_VOLT,
|
||||
adc_buffer.data(),
|
||||
&adc_count,
|
||||
din_buffer.data(),
|
||||
&din_count),
|
||||
"Process ADC+DIN data");
|
||||
const int32_t process_err = api.ProcessData(device.hnd,
|
||||
raw.data(),
|
||||
static_cast<uint32_t>(recvd),
|
||||
X502_PROC_FLAGS_VOLT,
|
||||
adc_buffer.data(),
|
||||
&adc_count,
|
||||
din_buffer.data(),
|
||||
&din_count);
|
||||
if (process_err == X502_ERR_STREAM_OVERFLOW) {
|
||||
std::ostringstream message;
|
||||
message << "Process ADC+DIN data: " << x502_error(api, process_err)
|
||||
<< ". Try larger buffer_words/step_words, a longer live_update_period_ms, "
|
||||
<< "or a lower sample_clock_hz / DIN load.";
|
||||
fail(message.str());
|
||||
}
|
||||
expect_ok(api, process_err, "Process ADC+DIN data");
|
||||
|
||||
if ((adc_count == 0U) && (din_count == 0U)) {
|
||||
continue;
|
||||
@ -1182,7 +1247,7 @@ int run(const Config& cfg) {
|
||||
}
|
||||
|
||||
const uint32_t lch = next_lch;
|
||||
next_lch = (next_lch + 1U) % 2U;
|
||||
next_lch = (next_lch + 1U) % cfg.channel_count;
|
||||
|
||||
double stored_value = adc_value;
|
||||
if (zero_on_di1_change) {
|
||||
@ -1197,13 +1262,13 @@ int run(const Config& cfg) {
|
||||
++current_packet.stored_samples;
|
||||
++total_stored_adc_samples;
|
||||
++stats_stored_adc_samples;
|
||||
if (lch == 1U) {
|
||||
if (lch == (cfg.channel_count - 1U)) {
|
||||
++total_completed_frames;
|
||||
++stats_completed_frames;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_packet.frame_count() >= target_frames) {
|
||||
if (current_packet.frame_count(cfg.channel_count) >= target_frames) {
|
||||
finish_packet(PacketCloseReason::DurationLimit);
|
||||
if ((cfg.packet_limit != 0U) && (packets.size() >= cfg.packet_limit)) {
|
||||
stop_loop_requested = true;
|
||||
@ -1236,7 +1301,9 @@ int run(const Config& cfg) {
|
||||
|
||||
std::size_t total_packet_frames = 0;
|
||||
for (const auto& packet : packets) {
|
||||
total_packet_frames += std::min(packet.ch1.size(), packet.ch2.size());
|
||||
total_packet_frames += (packet.channel_count <= 1U)
|
||||
? packet.ch1.size()
|
||||
: std::min(packet.ch1.size(), packet.ch2.size());
|
||||
}
|
||||
|
||||
std::cout << "Captured " << packets.size() << " packet(s), "
|
||||
|
||||
Reference in New Issue
Block a user