SESSION 8 · 09/26 · WORKED
The mount moved on its own, forty-eight times, and every one of them was where I sent it
The night the laptop drove the telescope. INDI could connect but never finish a goto, so the mount went on raw motor-controller commands instead: a 0.05 degree test that landed to one arcsecond, then a 2 by 2, then a full 8 by 6 — 48 panels, 96 moves, none more than six arcseconds off. It took four hours of false starts to get there, most of them mine, and the best single frame of the night came from a fifty-frame manual run in between.

Where the night started
Camera in the Mak, the five-metre cable behaving — zero dropped frames all night, on the same lead that would not enumerate at all twenty-four hours earlier. I never did run the one-line link-speed check from [[usb3-or-no-max-resolution]], so whether it settled at 480 or 5000 is still an open question, which is embarrassing given it is one command.
The Moon was at 78 degrees at 18:22, above the 75 degree ceiling I had put in the mosaic script for the azimuth-rate reason, so the first hour was manual frames while it came down. And the camera settings were not what the script had set: AstroDMx reasserts its own defaults on open, so gamma was back to 300 and sharpness to 50 — [[inactive-uvc-controls-discard-writes]] has a sibling now, which is that another process owning the device re-writes the controls without telling you.
What the goto path turned out to be
Everything needed for INDI was installed and the driver connected without a slew — the
watchdog on the axis counts saw 0.05 degrees of motion over ten seconds, which is lunar
tracking, nothing more. Then the goto sat at _STATE=Busy and never moved the mount. Same
as 17 September. Two nights, same wall.
Raw SynScan commands need a carriage return. :e1 gets silence; :e1\r gets
=030FA5. Every probe I had ever sent without one, including the ones I had read as "the
module is busy, the phone must still have it", was simply malformed. Worse: the emergency
stop in my own script was sending :K1 without the \r. It would have done nothing.
The module is not single-client. Raw reads answered the whole time INDI held the
connection, and :K1\r :K2\r froze both axes instantly and kept them frozen. That is a
stop channel INDI cannot block, and it is the thing that made 10 September frightening
rather than manageable.
So the mount went on the motor controller protocol directly, the sequence libindi's own code uses for a short goto: stop, set low-speed goto mode and direction, set the offset in microsteps, set the ramp, start, poll until the running bit clears, read back. An offset goto is bounded by construction — it stops at its target — which is the whole reason it is safe where a bad sync plus tracking was not.
| test | commanded | landed | error |
|---|---|---|---|
| altitude up | +0.0500° | +0.0500° | −1" |
| altitude back | −0.0500° | −0.0500° | 0" |
One catch I found the hard way: :K stops the firmware's tracking on that axis, so
after any move the mount is no longer following the Moon, and both axes are stopped when a
run ends. INDI's abort does the same. I stopped tracking on myself three separate times
tonight before I understood that, and each time the Moon walked out of the frame while I was
looking for a fault somewhere else.
Two by two
Anchoring is the clever bit and I want it written down. The axis counts carry the
alignment residual — 0.8 to 1.5 degrees on sky tonight — so the script never points from
the ephemeris alone. At the start it assumes the tube is on the Moon, measures
residual = axes − moon, and every panel targets moon_live + grid_offset + residual,
moving by the difference from the current axis reading. Absolute ephemeris, relative
motion. The Moon's drift and the residual are both absorbed without ever being modelled.
Four panels at 19:50, sixty frames each, every goto within one arcsecond. And the top row
was only half lit: the limb ran through it. My "centred" was about eleven arcminutes
inside the disc edge, not at the disc centre — either I centred on a feature or the SV165
crosshair is that far off the Mak's axis. Fitting a circle of the ephemeris radius to the
limb points across the two panels put the true centre at +10.8' altitude, +1.1'
azimuth from where I had put it. That became a --shift for the full grid.
The same fit told me the image orientation, and told me wrong: it said +y was +altitude. A drift measurement I had dismissed said the opposite. The composite settled it hours later — rows laid the wrong way produce three copies of the limb stacked in bands — and the seam test agreed with the drift: +y is −altitude. [[measure-dont-derive]], and then also distrust the measurement that had the fewest points.
Eight by six
Between the 2 by 2 and the full run:
- A 7 by 5 plan aborted at panel 11 on a 0.54 degree move that would never happen live. In a dry run the mount does not move, so every panel's move was measured from the start point and grew across the grid. The plan now simulates the position.
- The first real move — half the grid plus the shift — is 0.6 degrees, over the 0.4 cap. Rather than raise the cap, long moves are staged as legs, each bounded and read back. And the legs have to respect the axis cap too: at 58 degrees altitude an azimuth command is 1.9 times the sky distance.
- The row-return move of a plain raster is 0.52 degrees. Serpentine instead — across, step, back — and every move is one panel.
- The pre-flight gate refused at 94 per cent lit, 7 per cent clipped. An hour earlier the same 115 ms had clipped 0.01 per cent. Something in the light path changed when I swapped the camera between scopes, and a separate exposure ladder came back black because the abort had stopped tracking and the Moon had drifted off before it ran. So the gate now calibrates its own exposure once the disc is confirmed in frame: 115 → 72 → 45 ms.
- A refusal before any motion no longer sends the emergency stop. There is nothing to stop, and stopping only kills tracking.
- I tried to arm a watcher that would start the run by itself once it saw tracking on and the residual under 2.5 degrees. The harness refused to let me run something that moves the mount unattended. It was right.
Then, at about 21:05 with the Moon at 48 degrees:
48 panels. 96 gotos. Largest landing error 6 arcseconds, typical 0 to 1. Twelve minutes. Every panel 60 frames at 45 ms, 0.00 per cent clipped, brightest panel at 203 of 255. Sixty-one gigabytes.
At 46 per cent lit only the sunward half is bright, so about sixteen of the forty-eight panels hold the Moon and the rest are the dark half and sky. I had sized the grid for a full disc. The lit limb crosses the edge panels rather than running off them, which is what the margin was for.
Putting it together
The stacking was the easy part: best 30 of 60 per panel, 16 seconds each. The compositing took longer than the capture.
First composite: 74 per cent of the canvas lit. Impossible for a half Moon. The stacker's PNGs are individually stretched for display, so a sky panel becomes full-range noise. Composite from the linear float stacks, one global stretch at the end.
Second: the limb three times over, in bands. The row order, above.
Third: a real Moon, with a doubled limb. The panels were untracked, and each sat up to 0.8 arcminutes from where it was commanded. Registering the interior panels against each other by phase-correlating their overlaps fixed the middle — thirteen panels moved, median 0.43'. The limb panels cannot be registered that way: a limb arc slid along itself correlates as well as the true match. I tried edge-based correlation (worse, the disc grew to 33'), a robust re-solve (one bad pair with nothing to contradict it survives), and a joint fit forcing limb points onto a circle of known radius (the terminator is also a lit/dark edge and got forced onto the circle; a panel ran away by thirty arcminutes). Stopped there. Four attempts, each worse.
What did work: give every output pixel to the panel whose centre is nearest, so nothing can double; feather only a 90-pixel band at the seams, narrower than any misplacement; and solve a gain per panel from the overlap medians — but only between registered pairs, capped at plus or minus 15 per cent, because an uncapped solve over mis-placed limb panels produced gains of 0.56 and a dark tile. The key image is that.
The honest fix is at capture, not in processing: restart tracking after every goto, or shorten the panels, so they sit within ten arcseconds of nominal and the limb never doubles.
The manual runs
Twenty-four between the automation, and I switched to fifty-frame runs partway through. That was the right call.
| run | time | frames | detail |
|---|---|---|---|
| 000349 | 18:41 | 500 | 6.89 |
| 000352 | 18:45 | 50 | 9.25 |
| 000363 | 18:52 | 50 | 11.92 |
| 000367 | 20:00 | 50 | 14.62 |
| 000370 | 20:29 | 50 | 8.06 |
| 000371 | 20:58 | 50 | 6.49 |
Last night's best was 7.18. 000367 doubles it, from twelve stacked frames. Two empties at 20:24 where I was re-centring. Twenty-two of twenty-four usable.
Run 000349 was also the one that settled an argument: I was sure 115 ms would flatten the seeing and kill lucky imaging, and said so. Its stack scored 6.89, level with the best of the night before. The exposure was fine. [[no-gain-control]] means the exposure is the only lever, and tonight the lever was in the right place before I touched it.
The archive, finally
Eight of last night's runs — two empty, six soft — deleted after checking each one's stack, array and log were on the Xeon. Eighty-five gigabytes back. Then the cold tier, which had never once run:
mnt-macbook-DUMP1.automountdead since the 14th withmount-start-limit-hit, leaving/mnt/macbook/DUMP1as a plain directory on the Xeon's root disk.test -dpasses on that. The archive script would have written 256 GB onto 199 GB of free root.- Underneath it,
mount error(2): No such file or directory— the Mac was not offering the shares at all. Fixed on the Mac. - The unit needed
reset-failedandstartas root.sudo -nworks on the Xeon. - The new guard then refused a live mount, because
findmntprints the autofs layer first and the CIFS mount second, and I read the first line.
Last night's eighteen runs are on the Mac: 18 of 18, 190,356,555,204 bytes, verified
against the source byte for byte. 178 GB in about forty minutes, roughly 80 MB/s onto the
spinner. Tonight's followed: 76 of 76, 100,888,961,128 bytes, verified — the 24
manual runs, the 48 mosaic panels and the 2 by 2. DUMP1 has 611 GB left. The one thing that went wrong in between was my job
that was meant to queue the second night behind the first: it waited on pgrep -f with a
pattern that appeared in its own command line, so it waited for itself, forever. Same trap
as the shell I killed earlier tonight, same note in the log since the 10th.
What I got wrong
- Read black frames as a blocked light path, again, and sent Brendan hunting for caps with a torch. Every one of them was ffmpeg resetting the exposure to 16 ms on open. The same pipeline had never produced an image from this camera; AstroDMx had produced twenty-six runs through it. When my tooling and the working software disagree, doubt the tooling first.
- Told him 115 ms would ruin the run. It scored 6.89. Then told him the histogram was clipping from a thumbnail; the data said 0.00 per cent.
- Recommended 640 by 480 for close-ups, one session after writing [[lower-resolution-is-not-a-roi]].
- Argued for the wrong image orientation from a limb fit, against a drift measurement that had it right.
- E-stopped on a pre-flight refusal, three times, killing tracking each time and then measuring the resulting drift as a fault.
- Ran an exposure ladder on a frame the Moon had already left, and nearly used the answer.
- Killed my own shell with
pgrep -fmatching the command that was running it. The note about that has been in my own log since the 10th. - Left the register script on its worst settings and built the next composite on them without noticing.
- Object Name is still
Object_Name. Fifth session.
Results
Success. The first automated lunar mosaic from this rig: 48 panels, every move within
six arcseconds, self-calibrated exposure, full sunward half captured with margin.
~/stacked/mosaic-8x6/mosaic8x6_FINAL_moon_full.png, and the same in the night's keepers
on the Xeon.
A proven stop channel and a proven goto path, neither of which existed this morning. The mount is now something the laptop can drive.
000367, the sharpest single field of any session, from a fifty-frame run.
The disk is no longer the constraint. Hot tiers for both nights verified on the Xeon; both nights' raw verified cold on DUMP1, the first time the cold tier has ever completed.
To fix
- [ ] Restart tracking after every goto in
lunar-mosaic.py— slew mode, rate, start. This is the whole limb problem. - [ ] Size the grid for the phase. Sixteen useful panels out of forty-eight.
- [ ]
cat /sys/bus/usb/devices/*/speedwith the camera in. Still. - [ ] Object Name →
Moon. - [ ] Delete tonight's raw once the cold verify passes, and last night's remaining eighteen.
- [ ] Find what in the camera swap changed the exposure by 2.5 times, or stop swapping.
- [ ] Work out whether the SV165 crosshair really is 11' off the Mak, and fix it if so.
What's next
- Full disc at a fuller phase, tracked panels, a quarter of the grid empty instead of two thirds.
- Fifty-frame runs as the default for single fields. The numbers are not close.
- Saturn is climbing. The goto path is now good enough to find it.


