Auto Flat

Automatic flat-frame capture, modelled on Voyager's Auto Flat. For each filter you configure, the runner meters a test frame, scales the exposure toward a target mean ADU, and once it's close enough saves a set of flats tagged IMAGETYP=FLAT so your stacking software classifies them correctly. It has its own Auto Flat page in the left nav (a Plan summary, Run / Cancel, and a live progress log); the plan is edited in Settings → Flat device → Auto Flat. It runs as a background job you can watch and cancel mid-run.

The per-filter target-ADU workflow

This is the heart of it. A good flat sits at a known, mid-well level (the default target is 25000 ADU, roughly half a 16-bit well) where the sensor response is linear. The runner finds the exposure that lands there, per filter, instead of asking you to guess:

  1. Capture a test frame at the initial exposure (default 3 s) and measure the mean ADU over a centred ROI (calc_roi_pct, default 75% of the frame — a large region reads steadier).
  2. Compare the mean to the filter's target ADU. If it's within max error (max_err_pct, default ±10%), accept this exposure.
  3. Otherwise scale the exposure linearly toward the target. Flats are linear above the bias, so ADU − bias is proportional to exposure time; the next exposure is exp × (target − bias) / (mean − bias). An optional bias_adu is subtracted first (0 = none). An underexposed frame just doubles the exposure.
  4. The new exposure is clamped to [min_exposure_s, max_exposure_s] (defaults 1 s … 30 s), and the search is capped at max_iterations metering frames (default 6).
  5. Once an exposure is accepted, save count flats at it. If the panel can't reach target within the exposure bounds, the runner saves its best effort and flags the filter with "target ADU not reached within bounds".

The per-filter plan

The plan is a list of filters, one row each, edited as a small form table in Settings → Flat device → Auto Flat — no JSON to hand-write. Click + Add filter to add a row (new rows default to 20 shots) and the × button to remove one. Each row has:

A row can also carry a gain, offset, min/max exposure and per-filter panel brightness if you set them (these aren't columns in the table but are honoured if present). Any blank field falls back to the matching global default, so the simplest useful plan is a single row: filter L, 20 shots.

The Auto Flat page shows the same plan read-only as a Plan card — Filter, Count, Bin, Init exp, Target ADU — headed by a one-line summary of the flat source, global target ADU and tolerance.

Flat source and timing

The light source is the flat type (flat_type):

Twilight flats on the real sky have their own runner - see Sky flats below. For Run auto flat the sky_dusk / sky_dawn flat-source values simply behave like manual.

Between the individual saved shots the runner pauses settle between frames (settle_s, default 0.5 s) so the panel and camera settle. It does not pause after the last shot.

On completion the runner can optionally close the cover (cover_close_on_end) and park the mount (park_on_end); both default off. In panel mode the calibrator light is always turned off at the end regardless of these.

Where the flats are saved

Saved flats land in your normal IMAGES folder, written with IMAGETYP=FLAT and a flat_<filter> filename prefix (the header also carries the filter name and exposure). The save layout (save_layout) controls the folder:

Sky flats (twilight)

Sky flats use the twilight sky as the light source. They are what a panel cannot give a big scope - the true illumination of the whole optical train through the real sky - and they are unforgiving about timing. The Sky flats (dawn) / Sky flats (dusk) buttons on the Auto Flat page run the same per-filter plan as Auto Flat, but on the sky.

Shoot late and short. With the mount stopped, every star draws a trail, and a trail is as bright per pixel in a dark twilight as in a bright one - what a brighter sky buys is a shorter exposure, i.e. a shorter trail and far fewer contaminated pixels. A first hand-run dawn on a rig (sun -6 deg, 5-15 s exposures) showed long trails in every frame; the defaults therefore use the sun between -7 and -1.5 degrees and exposures between 0.5 and 5 s. Whatever the sky, stars show in single sky flats: it is the stack that removes them, because the drift puts each trail somewhere else in every frame. Shoot at least 15 frames per filter and combine them with a rejecting method (median, sigma clip).

  1. Start the cooler. Flats belong at the lights' sensor temperature: hot pixels sit on the same pixels in every frame, so no stack rejects them. With sky_cool_camera on (the default) the run starts the camera cooler twenty minutes before the window opens (at once when the window is nearer than that) - towards [cooling] default_setpoint_c, or the set-point the camera still holds from its last session when that key is empty - and then has the wait for twilight, for SAFE and for the roof to get there. It never waits for a temperature (twilight does not wait either): if the first frame is about to be shot more than a few degrees warm the result carries a warning, and ccd_temp_c records the temperature the set was shot at. The cooler is left on at the end.
  2. Wait for the window. Nothing happens until the sun is between sky_sun_alt_min_deg and sky_sun_alt_max_deg. A dawn run started at 3 am waits (up to 6 hours) and sets the rig up three minutes before the window opens; a run started after the window is refused as "window is over". Set the top of the window to the sun altitude where your roof closes at dawn (or opens at dusk) if something other than Starship drives it.
  3. Set up the rig. A run never opens up under an UNSAFE verdict - it waits for SAFE for as long as the window lasts, then gives up with "never SAFE inside the window". That is what makes a dusk run work on a site whose weather station holds the roof shut until the daylight has faded: start the run in the afternoon and it opens up the moment the verdict turns SAFE. The roof claim is opened (on a shared roof the claim is what you own while the camera is uncovered; on a real roof this is the motion), the flat cover is opened if the device has one, the mount is unparked and slewed to the flat spot - sky_flat_spot_ha_h hours from the meridian on the side away from the sun (west at dawn, east at dusk) at the site's latitude, about 12 degrees from the zenith - and tracking is switched off and read back. A mount that keeps tracking would put every star on the same pixels in every flat, where no stack can reject it, so that is an error, not a warning. The mount does all of this while the roof is still opening - a roof can take minutes of a twilight that lasts twenty - and the first look at the sky happens the moment the roof reports open. Only a site that sets [observatory] roof_motion_requires_safe_mount (a roof that can hit an unparked mount) keeps the mount parked until the roof is open. A roof that says it is opening is waited for up to ten minutes; [observatory] verify_shutter_timeout_s is only the patience for a roof that says nothing. If the verdict turns UNSAFE again while the roof is opening (a weather station flapping around its daylight threshold shuts the roof again by hardware), the run goes back to waiting for SAFE and asks again, for as long as the window lasts.
  4. Probe. Each filter gets two short in-memory looks (never saved). The sky rate is the slope between them and the camera's bias is the intercept, so a faint filter's rate is not inflated by the pedestal (a 1 s frame of a faint sky is mostly bias). A look that saturates is repeated at the exposure floor. If the camera sees next to nothing through every filter while the sun is inside the window, the run stops and says so ("almost no light ... is the roof open and the cover off?") instead of calling the sky too dark. And when the first look shows a sky that is already out of reach for every filter - at dusk the usual cause is a roof that opened late - the run says exactly that, with the sun altitude at which the camera first saw the sky, instead of four separate "sky too dark" lines.
  5. Shoot the filter that is about to leave its window. At every step the runner shoots the filter whose exposure is inside sky_min_exposure_s .. sky_max_exposure_s and closest to leaving it: at dawn the one nearest the floor (the most sensitive - B, then G, L, R on an LRGB rig), at dusk the one nearest the cap (the least sensitive). A filter the sky has not reached yet is waited for; one the sky has passed is dropped with the reason. Every exposure is predicted from the last measured rate and a brightening model whose doubling time is learnt from the run's own frames (sky_brightness_doubling_min is only the first guess), and every saved frame is read back from disk and metered. The exposure allows for the sky changing during the frame (the rate is integrated over the exposure): it makes no difference at 2 s, and it is what keeps a 40 s narrowband flat on target instead of 8 % under it.
  6. Count only good frames. A frame within the filter's tolerance counts. An off-target or saturated frame is not counted and, with sky_move_off_target on, is moved to a skyflat_rejects folder beside the flats - never deleted - so the folder you stack holds only good flats. A filter that keeps missing (clouds) is given up after a few misses.
  7. Stop when the light is cut. The safety verdict is checked before every frame: the moment it goes UNSAFE (on a site where the weather station closes the roof on daylight, that is the roof closing) the run stops instead of saving dark frames. A frame that comes out far darker than the sky model expected is treated the same way - twilight does not lose two thirds of its light between two frames, so the roof closed, the cover shut or cloud rolled in.
  8. Finish. The mount is parked (sky_park_on_end), the cover closed (cover_close_on_end) and the roof claim closed (sky_close_roof_on_end) - park first, then the claim. Inside a sequence phase both default off so the rig is left as the phase found it (tracking back on) and the plan's own on_end parks and closes.

The frames are saved as IMAGETYP=FLAT with a skyflat_<filter> prefix (folder per the save layout, as for Auto Flat). The job result has the Auto Flat shape plus order (the filters as shot), sun_alt_start / sun_alt_end, the measured bias_adu, the learnt doubling_min, ccd_temp_c (the sensor temperature at the first frame), stopped (why the run ended early, if it did) and per filter off_target. When a weather station reports the sky brightness (mag/arcsec2) it is logged beside every frame.

Settings ([flat], Settings -> Flat device -> Auto Flat, "Sky flats"):

| key | default | meaning | | --- | --- | --- | | sky_sun_alt_min_deg | -7 | bottom of the window: the darkest sky the runner will use | | sky_sun_alt_max_deg | -1.5 | top of the window; set it to where your roof closes at dawn / opens at dusk | | sky_min_exposure_s | 0.5 | shortest exposure (shutter and readout artefacts below this) | | sky_max_exposure_s | 5 | longest exposure = longest star trail; a filter that needs more waits (dawn) or is dropped (dusk) | | sky_probe_exposure_s | 1.0 | the first look at each filter, in memory, not saved (the second is three times longer) | | sky_flat_spot_ha_h | 1.0 | hours from the meridian, away from the sun | | sky_brightness_doubling_min | 3.0 | first guess for how fast the sky doubles (dawn) / halves (dusk); the run learns the real value | | sky_park_on_end | true | park the mount when done or aborted (page / API runs) | | sky_close_roof_on_end | true | then close the roof claim (page / API runs) | | sky_move_off_target | true | move off-target frames to skyflat_rejects (never deleted) | | sky_cool_camera | true | start the camera cooler 20 min before the window (never waits for it; left on at the end) |

A filter row may carry its own sky_min_exposure_s / sky_max_exposure_s: narrowband filters need a much longer cap than 5 s (and accept the longer trails).

Launching: the two buttons on the Auto Flat page; POST /api/console/flat/sky with {"when": "dawn"|"dusk"} (optional park_on_end, close_roof_on_end), which returns a job_id to poll like Auto Flat; or the Sky flats sequence action (sky_flats with when, and park_on_end / close_roof_on_end off by default) in a plan's on_start (dusk) or on_end (dawn) phase. Stop on the sequence cancels the flats job.

Needs: site coordinates (Settings -> Site) for the sun and the flat spot, a connected mount and camera, the FITS module, and at least one filter row. Bin 1 is what the plan rows say; the runner does not bin for you.

How to launch it

Both require a connected camera, the FITS module enabled (flats can't be saved without it), and at least one filter in the plan. If any of those is missing the run is refused with a clear reason.

Caveats

Astroworx Starship v22.2.10 · this page is the in-app help of that buildDownload · HTTP API