Skip to content
SkyCruncher

Note · Under the hood

Improvising a Dynamic Calibration Pipeline

A Seestar points straight up. A sheet of printer paper lies on its lens, and a phone lies screen-down on the paper. The phone's screen becomes a light source a program can draw on, and the scope's own sensor reports how its optics really behave.

Updated

A thin line beside a passage says how far its claim is proven: measured, tested, planned, inferred. Colored numbers carry the same mark. A passage with no line is stated. How claims are marked.

Where this stands: promising, not validated. Two sessions on one S30 Pro with one phone. The sensor numbers repeat well; the method needs a second phone and a second scope before it counts as a method. The test program isn't published. If you want to shoot calibration frames today, the calibration guide is the page to use.

Why improvise

Calibration frames are the pictures you take to learn what the camera does on its own: bias frames (zero-length, the sensor's offset), darks (the sensor's heat), and flats (an even light, which shows the lens's falloff and any dust). The usual flat calls for a light panel. A Seestar user has something close to one in a pocket: a bright, even, programmable screen.

Used as a plain white light, a phone gives ordinary flats. This test asks for more. Because the screen can draw anything, it can light one part of the lens at a time, which turns a desk into a small optics bench. The goal is to characterize one sensor and one set of optics with what's already in the house, in a run that needs almost no hands.

One finding sets the scope, so it comes first. On an app-shot M 33 night, the Seestar app was already dividing its telephoto subs by a measured flat for this unit: its dawn sky was flat to 0.14 %, and dividing again added a bright-corner bowl of 5 to 6 % (measured). So none of this improves a stack made in the app. It describes the hardware, and it serves frames shot raw.

Three objects on a desk

The setup, seen from the sideA Seestar on a desk with its arm raised so the lens points straight up. A sheet of paper lies on the lens, and a phone lies screen down on the paper. A computer beside it controls the scope over Alpaca on the home network, and sends the phone its patterns over Wi-Fi. Seestar phone, screen down paper lens, 30 mm sensor test programplans, shootsand checks Alpaca Wi-Fi: patterns,sounds
Side view, not to scale. The phone rests in the plane where light enters the lens.

A computer drives the scope over Alpaca, the open network protocol that astronomy software uses to talk to devices: exposure, gain, filter and the arm. The phone opens the program's page from a QR code and shows whatever it's asked to: white, a dimmed level, a bar, a pattern. It also makes a sound when a step needs a hand.

The odd shape is forced. The scope sends no CORS headers, the permission a browser needs before a web page may read another address, on either Alpaca server version tried (1.2.0-3 and 1.3.1-1) (measured), and Chrome also blocks calls from a secure page to a device on the home network. So a plain web page can't drive it. A small program on the computer serves its own page and talks to the scope outside the browser.

The paper spreads the screen just enough to hide its pixel grid. The blur should be well under a millimeter across a 30 mm aperture, so the patterns survive (inferred). There's a privacy side to the layout too: with the lens pointing straight up, the camera only ever sees a phone and a ceiling.

Why a screen on the lens tells you about the optics

A camera focused on the stars can't focus on something resting on its own lens. So the sensor never sees the pattern as a picture. Every pixel on the sensor sees the whole screen, through its own slice of the lens: a corner pixel looks through a different set of edges and stops than a center pixel does. Each frame is the pattern, weighted by how each pixel views each point of the aperture:

frame(θ) = ∫ V(u, θ) · P(u) du P(u) is the pattern drawn at aperture point u. V(u, θ) is the unknown: how much of aperture point u the sensor pixel at field position θ receives.

Show enough known patterns and you can solve for V, a map of the pupil (the opening the light actually gets through) at every point in the field. It's a single-pixel camera run backwards: the patterns go into the lens, and each of the sensor's 8.3 million pixels (2160 × 3840) reads one weighted sum.

andA pattern and its complement. Their difference cancels room light and the sensor's offset.
The 16 Hadamard patterns the deep run uses: on-and-off grids built so that each is independent of the others, and every one after the first lights exactly half the screen. 16 patterns give a 4 × 4 map of the pupil. Each is shot with its complement, which makes 32 frames.

Colors can share a frame. Draw three different patterns in red, green and blue, then separate them by the sensor's color planes, and one frame does the work of three. Only one color-coded pair has been shot so far: its unmixed fractions landed within 0.004 of the same shapes shot in black and white (measured).

What makes it dynamic

Nothing about a session is fixed in advance except the order of the steps. Phones differ in brightness, nobody puts one down in the same spot twice, and an exposure that suits one screen level clips at another. So the program measures first, then plans the next step from what it found. On the second session:

Signal against the position of a bright bar on the phone screenTwelve bars. The signal is near zero when the bar is off the lens, rises to about 75,000 counts per second between screen positions 95 and 220, and falls back to near zero by position 285. A disk fit puts the lens center at 169.8 and its radius at 94.3 screen pixels.020k40k60k80k0100200300400fitted widthBar position on the phone screen (screen pixels)Signal (counts per second)
The lens sweep across the screen after the phone was turned, from the second session. A bar off the lens gives almost nothing. A disk fit puts the center at 169.8 and the radius at 94.3 screen pixels; the other axis gives 93.6, so the lens reads as round (measured).

The checks came from a failure. On 2026-10-09, the first full run of an earlier program shot 221 frames. The bias, darks and brightness ladder were good. The flats were not: a bright room fooled its "is it lit?" check, so they were flats of the ceiling. A saturated frame fooled its "is it covered?" check on the wide camera too (measured). Single-frame checks gave way to references: the program measures the room first, then asks whether the phone changes anything.

The current program passed 69 of 69 checks against a simulated scope at both depths (tested), before and apart from the real runs below.

What runs, in order

The light run takes 191 frames, reference flats included. It's planned at 14.7 minutes, and the second session took 12.9 (measured). Two moments need a hand: setting up, and turning the phone. Everything else the program works out from quick frames, such as when the light has come back.

The light run on the telephoto camera. A deep run adds a ladder at gains 0, 80 and 300, the Hadamard set, the color-coded pair and colors through the LP filter: 406 planned frames in 30.7 minutes of planned time.
StepWhat it doesFrames
Arm up, paper, phoneTurn on Alpaca once in the Seestar app. The program raises the arm until the lens points straight up; keep a hand on the scope while it moves, then lay paper and phone on top.by hand
BiasZero-length frames, with the Dark filter in: the sensor's offset and read noise before any light.6
Light checkIs the phone the light? Switching the screen to black must remove 90 % of the signal, twice, or room light would be counted instead.4
Find the lensA bright bar sweeps across the screen, 12 positions in x and 12 in y. Where the signal peaks is where the lens sits under the phone.24
Set the levelDim the screen until a flat needs at least 1 s, which outlasts the screen's flicker.4
LadderPairs of flats from 2 % to 120 % of the clip level: gain, read noise, linearity and where the sensor clips.26
ColorSolid red, green, blue and white, through the IR-cut filter: how the phone's colors land in the sensor's color planes.9
LP filterThe same light through the light-pollution filter: how much it passes, its flats, and red-only and cyan-only flats for its two bands.33
PatternsHalves of the lens and five small disks, each as a pair. Only the deep run adds the Hadamard set below.19
Turn the phoneA half turn on the paper, then the lens is found again and the halves and disks repeat. What turns with the phone belongs to the phone; what stays belongs to the scope or the paper.47, one touch
BlinkThe screen flashes at a known rate. The stripes it leaves give the sensor's readout time.5
DarksThe Dark filter goes in and the screen goes off; the phone can stay where it is. Check darks and darks for the flats. Then the arm folds itself.14
A bias frame: even gray noise with no structure.
BiasNo light at all.
A short exposure of the white screen, crossed by strong vertical bands.
A short exposureFull white, a fraction of a second: bands from the screen's flicker.
A flat of about one second: smooth, slightly darker in the corners, with no visible bands.
A 1.1 s flatThe bands wash out. The corners fall off.

Real frames from the second session, binned down and stretched for the screen, each on its own scale, so their brightness can't be compared (measured).

Phones dim by pulsing their screens, and a sensor that reads out line by line catches each pulse as a stripe. The stripes repeat every 234 pixels (the same in both sessions). At the measured 17.8 µs per line, that's 240 Hz: twice the phone's 120 Hz refresh, so two independent numbers agree (measured). Longer exposures average them away, which is why every flat here runs a second or more.

What the sensor said

The gain here is how many electrons each count stands for, and a count (ADU) is the unit the camera records. Light arrives in random packets, so its noise grows with its signal, and the slope of noise against signal gives the gain with no reference to anything else. That is photon transfer, and it needs only pairs of flats at rising brightness.

Noise variance against signal for pairs of flatsTwelve points climb a straight line from near zero to a variance of about 960,000 at a signal of about 57,500 counts, then the variance collapses to almost zero as the sensor clips near 64,400 counts above black. The straight line has the slope of a gain of 0.0592 electrons per count.0250k500k750k1000k020k40k60kslope sets the gainclipped: noise goneSignal above black (counts)Variance (counts squared)
Photon transfer from the second session: telephoto, gain 200, IR-cut filter, raw frames over Alpaca, first green plane. The dashed line is the gain the program fitted across the color planes from the points between 5 % and 80 % of full scale: 0.0592 e⁻ per count (measured).
One S30 Pro, telephoto, gain 200, raw frames over Alpaca. Session 1: 2026-10-13, deep run, 505 frames, sensor at 26.3 to 30.8 °C. Session 2: late on 2026-10-28, light run, 191 frames, sensor at 30.8 to 31.3 °C. Session 2's numbers come from the program's built-in analysis and haven't had a separate review. Ranges and triples run across the color planes (green, red, blue) (measured).
QuantityFilterSession 1Session 2
Gain, e⁻ per countIR-cut0.0591 to 0.05960.0588 to 0.0595
Gain, e⁻ per countLP0.0587 to 0.05920.0586 to 0.0595
Read noise, countsDark17.7 to 17.917.9 to 18.0
Read noise, electronsDark1.051.06
Black level, countsDark1111.5 to 1112.11112.3 to 1112.9
Worst linearity error, 5 to 80 % of clipIR-cut0.36 %0.20 %
Readout, whole frameIR-cut38.4 ms38.4 ms
Flat, corner over centerIR-cut0.946 / 0.951 / 0.9440.946 / 0.951 / 0.945
Flat, corner over centerLP0.941 / 0.952 / 0.9420.939 / 0.952 / 0.940
LP light as a share of IR-cut light, phone whiteLP vs IR-cut7.0 %5.4 %
Flicker bands near 1 sIR-cut0.06 to 0.07 %0.05 %
Drift of the light over the runIR-cut0.29 % in 26.6 min1.01 % in 6.4 min
Red + green + blue screen vs white, worst planeIR-cut4.5 % short1.4 % short
Dark current at about 31 °CDarknot shot this way0.57 e⁻/s

The gain repeats to better than 1 % on new frames, and it's the same through both filters, as a property of the sensor should be. Raw frames clip hard at 65,520, the top code of a 12-bit converter written into 16 bits, and only every 16th count value occurs (measured). The two that moved both involve the light source, not the sensor: how much of the phone's white passes the LP filter, and how much the light drifted. Neither has been explained yet.

The sensor at four gains, raw route. Session 1 ran full ladders. Session 2's deep run stopped after 60 frames, so it adds bias frames at the four gains and nothing lit (measured).
Gain settinge⁻ per countRead noise, e⁻Black, S1 / S2Read noise, counts, S1 / S2
00.6146.71002 / 100210.9 / 11.0
800.2464.91065 / 106619.8 / 19.9
2000.05941.051112 / 111217.8 / 17.8
3000.0180.861453 / 145547.8 / 48.2

Read noise falls almost fivefold between gains 80 and 200, and barely after that. That shape would fit a sensor switching to a quieter readout mode somewhere between them, but four gains can't place a switch, and nothing here has tested for one directly (inferred).

What the patterns said about the lens

Predictions, written down first

Before session 2, its expected results were written down, so the session could miss them. The table scores them against the session's quick analysis. The misses are the useful part.

Scored for this Note from session 2's analysis. Others on the list need a deep run or live testing and stay open: colors through the LP filter, and the pause and phone-call handling (measured).
PredictionResultScored
Gain at 200 within 2 % of 0.05940.0592Hit
Readout within 2 ms of 38.4 ms38.41 msHit
IR-cut flat, corner over center, within 0.003 of 0.9460.945 to 0.947Hit (green)
Lens radius within 3 % of 93.7 screen pixels after the turn94.3 × 93.6Hit
Check darks within 20 counts of bias−4.3, −4.1Hit
Red and cyan band flats match each other within 0.01at most 0.0095 apartHit
Red-only through LP: red plane holds at least 80 %69.6 %Miss
Cyan through LP: red plane under 10 %13.9 %Miss
The 11 % dim spot turns with the phone (a 60 % lean)not seen againNo score

Raw frames and app frames, kept apart

Every number above is on the raw route: frames over Alpaca, with no white balance, no flat and no dark taken off. A sub saved by the Seestar app (the app route) has had the app's dark subtracted, been multiplied by a white balance and divided by the app's flat, and had a pedestal added back. Raw frames clip hard at 65,520. In app subs on firmware 8.46, green stops softly lower, about 55,500 at the center of the frame and varying across the field, while red and blue reach 65,535 (measured).

So a raw number can't be used on an app sub as it stands. Dividing the raw gain by the app's white-balance multiplier gives an estimate at the center of the frame, and it differs by filter, which is why filter results stay separate here. Green is about 0.072 e⁻ per count through either filter; red is 0.050 through LP and 0.042 through IR-cut; blue is 0.043 and 0.039. Those multipliers were fitted on firmware 8.46 nights and the gain was measured on 9.31, so whether they carry across is still an open question (inferred).

What it's meant to give

None of those are delivered results yet. The gain, read noise, linearity, clip level and readout are the part that's already measured.

What it still needs

Where these numbers come from