Processing and analysis code for remote-telescope imaging sessions

The scripts that processed the NGC 5128 session of 2026-07-21 previously
lived inside the data directory and addressed it with absolute paths.
Code and data are now separated: the code lives here, and a session is
located at runtime through the ASTRO_SESSION environment variable.

layout.py is what makes that work. It maps a FILENAME to the
subdirectory that file belongs in, using the same rules the session
directories are organised with, so a script can go on asking for
'master-Red.fit' or '_stars.npz' without any call site knowing the
directory structure. Anything unrecognised resolves to the session root,
which is visible and correctable rather than silently wrong.

restructure.py reorganises a flat session directory into that layout. It
is idempotent and dry-run by default.

The 50 session scripts are kept as they were run rather than tidied into
a library. They were written in sequence as the work went along, several
of them by parallel agents, and they show it - but they are the honest
provenance of a published set of results, and the productionised pipeline
should be able to reproduce those results exactly.

Verified before committing: all 51 files compile without warnings, and
verify_core.py, closeup.py and triptych.py were run end to end against
the reorganised session, correctly finding inputs across calibrated/,
stacks/masters/ and final/ and writing outputs back to the right places.
This commit is contained in:
laurence 2026-07-21 15:29:49 +01:00
commit 5286a2e81b
53 changed files with 8820 additions and 0 deletions

33
session-scripts/unzip.py Normal file
View file

@ -0,0 +1,33 @@
"""Uncompress every calibrated-*.zip in the session folder, in place.
iTelescope ships each calibrated frame as a one-entry zip (.fit.zip and
.tif.zip). Extraction is skipped when the target already exists and is
non-empty, so this is safe to re-run.
"""
import glob
import os
import zipfile
import layout
SRC = layout.SESSION
zips = sorted(glob.glob(layout.path("calibrated-*.zip")))
print(f"{len(zips)} calibrated archives")
done = skipped = 0
for z in zips:
with zipfile.ZipFile(z) as zf:
for info in zf.infolist():
target = layout.path(os.path.basename(info.filename))
if os.path.exists(target) and os.path.getsize(target) == info.file_size:
skipped += 1
continue
with zf.open(info) as fsrc, open(target, "wb") as fdst:
while chunk := fsrc.read(1 << 20):
fdst.write(chunk)
done += 1
print(f"extracted {done}, already present {skipped}")
for ext in (".fit", ".tif"):
n = len(glob.glob(layout.path(f"calibrated-*{ext}")))
print(f"{ext}: {n} files")