SESSION 11 · 09/26 · WORKED
A mount that answered every question and refused to move
The best sky in weeks, and two hours lost to a mount that talked happily and would not turn. Saturn is a fortnight from opposition.
Started at 17:30 before the night, finished at 23:40 after it.
Clear, still, and — for the first time since 12 September — no mosquitoes. The forecast has cloud at 73% right now falling to 4% by sunset and 0–1% for the rest of the night, ground wind dropping from 13 km/h to about 7. If the sky holds this is the best night since first light.
The one blemish is upstairs: the 250 hPa wind sits at 112–126 km/h all night, a jet straight overhead. Beautiful transparency and mediocre seeing is a familiar pairing, and it is exactly the night where lucky imaging earns its keep — shoot deep, keep a small fraction.
Two hours on a mount that was never switched on
The mount answered everything. It pinged at 7 ms. :e1 gave me the board version, :j1
gave me a position, :f1 gave me a status. Every diagnostic said healthy. It would not
move a millimetre, from the app or from anything else.
The answer was in the status byte I had been reading all along and not looking at:
:f1 -> '=100'
^ third nibble, bit 0 = INITIALISED
Zero. Both axes. A Skywatcher motor board will happily answer queries while un-energised
and will not turn for any command until each axis gets :F. Both axes reading exactly
0x800000 — the power-on default, untouched — said the same thing and I did not hear it.
synscan-goto.py decodes that flag on line 61 and has never once sent :F. It assumes
the app energised the motors on connect, which is normally true. So the toolchain cannot
recover from this by itself, and worse, it cannot even notice: goto_offset() polls :f
until the running bit clears, and on an un-initialised axis it clears immediately. A goto
"succeeds" in one packet round-trip. A whole grid would march through every panel, report
every move landed, and move nothing at all.
That gate is missing and it should not be. One check on init before a run would have
said this in the first second instead of costing the evening.
Then it vanished, and that one was on me
:F fixed it — =100 became =101, both axes. Twenty minutes later the mount was gone
from the network entirely: 100% packet loss, no UDP, nothing.
Two things I did wrong. :F energises the motors, and energised steppers draw holding
current whether or not they are turning; I left both axes live for twenty minutes on a
mount that had been sitting on its batteries all afternoon. And the whole time I was
probing over UDP while the app was trying to connect — the wifi module takes one client,
which is the first line of my own checklist, written down precisely because it has bitten
before.
Power sorted it. The mount came back initialised, aligned, and tracking, and the encoders now agree with the ephemeris to 0.34° in altitude — the ~6° azimuth gap is the usual constant offset from home being set on approximate north, which the anchor maths absorbs.
Tonight's plan
| 18:06 | sunset |
| 19:31–20:51 | Moon over 75° — not shooting, field rotation |
| 20:56 → | Fomalhaut clears 60°, focus with the Bahtinov |
| 22:21–23:31 | Moon mosaic, the 45–60° band |
| 23:21–02:41 | Saturn, peaking 52.1° at 01:01 |
| from 02:00 | dew watch — spread closes to 2.3 °C by 04:00 |
The Bahtinov mask arrived in the post today, which after 20 September is well timed. Fomalhaut peaks at 84° — near enough the zenith that the azimuth rate gets unpleasant on an alt-az — so catch it climbing at 21:00 rather than at culmination, and confirm focus on the target afterwards, because it sits 40° from Saturn.
Saturn is a fortnight from opposition and the rings are still nearly edge-on after the March 2025 plane crossing. First proper attempt at it.
What is NOT running tonight
A lit-panel gate — skip the panels holding no sunlit Moon — is written, reviewed, fixed and
committed, and is staying on its branch. It has never seen the mount. Tonight runs on
main, which is the code that produced the last mosaic that worked.
The review was worth it. It found that a skipped panel leaves the previous run's SER in
place, and since everything downstream globs panel_r*c*, a re-run would quietly composite
frames shot at a different anchor and a different exposure. That is a worse failure than
the blank panels the gate exists to prevent, because blank panels are visible and that
would not be.
The picture I already have is as good as this aperture gets
Measured on FINAL3 from 19 September: the power spectrum falls steeply and dies at about
0.15–0.2 × Nyquist, putting one resolution element at 5–6 px. At 0.222"/px that is
1.1–1.3 arcseconds, against a Dawes limit of 1.14" for 102 mm. The optics are handing
over everything the aperture allows and the SV205 is spreading it across five pixels.
Binning a terminator crop 2× is indistinguishable from native.
No seams either — median brightness step across the disc 0.48 DN, and the only discontinuities the detector found were the limb itself. The per-panel gain solve works.
So the softness at 100% is empty magnification, not a focus fault. None of which is a new idea here — More magnification past the sampling limit makes the image worse, not bigger has been sitting in the learnings since 6 September, measured on a road sign with the 76/700 and a Barlow. What is new is that this is the recomputation its own last line demands, for a different optical train: 102 mm at f/12.75 wants ~2.55 µm pixels and has 1.4 µm ones.
It is also the rule I watched get broken today. Claude argued for the ASI678MC on sampling grounds — finer pixels, better sampled — which is exactly backwards for a tube already oversampled fivefold, and the shopping list had the correct arithmetic in it since 13 September. The principle was written down. It still got missed. The camera decision settles as it already had: the ASI662MC at $319, whose 2.9 µm pixels suit f/12.75 and whose 102 fps against 15 is the one change that would actually improve these images.
The night
The best mosaic this project has produced, and two hours of the evening thrown away on a mistake I made twice in different forms.
The mosaic
moon-2026-09-21-v3_moon_full.png, 6841 x 6341, fourteen panels.
| tonight | 19 September | |
|---|---|---|
| panels registered | 14 of 14 | 15 of 36 |
| control points | 476 inliers / 30 pairs | 375 / 29 |
| median residual | 0.64" | 0.6" |
| gain pairs dropped | 0 | — |
| gain range | 0.988 - 1.036 | capped at 1.18 |
Gains within three percent of unity means the panels are photometrically interchangeable, which is what a working exposure preflight buys. It settled on 72 ms twice, independently, in two separate runs.
The clouds that were not clouds
Two panels came back dark mid-run - r2c3 at 109, r3c3 at 3 then 1 out of 255. Cloud was forecast. I said cloud, and advised stopping the run. We stopped it, re-centred, and started again under a sky with zero cloud in it.
The same two panels failed again. Identically. r3c3 came back 3 then 1 a second time.
Weather does not repeat itself to the digit. Plotting every panel's peak made it obvious, and I should have done that before opening my mouth:
c0 c1 c2 c3
r5 122 1 1 1
r4 255 254 82 1
r3 255 255 217 1
r2 255 255 247 165
r1 255 255 255 253
r0 255 255 255 250
That is the limb of the Moon, drawn in exposure values. A curved boundary with 255 inside and 1 outside. Five panels of the twenty-four were pointed at empty sky, and at 0.63 GB each, shot twice, that is about 6 GB of photographs of nothing.
The lesson is not "check the sky". It is that a dim panel has two possible
causes and the pipeline conflates them. SATURATE_MIN measures one thing - did
this frame saturate - and cannot distinguish the exposure failed from there is
no Moon in this panel. So it blamed the UVC reset trap, prescribed a longer
discard, and re-shot blank sky at full size. The lit-panel gate written earlier
today exists precisely to stop those panels being shot at all; it is on a branch,
untested against the mount, and tonight is the measurement that justifies it.
The piece that was never photographed
Brendan looked at the composite and said a piece was missing. He was right, and it took me four attempts and an hour to work out why, because I kept assuming it was a registration problem.
It was not. The grid never reached the western limb.
Fitting a circle to the disc and measuring where the edge actually falls:
NOTCH rows y 2806-3645 (840 rows)
limb x stuck at exactly 316, expected 128-256
An edge that sits at exactly x=316 for 840 consecutive rows is not a limb. It is a straight vertical panel boundary. The confirmation was in a table printed hours earlier and misread:
c0 lit fractions: r0c0 58% r1c0 93% r2c0 99% r3c0 94% r4c0 58%
r2c0 is 99% lit - entirely inside the disc. The outer column never straddled the Moon's edge at all, so what looks like the limb in the middle of the frame is the edge of the mosaic. Roughly 190 px by 840 rows of Moon was never pointed at.
The miss is tiny and that is the galling part: the limb should reach x=128-256 and
the mosaic stops at 316, a sliver between 0.22' and 0.70' - under 42 arcseconds.
The grid needed to sit less than an arcminute further west. That is squarely what
find-centre.py exists to fix, and the same offset-anchor thread that runs through
the whole night.
None of the eighteen single runs can patch it either: every one measures 0.0% sky, so not one contains a limb. There is no data. It cannot be recovered in post by any means.
The separate problem: a panel that genuinely cannot be placed
r4c0 really is badly constrained, and this is true independently of the notch. Interior panels get 100-143 control points; the limb column gets 4 to 42, because a limb panel is half empty sky and half featureless bright edge. The spacing down the column shows it - the lattice step should be a constant ~1293 px:
r1c0 -> r0c0 : dy 1236 ok
r2c0 -> r1c0 : dy 890 off by -403
r3c0 -> r2c0 : dy 1758 off by +465
r4c0 -> r3c0 : dy 908 off by -385
The experiment that failed, recorded because it failed. The other thirteen panels define a lattice to a median 145 px, so I put r4c0 at its lattice prediction, 340 px away. A ghost seam opened along the limb: a straight diagonal line where the modelled panel overlapped its neighbour at the wrong offset. Trading a measured error for a modelled one is not progress.
The gains called it before the eye did. Measured placement gave gains of 0.988-1.036; modelled gave 0.862-1.076. A correctly placed panel agrees photometrically with its overlaps, so widening gains mean the placement is wrong. Cheap diagnostic, worth running before rendering anything.
Same wall mosaic-register2.py hit, and the old note stands: do not spend more
time on post-hoc limb registration. The fix is at capture - raise the overlap on
the outer column, or shift the grid so the limb falls through the middle of a
panel rather than along its edge.
Two failures that look identical and are not
The lesson worth keeping from all of this. A straight edge in a mosaic has two possible causes and they need opposite responses:
- a panel placed wrongly - fixable in post, shows as a ghost or a step with real surface on both sides
- a panel never pointed there - not fixable at any price, shows as a hard straight boundary with black on one side
A lit fraction near 100% on an outer panel is the tell for the second: it means that column is inside the disc and the grid is too small or off-centre. Check it before touching the solver.
Three things to fix
mosaic-hugin.py does not clear lit/ before writing it. Five symlinks from
an earlier run survived, mosaic-final.py globbed all eighteen, and the five with
no entry in the positions npz fell back to nominal placement. They are the floating
rectangle and the limb step in the first composite. Re-running with only the
thirteen registered panels fixed both, and took dropped gain pairs from 8 to 0.
Same bug class a code review found this morning in the SER output directory.
Deviation from commanded is 1.97' median, 3.54' max - worse than the 1.29' on
19 September, on a night when every goto landed within an arcsecond and the panels
were tracked. It cannot be drift and it cannot be pointing. What is left is the
grid geometry: SCALE = 0.222 or the step calculation. The 19 September note
recorded a global similarity fit wanting scale 1.0749 and dismissed it as
per-panel drift. With drift eliminated, that 7.5% deserves another look.
The single-frame runs beat the mosaic panels again. Eighteen of them, 000386 through 000403, and the sharpness climbed all night as the focus settled, the Moon came down out of the zenith mush and the sky cleared:
| run | time | sharpness (median) |
|---|---|---|
| 000386 | 19:11 | 23.8 |
| 000391 | 22:08 | 2.9 |
| 000393 | 22:25 | 48.1 |
| 000396 | 22:42 | 108.0 |
| 000403 | 22:45 | 75.6 |
000396 is the keeper of the night - best 131.3, worst 87.4. Its worst frame beats the best frame of anything shot before 22:25. 000391 at 2.9 sits inside the window I called cloud, and unlike the mosaic panels that one is not explicable by geometry, so it probably was.
What did not happen
Saturn. It is a fortnight from opposition, it was clear from 23:00, and it peaked at
52 degrees at 01:00. Getting there means a 74.7 degree slew - 127.6 degrees in
azimuth - which the toolchain refuses by construction: synscan-goto.py caps a
single move at 0.40 degrees and lunar-mosaic.py hard-caps at 1.5. That bound is
the safety property the whole thing rests on, so the app has to do it, and by then
it was late and the will had gone. Next clear night.