fwt_software/include/fgc/SettleRule.h

77 lines
2.9 KiB
C++

#pragma once
#include <cstdint>
#include <deque>
namespace fgc {
// What the scheduler needs to know about one delivered frame to judge whether
// the camera's auto-exposure has settled: its place in the stream, its
// brightness (sampled from the pixels, 0..255, negative if unknown) and the
// exposure and gain it was taken with (chunk data, negative if unknown).
struct FrameStats {
uint64_t seq = 0;
double brightness = -1.0;
double exposure_us = -1.0;
double gain_db = -1.0;
};
// When has the image settled after the gimbal stops? Measured on 24 forced
// exposure steps (docs/raw12-pipeline-measurements.md, "Which frame to keep"):
//
// "exposure and gain unchanged for 3 frames" right 0 of 24 times
// "brightness in the camera's target band, 2 frames" right 12 of 24
// "in the band AND steady (<= 2% change) for 3 frames" right 21 of 24 <- this
//
// Auto-exposure moves in steps and pauses mid-way, so values that hold still
// prove nothing; and coming down from too bright, brightness enters the band
// ~15% above where it settles, so "in band" alone fires early. The band is the
// camera's own: IntensityControllerTarget +- IntensityControllerTolerance (in
// percent of full scale), 50 +- 5 by default.
//
// A second way to finish: brightness is out of the band but steady, and exposure
// and gain are at the limits that would move it towards the band. Waiting cannot
// help then (a scene too dark or too bright for the camera's limits).
class SettleRule {
public:
struct Params {
double target_pct = 50.0; // IntensityControllerTarget
double tolerance_pct = 5.0; // IntensityControllerTolerance
int frames = 3; // consecutive frames that must agree
double steady = 0.02; // largest frame-to-frame brightness change
// Auto-exposure limits; negative = unknown, and the limit test is off.
double exposure_min_us = -1.0;
double exposure_max_us = -1.0;
double gain_min_db = -1.0;
double gain_max_db = -1.0;
};
enum class Verdict { Waiting, Settled, AtLimit };
SettleRule() = default;
explicit SettleRule(const Params& p) : p_(p) {}
void setParams(const Params& p) { p_ = p; reset(); }
const Params& params() const { return p_; }
// Start over (the gimbal stopped at a new waypoint).
void reset() { window_.clear(); }
// Feed the next frame, in arrival order. A frame without a brightness
// breaks the run of agreeing frames.
Verdict add(const FrameStats& f);
// The band on the 0..255 scale frames are sampled on.
double bandLow() const { return (p_.target_pct - p_.tolerance_pct) * 2.55; }
double bandHigh() const { return (p_.target_pct + p_.tolerance_pct) * 2.55; }
private:
bool steady() const;
bool atLimit() const;
Params p_;
std::deque<FrameStats> window_; // the last p_.frames frames
};
} // namespace fgc