initial commit
This commit is contained in:
11
include/adc_sweep/device_discovery.hpp
Normal file
11
include/adc_sweep/device_discovery.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace adc_sweep {
|
||||
|
||||
std::optional<std::string> find_tty_by_vid_pid(uint16_t vid, uint16_t pid);
|
||||
|
||||
} // namespace adc_sweep
|
||||
24
include/adc_sweep/serial_port.hpp
Normal file
24
include/adc_sweep/serial_port.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace adc_sweep {
|
||||
|
||||
class SerialPort {
|
||||
public:
|
||||
SerialPort() = default;
|
||||
~SerialPort();
|
||||
|
||||
bool open_raw(const std::string& path, int baud, std::string& err);
|
||||
void close();
|
||||
bool is_open() const { return fd_ >= 0; }
|
||||
|
||||
ssize_t read_some(uint8_t* buf, size_t cap, std::string& err);
|
||||
|
||||
private:
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
||||
} // namespace adc_sweep
|
||||
82
include/adc_sweep/shm_stack.hpp
Normal file
82
include/adc_sweep/shm_stack.hpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "adc_sweep/sweep_core.hpp"
|
||||
|
||||
namespace adc_sweep {
|
||||
|
||||
constexpr uint32_t kShmMagic = 0x53575041; // "AWPS"
|
||||
constexpr uint32_t kShmVersion = 1;
|
||||
|
||||
struct alignas(64) ShmHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t capacity;
|
||||
uint32_t sweep_width;
|
||||
uint32_t slot_stride;
|
||||
uint32_t reserved0;
|
||||
uint64_t producer_seq;
|
||||
std::atomic<uint64_t> write_seq;
|
||||
std::atomic<uint32_t> latest_slot;
|
||||
uint32_t reserved1;
|
||||
uint8_t reserved2[64 - 48];
|
||||
};
|
||||
static_assert(sizeof(ShmHeader) == 64, "ShmHeader must stay fixed size");
|
||||
|
||||
struct SweepSlotHeader {
|
||||
std::atomic<uint64_t> seq;
|
||||
uint64_t ts_mono_ns;
|
||||
uint32_t sweep_idx;
|
||||
int32_t ch_primary;
|
||||
uint32_t ch_mask;
|
||||
uint32_t reserved;
|
||||
float n_valid;
|
||||
float min;
|
||||
float max;
|
||||
float mean;
|
||||
float std;
|
||||
float dt_ms;
|
||||
};
|
||||
|
||||
class ShmSweepStackWriter {
|
||||
public:
|
||||
ShmSweepStackWriter(std::string shm_name, uint32_t capacity, uint32_t sweep_width);
|
||||
~ShmSweepStackWriter();
|
||||
|
||||
bool open_or_create(std::string& err);
|
||||
void close();
|
||||
bool publish(const SweepResult& result, std::string& err);
|
||||
|
||||
uint32_t capacity() const { return capacity_; }
|
||||
uint32_t sweep_width() const { return sweep_width_; }
|
||||
|
||||
private:
|
||||
SweepSlotHeader* slot_header(uint32_t slot_idx);
|
||||
float* slot_sweep_data(uint32_t slot_idx);
|
||||
|
||||
std::string shm_name_;
|
||||
uint32_t capacity_;
|
||||
uint32_t sweep_width_;
|
||||
uint32_t slot_stride_;
|
||||
int shm_fd_ = -1;
|
||||
void* mapped_ = nullptr;
|
||||
size_t mapped_size_ = 0;
|
||||
ShmHeader* header_ = nullptr;
|
||||
uint64_t next_seq_ = 1;
|
||||
};
|
||||
|
||||
struct LatestSnapshot {
|
||||
uint64_t seq = 0;
|
||||
SweepMeta meta;
|
||||
std::vector<float> sweep;
|
||||
};
|
||||
|
||||
std::optional<LatestSnapshot> read_latest_snapshot(void* mapped, uint32_t capacity, uint32_t sweep_width, uint32_t slot_stride);
|
||||
|
||||
} // namespace adc_sweep
|
||||
71
include/adc_sweep/sweep_core.hpp
Normal file
71
include/adc_sweep/sweep_core.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace adc_sweep {
|
||||
|
||||
struct SweepMeta {
|
||||
uint32_t sweep_idx = 0;
|
||||
int32_t ch_primary = 0;
|
||||
uint32_t ch_mask = 0;
|
||||
float n_valid = 0.0f;
|
||||
float min = 0.0f;
|
||||
float max = 0.0f;
|
||||
float mean = 0.0f;
|
||||
float std = 0.0f;
|
||||
float dt_ms = 0.0f;
|
||||
uint64_t ts_mono_ns = 0;
|
||||
};
|
||||
|
||||
struct SweepResult {
|
||||
std::vector<float> sweep;
|
||||
SweepMeta meta;
|
||||
};
|
||||
|
||||
int32_t u32_to_i32(uint32_t v);
|
||||
|
||||
class SweepFinalizer {
|
||||
public:
|
||||
SweepFinalizer(uint32_t sweep_width, float invert_threshold, bool fancy_fill);
|
||||
|
||||
std::optional<SweepResult> finalize(
|
||||
const std::vector<int>& xs,
|
||||
const std::vector<int32_t>& ys,
|
||||
uint32_t ch_mask,
|
||||
int32_t ch_primary);
|
||||
|
||||
private:
|
||||
uint32_t sweep_width_;
|
||||
float invert_threshold_;
|
||||
bool fancy_fill_;
|
||||
uint32_t max_width_seen_ = 0;
|
||||
uint32_t sweep_idx_ = 0;
|
||||
std::optional<std::chrono::steady_clock::time_point> last_sweep_ts_;
|
||||
std::deque<std::pair<std::chrono::steady_clock::time_point, int>> n_valid_hist_;
|
||||
};
|
||||
|
||||
class BinarySweepParser {
|
||||
public:
|
||||
using SweepCallback = std::function<void(const std::vector<int>&, const std::vector<int32_t>&, uint32_t, int32_t)>;
|
||||
|
||||
void feed(const uint8_t* data, size_t size, const SweepCallback& on_sweep);
|
||||
void flush(const SweepCallback& on_sweep);
|
||||
|
||||
private:
|
||||
void emit_current(const SweepCallback& on_sweep);
|
||||
|
||||
std::vector<int> xs_;
|
||||
std::vector<int32_t> ys_;
|
||||
bool has_cur_channel_ = false;
|
||||
int32_t cur_channel_ = 0;
|
||||
uint32_t ch_mask_ = 0;
|
||||
std::deque<uint16_t> words_;
|
||||
std::optional<uint8_t> odd_byte_;
|
||||
};
|
||||
|
||||
} // namespace adc_sweep
|
||||
Reference in New Issue
Block a user