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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user