The estate now runs Astrometry.net itself (ankh-morpork-infra astrometry/):
solve-field against ~5 GB of local index files, sized from this repo's
TELESCOPES.md so the whole iTelescope fleet's fields of view are covered.
blind.py now tries it before nova.astrometry.net. Speed is the least of the
reasons - a blind solve of a real DSS field returns in 0.7s where nova queues
for minutes. The reasons that matter are that nova requires UPLOADING the
master to a third party and holding an API key, and neither is necessary any
more for the ordinary case. nova remains the fallback, so an estate outage
costs speed and privacy rather than the ability to solve.
pipeline/astrometry_net.py is the client and nothing more: stdlib-only POST,
parses the returned .wcs so the full TAN solution is used rather than a
re-derivation from the summary numbers. It deliberately does NOT decide whether
to trust a solution - blind.py already verifies every blind solve against Gaia
and applies a star-count and residual gate, and two gates that can disagree is
worse than one that is trusted.
The source ('estate' or 'nova') is threaded through the log lines, the returned
method, and the ASTRSOLV card, because a year from now that card is the only
way to tell whether a frame was solved in-house or uploaded.
Also corrected astrometry.py's 'cannot solve without a blind solver' message,
which has been untrue since run.py started falling through to blind.py.
Tested against the live service: blind solve of a DSS2 field with a known
centre returned within ~4 arcsec in 0.7s; the WCS round-trips through astropy;
and an unreachable service falls through to nova instead of raising.
Three fixes and one refusal, all found by working on the wide-field
narrowband session that would not solve.
The detection floor was a fixed 12 pixels, which quietly assumed a well
sampled star. At 0.53 arcsec/px with 5.9 px seeing that is right; at 3.5
arcsec/px the stars are undersampled at 1.75 px FWHM and cover a handful
of pixels each, so the floor discarded nearly every real star and kept
blends and galaxies instead. It now scales with the measured seeing, and
that session went from 305 usable detections to 600.
Matching now happens over the frame's inscribed circle rather than a cone
reaching its corners. On a 4 degree field the old cone covered 33 square
degrees of sky against 16 of image, so half the catalogue was not in the
picture at all.
Where the header records a roll angle - and iTelescope's does - the
orientation no longer has to be recovered from scratch. Rotation, scale
and parity are applied directly and only the residual pointing error is
searched, by histogramming every detection-to-catalogue offset and taking
the peak. Asterism matching remains as the fallback for headers that say
nothing about orientation.
The refusal matters most. With catalogue depth slices added, the wide
field produced a "solution" of 25 stars at 2.50 px residual, claiming a
centre half a degree from the pointing. A genuine solve on that field
matches hundreds of stars and refines to well under a pixel. Accepting it
would have silently corrupted every position in the science catalogue, so
a solution must now reach 40 stars AND 1.5 px or it is discarded and the
session is reported as unsolved. A wrong WCS is worse than no WCS.
NGC 2030 improves from 0.31 to 0.12 arcsec on 123 stars under the
pointing-based match. NGC 2070 is honestly unsolved.
astrometry.py solves the deepest master against Gaia and copies the WCS
into every master. Asterism matching is invariant to rotation and scale,
so camera angle never has to be guessed, but not to a mirror flip, so
both parities are tried.
Three defects were found and fixed by running it, and each is worth
recording because none would have been found by reading the code.
Gaia's launch_job_async submits to a job QUEUE and polls. On a query of
600 rows it hung for ten minutes while curl showed both the Gaia and
VizieR endpoints answering in under a second. VizieR is now queried
first - it serves the identical DR3 catalogue over a plain HTTP request -
with Gaia's SYNCHRONOUS endpoint as the fallback.
VizieR's row_limit truncates before sorting, so asking for the 600
brightest stars returned 600 arbitrary ones. Asterism matching only works
when both lists hold the same bright stars, so this silently produced no
match at all. It now fetches generously and picks the brightest locally.
The detector deliberately rejects saturated cores, which means the
brightest DETECTIONS are not the brightest STARS, while the catalogue's
are - so the two top-N lists can barely overlap. A sliding window down
the catalogue's magnitude ranking fixed NGC 2030, which matched at
catalogue[15:135], skipping the 15 brightest. That is the saturation
hypothesis confirmed rather than assumed.
NGC 2030 now solves on 243 stars at 0.31 arcsec residual. NGC 2070 and
NGC 6744 still do not, and TODO.md records what has already been ruled
out - scale, the fetch, truncation - so the next session starts from the
remaining candidates rather than repeating the elimination.