First stage of the generic pipeline. The existing scripts parse filenames, expecting calibrated-T32-...-Luminance-BIN2-W-300-001.fit, which works for exactly one telescope, one binning and one exposure length. Three of the four archived sessions have no luminance channel at all, so that approach cannot process them. session.py opens each frame and reads FILTER, EXPTIME, XBINNING, OBJECT and DATE-OBS instead. It groups frames by filter, works out which colour scheme the session supports (LRGB, RGB, SHO, HOO or mono), and writes a manifest. Filter aliases are mapped, so a header saying H-alpha, Halpha or HA all resolve to Ha. Three real defects were found by running it against the archive rather than by reasoning about it: Frame counts came out doubled. iTelescope ships every frame twice, as 'calibrated-*' with bias, dark and flat applied and 'raw-*' without, and extracting both put two copies of each frame into the stack. Only the calibrated archives are extracted now, with CALSTAT used as a header level safety net for sessions that were already extracted the old way. NGC 5128 now scans as 12/4/4/4, exactly matching the session processed by hand, which makes it a usable regression test. A re-mirror over the organised tree produced duplicates and mirror damage. An interrupted segmented download leaves DIRECTORIES named after the file being fetched - a directory called something.fit - plus .cyberducksegment parts. Anything that trusts a file extension will try to open a directory as a frame. Those are now skipped explicitly at discovery, ingest and scan, and byte-identical copies that a re-mirror puts back are removed as they are refiled. Two T20 archives are corrupt and cannot be extracted. That is real data loss, not a bug: the session has 14 of its 16 frames and the warning is printed rather than swallowed. Not yet done: NGC 6744 mixes bin1 luminance at 4096x4096 with bin2 colour at 2048x2048, so registration will need to resample onto a common grid rather than assuming every frame shares a shape. |
||
|---|---|---|
| .. | ||
| analyse.py | ||
| annotate.py | ||
| closeup.py | ||
| compose.py | ||
| depth.py | ||
| enhance.py | ||
| final.py | ||
| gaia_colours.py | ||
| gc-bwtrial.py | ||
| gc-classify.py | ||
| gc-complete-inner.py | ||
| gc-complete.py | ||
| gc-detect.py | ||
| gc-fetch-vizier.py | ||
| gc-gaia-astrom2.py | ||
| gc-plots.py | ||
| gc-validate.py | ||
| hdr.py | ||
| layout.py | ||
| mo_cavs.py | ||
| mo_check.py | ||
| mo_common.py | ||
| mo_detect.py | ||
| mo_fig_moving.py | ||
| mo_fig_transient.py | ||
| mo_finalise.py | ||
| mo_gccheck.py | ||
| mo_link.py | ||
| mo_mpc.py | ||
| mo_sensitivity.py | ||
| mo_shiftstack.py | ||
| mo_transient.py | ||
| mo_vet.py | ||
| original.py | ||
| README.md | ||
| rename.py | ||
| restructure.py | ||
| sb_common.py | ||
| sb_dust.py | ||
| sb_iso.py | ||
| sb_limits.py | ||
| sb_model.py | ||
| sb_prep.py | ||
| sb_profile.py | ||
| sb_render_tail.py | ||
| sb_residual.py | ||
| session.py | ||
| solve.py | ||
| stack.py | ||
| starless.py | ||
| subdir_readmes.py | ||
| triptych.py | ||
| unzip.py | ||
| verify_core.py | ||
pipeline
Processing and analysis code for remote-telescope imaging sessions, starting with iTelescope data from the itelescope drain campaign.
The code lives here. The data does not - image sessions stay on disk (or wherever they are archived) and are addressed by an environment variable, so a session directory contains only pixels, results and a description of what was done to them.
What is here now
pipeline/ - the 50 scripts that processed the NGC 5128 session of
2026-07-21, exactly as they were run, plus the shared layout.py that tells
them where files live. This is a working record rather than a finished product:
the scripts were written in sequence as the work went along, several of them by
parallel agents, and they show it. They are kept because they are the honest
provenance of a set of published results, and because the productionised
pipeline should be able to reproduce those results exactly.
pipeline/restructure.py - reorganises a flat session directory into the
named layout below. Idempotent, dry run by default.
Pointing the scripts at a session
set ASTRO_SESSION=D:\astro\NGC5128\20260721 # Windows
export ASTRO_SESSION=/data/astro/NGC5128/20260721 # POSIX
python pipeline/layout.py # prints the resolved layout
layout.py maps a filename to its subdirectory, so a script asks for
master-Red.fit or _stars.npz and gets the right path without knowing the
directory structure:
| Directory | Holds |
|---|---|
raw/ |
exactly what the telescope delivered: archives and their preview jpegs |
calibrated/ |
uncompressed calibrated subs |
stacks/masters/ |
per-filter registered, plate-solved masters |
stacks/original/ |
alignment-only baseline stacks, no other processing |
final/ |
the deliverable renderings |
renderings/ |
other finished images |
science/figures/ |
analysis plots |
science/catalogues/ |
measured tables (CSV) |
science/data/ |
models, masks, derived quantities |
science/notes/ |
analysis write-ups |
intermediates/ |
caches a re-run can regenerate |
Every session directory also carries its own METHODS.md describing what was
done to that data and what was found - written for a reader who was not there.
Running order
The scripts are named for their stage and run in this order:
unzip.py -> analyse.py -> stack.py -> solve.py -> depth.py
-> compose.py -> hdr.py / enhance.py / starless.py / annotate.py
-> final.py -> closeup.py -> triptych.py
The analysis families are independent of each other and of the renderings:
gc-* (globular clusters), sb_* (surface photometry), mo_* (moving objects
and transients).
Requirements
Python 3.12 with numpy, scipy, astropy, scikit-image, sep, astroalign, photutils, astroquery, matplotlib, tifffile, Pillow.
Where this is going
The next piece of work is a scheduler-driven pipeline: a staged CLI
(ingest -> calibrate -> measure -> register -> stack -> solve -> compose -> analyse) with each stage resumable, packaged as an Apptainer image and driven
by Slurm array jobs. Targets beyond mono LRGB: narrowband palettes, one-shot
colour with debayering, other observatories' header conventions, and full
calibration from bias/dark/flat for sources that do not pre-calibrate.
Three findings from the first session are requirements for that build, not optional extras:
- Vet moving-object candidates in detector coordinates. Registration holds the sky still, so it drags detector-fixed defects across the frame on perfectly straight, constant-rate tracks. Hot pixels are better-behaved asteroids than real asteroids. This one cut took 141 confident spurious detections to zero.
- Carry
r50/psfthrough to any catalogue cross-match. Comparing an aperture magnitude of a resolved source against a point-source catalogue like Gaia is meaningless, and looks exactly like a 2.8 magnitude outburst. - Never fit a sky background to a field the target fills. A plane fitted around a large galaxy absorbs its halo - measured at -17.9 ADU/px here. Fit the background and a source model together.