Refactor radar system

This commit is contained in:
Ayzen
2026-10-05 16:46:28 +03:00
parent 515049d2ed
commit 8f097dfa2e
179 changed files with 8212 additions and 6676 deletions
@@ -27,9 +27,7 @@ public:
void initialize_live_plot() const;
void initialize_csv(std::size_t channel_count, bool has_di1_trace) const;
void append_csv_packet(const CapturePacket& packet,
double frame_freq_hz,
std::size_t& global_frame_index) const;
void append_csv_packet(const CapturePacket& packet) const;
void update_live_plot(const CapturePacket& packet,
std::size_t packets_seen,
@@ -234,12 +234,20 @@ std::string csv_spool_path(const std::string& csv_path) {
return replace_extension(csv_path, ".capture_spool.bin");
}
void finish_file(std::ofstream& stream, const std::string& path) {
stream.close();
if (!stream) {
fail_write("Cannot finish writing file: " + path);
}
}
void write_text_file(const std::string& path, const std::string& text) {
std::ofstream file(path, std::ios::binary);
if (!file) {
fail_write("Cannot open file for writing: " + path);
}
file << text;
finish_file(file, path);
}
void write_live_data_script(const std::string& path, const std::string& data_json) {
@@ -584,6 +592,7 @@ void write_live_html_document(const std::string& path, const std::string& data_s
<< " </script>\n"
<< "</body>\n"
<< "</html>\n";
finish_file(html, path);
}
[[noreturn]] void fail_write(const std::string& message) {
@@ -629,11 +638,14 @@ void CaptureFileWriter::initialize_live_plot() const {
}
void CaptureFileWriter::initialize_csv(std::size_t channel_count, bool has_di1_trace) const {
if (channel_count != 1U && channel_count != 2U) {
fail_write("Capture channel count must be 1 or 2");
}
std::ofstream csv_file(csv_path_, std::ios::binary | std::ios::trunc);
if (!csv_file) {
fail_write("Cannot prepare CSV output file: " + csv_path_);
}
csv_file << "";
finish_file(csv_file, csv_path_);
const std::string spool_path = csv_spool_path(csv_path_);
std::ofstream spool(spool_path, std::ios::binary | std::ios::trunc);
@@ -647,16 +659,15 @@ void CaptureFileWriter::initialize_csv(std::size_t channel_count, bool has_di1_t
static_cast<uint32_t>(has_di1_trace ? 1U : 0U)
};
spool.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!spool) {
fail_write("Cannot initialize CSV spool: " + spool_path);
}
finish_file(spool, spool_path);
}
void CaptureFileWriter::append_csv_packet(const CapturePacket& packet,
double frame_freq_hz,
std::size_t& global_frame_index) const {
(void) frame_freq_hz;
(void) global_frame_index;
void CaptureFileWriter::append_csv_packet(const CapturePacket& packet) const {
if ((packet.channel_count != 1U && packet.channel_count != 2U)
|| (packet.channel_count == 2U && packet.ch1.size() != packet.ch2.size())
|| (packet.has_di1_trace && packet.di1.size() != packet.ch1.size())) {
fail_write("Capture packet channel lengths are inconsistent");
}
const std::string spool_path = csv_spool_path(csv_path_);
std::ofstream spool(spool_path, std::ios::binary | std::ios::app);
@@ -689,9 +700,7 @@ void CaptureFileWriter::append_csv_packet(const CapturePacket& packet,
static_cast<std::streamsize>(frames * sizeof(uint8_t)));
}
}
if (!spool) {
fail_write("Cannot append packet to CSV spool: " + spool_path);
}
finish_file(spool, spool_path);
}
void CaptureFileWriter::update_live_plot(const CapturePacket& packet,
@@ -763,7 +772,7 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
CsvSpoolPacketHeader packet_header{};
spool.read(reinterpret_cast<char*>(&packet_header), sizeof(packet_header));
if (!spool) {
if (spool.eof()) {
if (spool.eof() && spool.gcount() == 0) {
break;
}
fail_write("Cannot read packet header from CSV spool: " + spool_path);
@@ -773,6 +782,17 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
const std::size_t channel_count = static_cast<std::size_t>(packet_header.channel_count);
const std::size_t frames = static_cast<std::size_t>(packet_header.frame_count);
const bool has_di1_trace = packet_header.has_di1_trace != 0U;
if (channel_count != 1U && channel_count != 2U) {
fail_write("Invalid channel count in CSV spool: " + spool_path);
}
const auto data_position = spool.tellg();
spool.seekg(0, std::ios::end);
const auto remaining = spool.tellg() - data_position;
spool.seekg(data_position);
const auto bytes_per_frame = channel_count * sizeof(double) + (has_di1_trace ? 1U : 0U);
if (!spool || remaining < 0 || packet_header.frame_count > static_cast<uint64_t>(remaining) / bytes_per_frame) {
fail_write("Truncated or invalid packet data in CSV spool: " + spool_path);
}
if (!header_written) {
csv << "packet_index,frame_index,global_frame_index,time_s,packet_time_s,ch1_v";
@@ -839,6 +859,13 @@ void CaptureFileWriter::finalize_csv_from_spool(double frame_freq_hz) const {
}
csv.flush();
if (!csv) {
fail_write("Cannot flush CSV output; capture spool retained: " + csv_path_);
}
csv.close();
if (!csv) {
fail_write("Cannot close CSV output; capture spool retained: " + csv_path_);
}
std::remove(spool_path.c_str());
}
@@ -1016,4 +1043,5 @@ void CaptureFileWriter::write_svg(const std::vector<CapturePacket>& packets,
<< "\" font-size=\"14\" font-family=\"Segoe UI, Arial, sans-serif\" fill=\"#203040\">DI1</text>\n";
}
file << "</svg>\n";
finish_file(file, svg_path_);
}
@@ -156,7 +156,8 @@ uint32_t parse_u32(const std::string& text, const std::string& field_name) {
const std::string clean = trim_copy(text);
char* end = nullptr;
const auto value = std::strtoull(clean.c_str(), &end, 0);
if ((end == clean.c_str()) || (*end != '\0') || (value > std::numeric_limits<uint32_t>::max())) {
if ((!clean.empty() && clean.front() == '-') || (end == clean.c_str())
|| (*end != '\0') || (value > std::numeric_limits<uint32_t>::max())) {
fail("Invalid integer for " + field_name + ": " + text);
}
return static_cast<uint32_t>(value);
@@ -191,15 +192,6 @@ uint32_t parse_ipv4(const std::string& text) {
return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
}
std::string ipv4_to_string(uint32_t ip_addr) {
std::ostringstream out;
out << ((ip_addr >> 24) & 0xFF) << '.'
<< ((ip_addr >> 16) & 0xFF) << '.'
<< ((ip_addr >> 8) & 0xFF) << '.'
<< (ip_addr & 0xFF);
return out.str();
}
uint32_t parse_range(const std::string& text) {
const double value = parse_double(text, "range");
if (std::fabs(value - 10.0) < 1e-9) {
@@ -2033,8 +2025,14 @@ int run(const Config& cfg) {
}
expect_ok(api, api.StreamsEnable(device.hnd, enabled_streams), "Enable data streams");
const double target_frame_count = (cfg.duration_ms / 1000.0) * actual_frame_freq_hz;
if (!std::isfinite(target_frame_count) || target_frame_count < 0.0
|| target_frame_count >= static_cast<double>(std::numeric_limits<long long>::max())
|| target_frame_count > static_cast<double>(std::numeric_limits<std::size_t>::max())) {
fail("Capture duration and frame clock produce an unsupported frame count");
}
const std::size_t target_frames = std::max<std::size_t>(
1, static_cast<std::size_t>(std::llround((cfg.duration_ms / 1000.0) * actual_frame_freq_hz)));
1, static_cast<std::size_t>(std::llround(target_frame_count)));
std::cout << "Capture settings:\n"
<< " clock source: " << sync_mode_to_string(cfg.sync_mode, false) << "\n"
@@ -2306,7 +2304,6 @@ int run(const Config& cfg) {
if (!fast_tty_avg_stream_mode) {
current_packet.reset(target_frames, cfg.channel_count);
}
std::size_t csv_global_frame_index = 0;
std::size_t packet_avg_steps = 0;
std::size_t fast_packet_frames = 0;
uint64_t packet_clock_count = 0;
@@ -2776,7 +2773,7 @@ int run(const Config& cfg) {
}
std::cout << "\n";
writer->append_csv_packet(packet, actual_frame_freq_hz, csv_global_frame_index);
writer->append_csv_packet(packet);
packets.push_back(std::move(packet));
if ((cfg.svg_history_packets != 0U) && (packets.size() > cfg.svg_history_packets)) {
packets.pop_front();
@@ -196,7 +196,7 @@ TtyProtocolWriter::TtyProtocolWriter(std::string path, std::size_t ring_capacity
: path_(std::move(path)),
impl_(std::make_unique<Impl>(ring_capacity_bytes)) {
if (is_character_device_path(path_)) {
impl_->fd = ::open(path_.c_str(), O_WRONLY | O_NOCTTY);
impl_->fd = ::open(path_.c_str(), O_WRONLY | O_NOCTTY | O_NONBLOCK);
if (impl_->fd < 0) {
throw std::runtime_error(io_error("Cannot open tty output", path_));
}
@@ -233,7 +233,20 @@ TtyProtocolWriter::TtyProtocolWriter(std::string path, std::size_t ring_capacity
}
}
impl_->worker = std::thread([this]() { worker_loop(); });
try {
const auto flags = ::fcntl(impl_->fd, F_GETFL);
if (flags < 0 || ::fcntl(impl_->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
throw std::runtime_error(io_error("Cannot make tty output nonblocking", path_));
}
impl_->worker = std::thread([this]() { worker_loop(); });
} catch (...) {
if (impl_->owns_link) {
::unlink(path_.c_str());
}
close_fd_if_open(impl_->slave_fd);
close_fd_if_open(impl_->fd);
throw;
}
}
TtyProtocolWriter::~TtyProtocolWriter() {
@@ -363,12 +376,12 @@ void TtyProtocolWriter::shutdown() {
impl_->stop_requested = true;
}
close_fd_if_open(impl_->fd);
impl_->data_ready_cv.notify_all();
if (impl_->worker.joinable()) {
impl_->worker.join();
}
close_fd_if_open(impl_->fd);
}
void TtyProtocolWriter::enqueue_frame(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3) {
@@ -402,6 +415,12 @@ void TtyProtocolWriter::worker_loop() {
const std::uint8_t* bytes = write_batch.data();
std::size_t remaining = batch_count * kFrameByteCount;
while (remaining != 0U) {
{
std::lock_guard<std::mutex> lock(impl_->mutex);
if (impl_->stop_requested) {
return;
}
}
const ssize_t written = ::write(impl_->fd, bytes, remaining);
if (written < 0) {
if (errno == EINTR) {