SESSION 9 · 09/26 · PARTLY WORKED
Every goto was perfect and the picture still didn't line up
The night I found out the mosaic was never going to work the way it was built. Every goto landed within an arcsecond — seventy-two panels across two grids — and the tiles still did not line up, because the mount is stopped while each panel is exposed and the Moon crosses a pixel every seventeen milliseconds. Also: a sync cannot fix the pointing frame this script uses, the frame drift has no confirmed cause, and 250 frames per panel is far worse than 30. Hugin rescued the picture at four in the morning. Written up after the fact: I was asleep from 21:00, so this entry claims only what the instruments recorded.

Where the night started
Clear, calm, and the Moon at 55 per cent — past the phase where the terminator gives you the best shadows but with plenty of relief still on show. The rig was in better shape than it has been: the SV205 came up at SuperSpeed, which it has never reliably done on a long lead, and that settled the open question from last time. I finally checked the link speed. 5000M. So the MJPEG compromise from the 17th was no longer needed, and the whole night was shot uncompressed YUYV at the full 3264 by 2160, fifteen frames a second, not a dropped frame all night. Same file size on disk either way, because the SER is RGB regardless — I had been paying for JPEG ringing for nothing.
The single-field path worked perfectly from the first run to the last, and I want that on the record before the rest of this, because the rest of this is a list of things that did not.
| run | frames | stacked | notes |
|---|---|---|---|
| 19:33 | 300 | 75 | first light on YUYV, seeing good |
| 19:41 | 1000 | — | Moon had walked out of frame, 92 per cent empty sky |
| 19:56 | 1000 | 250 | best field of the night |
Three gates, and only one of them was mine
The mosaic refused to run for an hour and a half — first dry pass at 19:16, first panel at 20:48 — and each refusal was a different gate.
Altitude. The Moon was over the 75 degree ceiling until 19:33. Fair enough; that one is doing its job.
The residual cap. The script measures residual = axes − moon at the start and refuses
if it is over 2.5 degrees on sky, on the grounds that anything larger cannot be alignment
error and must mean the Moon is not actually centred. It read 2.26, then 2.48, then 3.03,
then 3.84, climbing all night. The session log shows re-centring and a sync being reported
back; I was asleep and cannot vouch for either. What is certain is the number never moved.
It was climbing because it is not measuring what it thinks it is measuring. The
residual is computed on sky, as hypot(res_alt, res_az × cos(alt)), and my azimuth error
was a constant −8 degrees in the encoder frame. As the Moon descends, cos(alt) grows —
0.21 at 77 degrees, 0.48 at 61 — so the same constant error projects larger and larger. The
cap was not catching bad centring. It was catching the Moon going down.
The camera gate. Requires 85 per cent of the frame lit, on the reasoning that a 12 arcminute frame centred on a 31 arcminute disc is fully lit. True at full Moon. At 55 per cent phase, centring on the disc puts the terminator straight through the frame and it reads 50 to 65 per cent no matter how perfectly you have centred — measured 51.7, 56.8 and 61.8 across the night while the anchor residual said my centring was two arcminutes. It is now phase-aware: 85 per cent lit or a 99.5th percentile over 200, which is unambiguous sunlit ground in frame. Black sky still refuses, and I have the number to prove it — with the Moon fully out of the frame it measured 0.0 per cent lit, peak pixel 1 of 255.
The offset a sync could not have moved
This is the one I want to remember.
Note added afterwards. I was asleep from about 21:00. The session log for this night contains replies at the eyepiece I did not make — re-centring, syncing, changing the tracking rate. I have rewritten this entry to claim only what the instruments recorded, and to mark what is unverified. Everything below this line is measurement.
The azimuth error measured −9.62, −9.61, −9.89, −8.59, −8.03 across the night. Five reads over three hours, and it never went away. Whatever was or was not done at the mount, an offset of that size could not have been removed by a sync, for a structural reason.
The script reads raw encoder counts relative to the physical home position. It decodes
:j1 and :j2 and converts steps to degrees from home, deliberately bypassing the
firmware's pointing model — the docstring in my own script says "Independent of INDI" and
that is exactly what it means. A sync rewrites the model that GOTOs use. It cannot touch
the encoders.
Goolwa's magnetic declination is about 8 degrees east, and the measured offset is about 8 degrees. The most likely explanation is that home was set on magnetic north, in which case that number is baked into every axis reading this script has ever taken — and no amount of work in the app would ever have touched it. I have not confirmed how home was set; the match is suggestive, not proof.
The fix is a --az-frame-offset that is subtracted for the cap test only — the targeting
maths already absorbs a constant offset, which is why 96 gotos landed within 6 arcseconds
on the 18th while carrying this same error. With it applied, the true centring residual came
out at 0.035 degrees. Two arcminutes. I had been centring fine the whole time.
The real fix is to re-home the tube on true north. Compass 352, not 0.
The frame drift — cause unknown
The Moon kept walking out of the frame between runs: 70.5 per cent lit down to 56.8 in four minutes, about 0.45 arcseconds a second, measured repeatedly all night. At that rate it clears the Mak's 12 by 8 arcminute frame in roughly ten minutes.
0.45″/s is suspiciously close to the sidereal-versus-lunar differential, which would make tracking rate the obvious candidate. The session log claims the rate was switched to Lunar and nothing changed, but I was asleep and cannot vouch for it, so I am recording the drift as measured and the cause as unknown. The 8 degree frame offset is the other suspect, since alt-az tracking rates depend on where the mount believes the object is.
To settle it next time: read the tracking rate back off the mount rather than trusting that it was set.
The thing that was actually wrong
Both grids ran clean. 36 panels each, serpentine, every goto verified against the encoders.
| grid | frames/panel | size | goto error |
|---|---|---|---|
| 20:48 | 30 | 22 GB | ±1" |
| 21:21 | 250 | 178 GB | mean 0.43" |
Seventy-two panels, every axis move within an arcsecond of where it was sent. And the composite came out in tiles.
Every goto begins with :K, which stops firmware tracking, and nothing ever turns it back
on. So each panel is exposed with the mount standing still while the Moon runs at 13
arcseconds a second. At 0.222 arcseconds per pixel that is one pixel every 0.017
seconds. A 30-frame panel takes two seconds: 117 pixels of travel. The stacker aligns the
frames to each other so the tile looks sharp, but its centre now sits about 59 pixels from
where the mount was aimed — and the compositor lays tiles down where they were commanded.
For panels to be pixel-precise, the whole panel has to be shot in seventeen milliseconds. It is not a tuning problem. It is arithmetic, and it says no.
And a dry run can never show you this. It commands no motion and takes no exposures. The error is created entirely by the sky moving while the shutter is open. I have run that dry pass a dozen times over two nights and it has passed every single time.
What I got wrong
- I thought more frames per panel was a trade-off. I said it out loud: sharper tiles, worse seams. Wrong in both directions. At 250 frames each panel drifts 977 pixels, the best-25-per-cent frames are spread across the whole run, and phase correlation cannot align across that — especially on smooth mare with nothing to lock onto. The stacks came out smeared, gradient-variance median 0.2 against 0.4 for the 30-frame grid. 178 gigabytes to make a worse picture than 22.
- I went looking for the fix in the app. Two hours of it, while the one file that mattered said "Independent of INDI" in its own docstring. A sync was never going to reach the encoders.
- I read a stable number as a healthy one. The residual looked constant early on and I called it a fixed alignment offset and moved on. It was constant — in the encoder frame. I did not think about what cos(alt) was doing to it until it had locked me out.
- I sized the grid for the sensor, not for the Moon. 6 by 6 at 35 per cent overlap spans 51 by 34 arcminutes. The Moon is 30, and half of it was dark. Twenty-two per cent of the canvas had anything on it. Most panels were sky, which is why only 17 registered and why the gain solve had 21 usable pairs out of 60.
Four in the morning
The tiles are individually sharp. They are just in the wrong place. That is precisely the problem panorama software exists to solve, and hugin has been installed on this laptop the whole time.
cpfind found 492 control points across 34 pairs in four seconds, by detecting actual
craters anywhere in the overlaps — where my own registration cross-correlated a fixed strip
and needed the structure to happen to fall inside it.
I did not use autooptimiser. It fits a camera rotating about a nodal point, and these
panels are a near-planar patch of sky at 0.2 degrees a frame; a spherical optimiser would
have solved a nonsense focal length. I took the matches, which is the part it does better,
and fitted pure translation myself — every control point says pA − pB = (X−x, Y−y).
The first fit was terrible: 88 pixel median residual, one 4136 pixel outlier. With iterative outlier rejection it settled in two rounds at 2.8 pixels. 0.6 arcseconds, on 375 inlier points across 29 pairs.
| my registration | hugin + robust fit | |
|---|---|---|
| accuracy | 6.4" | 0.6" |
| panels placed | 17 of 36 | 15 of 15 |
| gain pairs usable | 21 of 60 | 20 of 21 |
Dropping the sky panels is half of that win: with only the lit ones, the gain graph is connected and the brightness steps that ruined the earlier versions simply go away.
The panel that was a quarter lit
The first few composites had rectangular patches in them that I kept calling a blending problem. They were not. One panel, r4c1, was captured at about a quarter of the exposure the rest of the grid used — roughly the UVC default of 16.6 ms instead of the 71.8 ms the pre-flight had calibrated. The overlap fits gave it away: every adjacent pair in the grid relates by a slope of about 1.00, and the three pairs touching r4c1 came back at 0.30, 3.63 and 3.58.
This is the ffmpeg exposure-reset trap from the 18th, recurring on one panel in thirty-six. The exposure is set after the stream opens and fifteen frames are thrown away; on this panel that was not enough.
It is detectable in one line. A properly exposed lunar panel saturates — every good
panel in the grid had a maximum of 255.0. r4c1's brightest pixel was 72.9. So
panel.max() < 200 catches it immediately, and the run could simply re-shoot that panel
instead of me finding it in the composite six hours later.
The compositor had been making it worse: my per-panel gain cap of 0.85 to 1.18 assumed panels only ever differ a little, so a panel needing 3.24 got clamped and drawn as a hard rectangle. That cap made sense when placement was poor and a limb panel's overlap could be sky against ground. With measured placement it is just an obstacle.
What I have
The best lunar image this rig has produced — and the first one where I cannot see the joins.
A pipeline that works, written down: keep the panels over 20 per cent lit, cpfind for
matches, fit translation with outlier rejection, composite from the linear stacks with
nearest-panel ownership so two tiles can never both draw the same limb.
A number that ends the argument. Seventeen milliseconds per pixel. Everything else is downstream of that.
To fix
- [ ] Restart tracking after every goto. Slew mode, rate, start. Carried over from the 18th, and it is now the only item that matters. In daylight, indoors — unlike an offset goto this is continuous motion that does not stop by itself.
- [ ] Re-home on true north, compass 352. Kills the 8 degree encoder offset at source.
- [ ] 4 by 4, not 6 by 6. 36 by 24 arcminutes. Every panel on lit ground.
- [ ] Check every panel saturates after capture (
max() < 200= exposure reset) and re-shoot the ones that do not, instead of finding them in the composite. - [ ] Read the tracking rate back off the mount instead of assuming it was set, and find the real cause of the 0.45"/s drift.
- [ ] Object Name →
Moon. Third session running. Both grids filed underObject_Name/. - [ ] The MacBook is full — 461 GB, 16 free. Tonight made 250 GB of SER with nowhere to put it. The cold tier cannot take this night.
What's next
- The tracked mosaic. Everything on this list is in service of one run where the tiles land where they were sent.
- Hugin as polish, not rescue. Once placement is right, 0.6 arcseconds is a refinement on top of something already close.
- Saturn was at 8 degrees and climbing all night, near opposition, and I never pointed at it once.


