"""Pass 1: measure every calibrated frame and cache its star list. Frames are 4788x3194 float32 (61 MB each) and the machine has little free RAM, so each frame is opened, measured and released one at a time. The star lists are cached to an npz because both the registration pass and the plate solve need them, and re-detecting costs more than re-reading a small array. Recorded per frame: sky background and its rms, the number of detections, and a median FWHM derived from sep's half-flux radius. The FWHM is the seeing metric used later to pick the registration reference and to weight the stack. """ import glob import os import re import numpy as np import sep from astropy.io import fits import layout SRC = layout.SESSION CACHE = layout.path("_stars.npz") os.makedirs(os.path.dirname(CACHE), exist_ok=True) NAME_RE = re.compile(r"-(Luminance|Red|Green|Blue)-BIN2-W-300-(\d+)\.fit$") def frame_list(): out = [] for path in sorted(glob.glob(layout.path("calibrated-*.fit"))): m = NAME_RE.search(path) if m: out.append((path, m.group(1), int(m.group(2)))) return out if __name__ == "__main__": frames = frame_list() print(f"{len(frames)} calibrated frames") store, meta = {}, [] for path, filt, idx in frames: with fits.open(path, memmap=False) as hd: data = hd[0].data.astype(np.float32) bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3) back_med = float(np.median(bkg.back())) rms = float(bkg.globalrms) sub = data - bkg.back() objs = sep.extract(sub, 5.0, err=rms, minarea=9, deblend_cont=0.005) objs = objs[(objs["flag"] == 0) & (objs["npix"] > 12) & (objs["npix"] < 2000)] objs = objs[np.argsort(objs["flux"])[::-1][:400]] rad, _ = sep.flux_radius(sub, objs["x"], objs["y"], 6.0 * objs["a"], 0.5, normflux=objs["flux"]) fwhm = float(np.median(rad) * 2.0) key = f"{filt}_{idx:03d}" store[key + "_xy"] = np.column_stack([objs["x"], objs["y"]]) store[key + "_flux"] = objs["flux"] meta.append((key, os.path.basename(path), filt, idx, len(objs), fwhm, back_med, rms)) print(f"{key:16s} stars={len(objs):4d} fwhm={fwhm:5.2f}px " f"bg={back_med:8.1f} rms={rms:6.1f}") del data, sub, bkg, objs np.savez_compressed( CACHE, meta=np.array(meta, dtype=object), **store, ) print("cached ->", CACHE)