77 lines
2.9 KiB
C++
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
|