diff --git a/web/calibrate/FindSafeExposure.ts b/web/calibrate/FindSafeExposure.ts new file mode 100644 --- /dev/null +++ b/web/calibrate/FindSafeExposure.ts @@ -0,0 +1,82 @@ +import { clamp } from "lodash"; +import { sleep } from "./Light9Calibrate"; +import { Light9Camera } from "./Light9Camera"; +import { XyPlot } from "./XyPlot"; + +export class FindSafeExposure { + expoMin: number; + expoMax: number; + expo: number; + expoStep: number; + sameStateSteps: number; + + constructor(public cam: Light9Camera, public plot: XyPlot) { + const fixedSteps = 5; + + this.plot.clear(); + this.expoMin = 1; + this.expoMax = 800; + this.expo = this.expoMin; + this.expoStep = (this.expoMax - this.expoMin) / fixedSteps; + this.sameStateSteps = 0; + } + + async run() { + while (true) { + const currentOverexposed = await this.gatherSample(); + this.step(currentOverexposed); + if (this.plot.data.length > 50 || Math.abs(this.expoStep) < 1) { + this.expo = this.expoMin; + await this.gatherSample(); + break; + } + } + } + + step(currentOverexposed: number) { + const maxAllowedOverexposedPixels = 5; + const turnaroundScale = 0.6; + const stepsPerSide = 3; + + const overexposed = currentOverexposed > maxAllowedOverexposedPixels; + if (this.expoStep > 0) { + if (overexposed) { + this.sameStateSteps += 1; + } + } else { + if (!overexposed) { + this.sameStateSteps += 1; + } + } + this.plot.setXMarklines([ + { txt: "min", x: this.expoMin }, + { txt: "max", x: this.expoMax }, + { txt: "expo", x: this.expo }, + ]); + const nextExpo = clamp(this.expo + this.expoStep, this.expoMin, this.expoMax); + if (this.sameStateSteps > stepsPerSide || nextExpo <= this.expoMin || nextExpo >= this.expoMax) { + if (this.expoStep > 0) { + this.expoMax = this.expo; + } else { + this.expoMin = this.expo; + } + this.expoStep = this.expoStep * -1 * turnaroundScale; + this.sameStateSteps = 0; + } + this.expo = clamp(this.expo + this.expoStep, this.expoMin, this.expoMax); + } + + async gatherSample() { + const settleMs = 200; + + await this.cam.set("exposureTime", this.expo); + const settleUntil = Date.now() + settleMs; + let miny = this.cam.saturatedPixelCount!; + while (Date.now() < settleUntil) { + await sleep(1000 / this.cam.videoSettings.frameRate); + miny = Math.min(miny, this.cam.saturatedPixelCount!); + } + this.plot!.insertPoint(this.expo, clamp(miny, 0, 100)); + return miny; + } +}