Rework radar system

This commit is contained in:
Ayzen
2026-10-06 12:32:26 +03:00
parent 552cdc688a
commit 5bb86b7165
299 changed files with 13537 additions and 27763 deletions
@@ -0,0 +1,50 @@
#pragma once
#include <cstdint>
#include <string>
#include "run_config.hpp"
namespace radar::drivers {
/**
* @brief Runtime settings for the H7992 GPIO switch driver.
*/
struct H7992MinimalDriverSettings {
std::string name{};
config::DriverMode mode = config::DriverMode::Native;
std::uint32_t positions = 4;
std::uint32_t default_position = 0;
std::string gpio_chip = "/dev/gpiochip0";
std::int32_t pin_a = 17;
std::int32_t pin_b = 27;
};
/**
* @brief Minimal H7992 implementation for fast position switching.
*
* Native mode uses Linux GPIO character-device API (uAPI v2).
* Mock mode keeps state in memory without touching hardware.
*/
class H7992MinimalDriver {
public:
explicit H7992MinimalDriver(H7992MinimalDriverSettings settings);
~H7992MinimalDriver(); // Leaves RF path safe even if close() was skipped.
void open();
void close();
void switch_to(std::uint32_t position);
private:
void open_native();
void close_native();
void switch_native(std::uint32_t position);
H7992MinimalDriverSettings settings_{};
bool is_open_ = false;
std::uint32_t current_position_ = 0;
int chip_fd_ = -1;
int line_fd_ = -1;
};
} // namespace radar::drivers
@@ -0,0 +1,185 @@
#include "h7992_minimal_driver.hpp"
#include <array>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <linux/gpio.h>
#include <stdexcept>
#include <sys/ioctl.h>
#include <unistd.h>
#include <utility>
namespace radar::drivers {
namespace {
// A transient ioctl failure (e.g. EINTR/EAGAIN/EBUSY under load) should not be
// immediately fatal; retry a small number of times before giving up.
constexpr int kSwitchIoctlRetries = 3;
// Position mapping for H7992 control pins:
// position -> (A, B)
constexpr std::array<std::array<std::uint8_t, 2>, 4> kPositionToAB = {
std::array<std::uint8_t, 2>{0, 0},
std::array<std::uint8_t, 2>{0, 1},
std::array<std::uint8_t, 2>{1, 0},
std::array<std::uint8_t, 2>{1, 1},
};
void validate_open_settings(const H7992MinimalDriverSettings& settings) {
if (settings.positions == 0U || settings.positions > 4U) {
throw std::runtime_error("H7992 switch positions must be in range [1, 4] for " + settings.name);
}
if (settings.default_position >= settings.positions) {
throw std::runtime_error("Switch default_position out of range for " + settings.name);
}
}
[[nodiscard]] auto make_line_values(std::uint8_t pin_a_state, std::uint8_t pin_b_state) -> gpio_v2_line_values {
gpio_v2_line_values values{};
values.mask = (1ULL << 0U) | (1ULL << 1U);
values.bits =
(static_cast<std::uint64_t>(pin_a_state) << 0U) | (static_cast<std::uint64_t>(pin_b_state) << 1U);
return values;
}
} // namespace
H7992MinimalDriver::H7992MinimalDriver(H7992MinimalDriverSettings settings) : settings_(std::move(settings)) {}
H7992MinimalDriver::~H7992MinimalDriver() {
// Safety net: if the owner never called close() (e.g. on crash/unwind),
// still drive the RF path to a known-safe state and release the GPIO lines.
try {
close();
} catch (...) {
}
}
void H7992MinimalDriver::open() {
if (is_open_) {
return;
}
validate_open_settings(settings_);
if (settings_.mode == config::DriverMode::Native) {
open_native();
}
is_open_ = true;
switch_to(settings_.default_position);
}
void H7992MinimalDriver::close() {
if (!is_open_) {
return;
}
if (settings_.mode == config::DriverMode::Native) {
close_native();
}
is_open_ = false;
}
void H7992MinimalDriver::switch_to(std::uint32_t position) {
if (!is_open_) {
throw std::runtime_error("Switch driver is not open for " + settings_.name);
}
if (position >= settings_.positions) {
throw std::runtime_error("Switch position out of range for " + settings_.name);
}
switch (settings_.mode) {
case config::DriverMode::Mock:
break;
case config::DriverMode::Native:
switch_native(position);
break;
default:
throw std::runtime_error("Unsupported switch driver mode for " + settings_.name);
}
current_position_ = position;
}
void H7992MinimalDriver::open_native() {
if (settings_.gpio_chip.empty()) {
throw std::runtime_error("gpio_chip is empty for native switch " + settings_.name);
}
if (settings_.pin_a < 0 || settings_.pin_b < 0 || settings_.pin_a == settings_.pin_b) {
throw std::runtime_error("Invalid pin_a/pin_b for native switch " + settings_.name);
}
chip_fd_ = ::open(settings_.gpio_chip.c_str(), O_RDONLY | O_CLOEXEC);
if (chip_fd_ < 0) {
throw std::runtime_error(
"Failed to open gpio chip '" + settings_.gpio_chip + "' for " + settings_.name + ": " + std::strerror(errno)
);
}
gpio_v2_line_request request{};
// The first requested line maps to logical bit 0, the second to bit 1.
request.offsets[0] = static_cast<std::uint32_t>(settings_.pin_a);
request.offsets[1] = static_cast<std::uint32_t>(settings_.pin_b);
request.num_lines = 2;
request.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
std::snprintf(request.consumer, sizeof(request.consumer), "radar_%s", settings_.name.c_str());
if (::ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) != 0) {
const auto error = std::strerror(errno);
::close(chip_fd_);
chip_fd_ = -1;
throw std::runtime_error("Failed to request GPIO lines for " + settings_.name + ": " + error);
}
line_fd_ = request.fd;
if (line_fd_ < 0) {
::close(chip_fd_);
chip_fd_ = -1;
throw std::runtime_error("GPIO line request returned invalid fd for " + settings_.name);
}
}
void H7992MinimalDriver::close_native() {
// Leave the RF path in its known-safe default position before releasing the
// lines, so shutdown/crash never strands the switch in an arbitrary state.
if (line_fd_ >= 0 && settings_.default_position < settings_.positions) {
const auto [pin_a_state, pin_b_state] = kPositionToAB[settings_.default_position];
auto values = make_line_values(pin_a_state, pin_b_state);
// Best-effort: do not throw out of the teardown path.
::ioctl(line_fd_, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
}
if (line_fd_ >= 0) {
::close(line_fd_);
line_fd_ = -1;
}
if (chip_fd_ >= 0) {
::close(chip_fd_);
chip_fd_ = -1;
}
}
void H7992MinimalDriver::switch_native(std::uint32_t position) {
if (line_fd_ < 0) {
throw std::runtime_error("Native GPIO line fd is not open for " + settings_.name);
}
const auto [pin_a_state, pin_b_state] = kPositionToAB[position];
auto values = make_line_values(pin_a_state, pin_b_state);
// Retry transient ioctl failures so a momentary hiccup is recoverable
// instead of aborting the whole sweep; only the final attempt is fatal.
int last_errno = 0;
for (int attempt = 0; attempt < kSwitchIoctlRetries; ++attempt) {
if (::ioctl(line_fd_, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) == 0) {
return;
}
last_errno = errno;
}
throw std::runtime_error("Failed to switch GPIO state for " + settings_.name + ": " + std::strerror(last_errno));
}
} // namespace radar::drivers