fwt_software/include/fgc/ExposureStore.h

79 lines
3.2 KiB
C++

#pragma once
#include "fgc/ExposurePolicy.h"
#include <map>
#include <optional>
#include <string>
namespace fgc {
// Per-angle exposure memory: what settings worked last time the gimbal looked
// this way. This is what lets a triggered capture start from a setting known to
// be roughly right, instead of paying the camera's own auto-exposure several
// frames to re-converge at every waypoint.
//
// Keyed by QUANTISED yaw/pitch rather than waypoint index, so the memory survives
// an edit to the scan grid and also serves ControlCode 1 directed moves, which
// have no waypoint index at all.
struct StoreEntry {
double yaw_deg = 0.0;
double pitch_deg = 0.0;
double exposure_us = 0.0;
double gain_db = 0.0;
double sharpness = 0.0; // sharpness of the last accepted frame here
double mean_luma = 0.0;
long long timestamp_ms = 0; // when this entry was last updated (epoch ms)
int attempts = 0; // attempts the last visit needed
};
class ExposureStore {
public:
// quantum_deg: angle bucket size. stale_s: how old an entry may be and still
// be trusted as this angle's seed.
explicit ExposureStore(std::string path, double quantum_deg = 1.0, long long stale_s = 1800);
// Load from / save to the CSV. Both are tolerant: a missing file is simply an
// empty store, and a malformed row is skipped rather than failing the load, so
// a corrupt line can never stop a scan from running.
bool load();
bool save() const;
// Bucket key for an angle, e.g. "y137_p-5".
static std::string keyFor(double yaw_deg, double pitch_deg, double quantum_deg);
std::optional<StoreEntry> find(double yaw_deg, double pitch_deg) const;
std::size_t size() const { return entries_.size(); }
const std::string& path() const { return path_; }
// Three-tier seed, in order of how well each predicts the CURRENT light:
// 1. this angle's own entry, if fresher than stale_s;
// 2. else the last settings accepted anywhere - light changes affect all
// angles together, so a 30 s old reading one heading over beats an
// hour-old reading at this exact heading;
// 3. else the configured defaults.
CaptureSettings seed(double yaw_deg, double pitch_deg, long long now_ms,
const CaptureSettings& defaults) const;
// This angle's reference sharpness for the relative blur check, or 0 when
// unknown or too old to compare against.
double referenceSharpness(double yaw_deg, double pitch_deg, long long now_ms) const;
// Record an accepted capture.
void update(double yaw_deg, double pitch_deg, const CaptureSettings& settings,
const ImageMetrics& metrics, long long now_ms, int attempts);
private:
bool fresh(const StoreEntry& e, long long now_ms) const;
std::string path_;
double quantum_deg_;
long long stale_s_;
std::map<std::string, StoreEntry> entries_;
std::optional<CaptureSettings> last_accepted_;
long long last_accepted_ms_ = 0;
};
} // namespace fgc