NOTE · 09/26

How the lunar mosaic is made, end to end

Written down properly after the 19 September run, because the method now has enough moving parts that I will not remember which order they go in, or why half of them are the way they are. This is the whole path from a cold tripod to a finished picture.

The rig

A 102 mm f/12.7 Maksutov at 1300 mm on the AZ-GTe, SV205 at prime focus, SV165 guidescope alongside for centring. Measured plate scale 0.222 arcseconds per pixel, which puts a single frame at 12.08' x 7.99' — about a third of the Moon's width. That is the whole reason a mosaic exists. The camera runs over USB 3 at SuperSpeed, uncompressed YUYV at the full 3264 x 2160 and fifteen frames a second; MJPEG was only ever a workaround for a cable that forced the link to USB 2.0, and JPEG ringing survives stacking.

Alignment, and its one great trap

The mount is homed with the tube level and pointed north, and everything downstream reads raw encoder counts relative to that physical home — :j1 and :j2 decoded as degrees, deliberately bypassing the firmware's own pointing model. That independence is what makes the motion path safe. It also has a sharp edge: because the script never consults the model, a sync or alignment in the SynScan app cannot correct anything it sees. Home set to magnetic rather than true north bakes Goolwa's ~8 degrees of declination into every axis reading, permanently. The only fixes are to re-home on true north — compass 352, not 0 — or to declare the offset with --az-frame-offset.

The handover

The script never points from the ephemeris alone, because the axis counts carry that residual. Instead I centre the Moon on the SV165 crosshair and hand over. The script reads the axes, computes residual = axes - moon, and holds it constant for the whole run. Every panel then targets moon_live + grid_offset + residual, moving by the difference from the current reading. Absolute ephemeris, relative motion — the Moon's drift across the run and the alignment error are both absorbed without either being modelled. A --shift corrects for the crosshair sitting about 11' off the Mak's axis.

Three gates must pass before anything moves: Moon below 75 degrees, centring residual under 2.5 degrees, and a camera frame proving the telescope is on sunlit ground rather than black sky. That last one is phase-aware now — at 55% illumination a disc-centred frame reads 50-65% lit no matter how good the centring, so it accepts either a mostly-lit frame or unambiguous sunlit ground in view.

Dry runs, and what they cannot tell you

Every run is planned in dry mode first. It commands no motion and takes no exposures, simulates each axis position, checks every move against the step caps and aborts on any gate failure. It catches bad anchors, oversized moves, grid geometry errors.

What it structurally cannot catch is the defect that dominates the result, because that one is created entirely by the sky moving while the shutter is open. A dozen clean dry runs across two nights, and misaligned output every time.

Motion

Gotos go over the raw SynScan motor-controller protocol, not INDI, which sat at Busy for two nights and never moved the mount. Per axis: stop, low-speed goto mode and direction, offset in microsteps, ramp, start, poll until the running bit clears, read back. Every command needs a trailing carriage return. An offset goto is bounded by construction — it stops at its own target — which is why it is safe where a bad sync plus continuous tracking was not. Each landing is checked against the encoders and the run aborts if a panel lands more than 36" off. In practice all 72 panels across two grids landed within one arcsecond.

The serpentine

Across, down one, and back — rather than raster-scanning and returning to the start of each row. A plain raster's row-return is over half a degree; serpentine makes every move a single panel step. Long moves are staged into legs so no single command exceeds 0.40 degrees, and the legs respect the axis cap as well as the sky one: at 58 degrees altitude an azimuth command is 1.9 times the sky distance it represents.

The flaw at the heart of it

Every goto begins with :K, which also stops the firmware's tracking, and nothing restarts it. So each panel is exposed with the mount standing perfectly still while the Moon runs at 13 arcseconds a second. At 0.222"/px the Moon crosses one pixel every 17 milliseconds. A 30-frame panel takes two seconds and therefore travels 117 pixels. The stacker aligns those frames to each other so the tile looks sharp, but its centre now sits about 59 px from where the mount aimed.

Untracked pixel precision is not difficult. It is arithmetically unreachable.

This is also why more frames per panel makes it worse, not better: at 250 frames the drift reaches 977 px, the selected frames span the whole sweep, phase correlation can no longer align them, and the tiles come out smeared. That grid could not even be registered afterwards.

Stacking

Each panel's frames are ranked by Laplacian variance and the sharpest 25% stacked with sub-pixel phase-correlation alignment, giving a linear float array per panel. Everything downstream works from those arrays and never from the per-panel PNGs, which are each stretched to their own percentile range — a stretch that turns sky into full-range noise and hands neighbouring tiles incompatible gains.

Stitching

Since the panels are misplaced, they cannot be laid out where they were commanded. Hugin's cpfind detects features — actual craters — anywhere in the overlaps, and those control points are fitted to a pure translation model with iterative outlier rejection. Hugin's own autooptimiser is deliberately not used: it fits a camera rotating about a nodal point, and these panels are a near-planar patch at 0.2 degrees a frame, so a spherical solver would invent a nonsense focal length. A connectivity check refuses the job outright if the control points leave any panel unlinked, because an unlinked panel's position would be invented rather than measured.

Compositing assigns each output pixel to the single nearest panel centre, weighted by how well-exposed each panel is, so two tiles can never both draw the same limb arc. A wide feather softens the ownership boundaries, and one gain per panel is solved from the median ratio in each overlap — wide enough to rescue a panel that caught the exposure-reset trap and came out at a quarter brightness.

The result

image 7054 x 5213 px (36.8 Mpx)
field 26.1' x 19.3' (1566" x 1157")
scale 0.222 arcsec/pixel
subject Moon, 30.0' diameter, 55.1% illuminated
panels 15 used of 36 captured, 12.08' x 7.99' each
frames 1080 captured (22.8 GB); 450 in the final; ~112 actually stacked
capture time 5 min 13 s
goto accuracy ±1 arcsec, mean error 0.43"
registration 0.63 arcsec median residual, 385 control points over 29 pairs
gains 0.905 – 3.241

The honest summary is that the pointing was never the problem. The mount is exact to an arcsecond. The entire difficulty lives in the two seconds after it arrives, when it stands still and the sky does not. Restarting firmware tracking after each goto would collapse the drift to nothing, put the tiles where they were commanded, and demote Hugin from a rescue to a polish.