PSF wing extension (wcc_sim.wings)

Analytic PSF wing model extending the finite stamps to large radii.

The rendered stamps truncate the PSF at the stamp boundary, which shows up as a square “postage stamp” edge around bright stars once the display stretch reaches the wing level (~100 sigma above sky for a G=8 star with the 129 px in-focus stamp). The wing beyond the stamp is smooth and follows a power law – r^-3 for the jitter-blurred Airy envelope, steeper for the defocused Huygens PSFs – so it can be fit from the outer annulus of the stamp itself and evaluated directly on detector pixels, out to a per-star radius where it drops below the noise floor.

These fits describe the diffraction wing only. The measured scattered-light halo lives in wcc_sim.scatter and is added to this diffraction wing by CombinedWing rather than replacing it – the two are physically separate terms of the same PSF, and beyond ~1000 px the scatter halo is the larger of them by an order of magnitude.

class wcc_sim.wings.WingModel(c, alpha, r_in)[source]

Bases: object

Azimuthally averaged power-law wing: profile(r) = c * r**alpha.

profile is the PSF fraction per detector pixel at radius r (px). Requires alpha < -2 so the total wing energy converges.

Parameters:
c: float
alpha: float
r_in: float
profile(r)[source]
r_out(flux_e, floor_e)[source]

Radius (px) where flux_e * profile(r) falls to floor_e.

energy_beyond(r)[source]

PSF fraction integrated over the plane outside radius r.

flux_norm(r_stamp)[source]

Divide star flux by this so stamp + drawn wing integrate to 1.

The rendered stamp is normalized to sum 1, so it already claims all the energy; the wing drawn beyond it is extra, and this divides it back out.

wcc_sim.wings.fit_wing_model(stamp, r_fit=(0.65, 0.98))[source]

Fit a WingModel to the outer annulus of a detector-sampled PSF stamp.

r_fit bounds the fit annulus as fractions of the stamp half-width; the default range sits outside the PSF core for all three focus modes while staying clear of the stamp corners.

class wcc_sim.wings.CombinedWing(core, halo)[source]

Bases: object

Diffraction wing plus the measured scattered-light halo.

The total PSF is (1 - f_scat) * core + f_scat * halo (the convention of wcc_etc.scatter_psf.make_total_psf), so beyond the stamp the two terms simply add: the core’s fitted power law scaled down by the scattered fraction, plus the halo at its absolute FRED brightness.

Exposes the same interface as WingModel, so wcc_sim.render.add_star() needs to know nothing about scatter.

Parameters:
core: WingModel
halo: object
property core_scale

1 - the scattered fraction.

Type:

Weight on the diffraction term

property r_in
profile(r)[source]
energy_beyond(r)[source]
flux_norm(r_stamp)[source]

Divide star flux by this so the drawn PSF carries the right energy.

The scattered light falling outside the halo’s modelled disc lands off the focal plane entirely, so it is neither drawn nor folded back into the star – the target is 1 - lost, not 1.

r_out(flux_e, floor_e)[source]

Radius where the summed profile falls to floor_e.

Inverted on a log grid rather than analytically: the sum of a power law and a tabulated profile has no closed form, and this is evaluated once per star. The crossing is then interpolated between the bracketing grid points – returning the grid point itself would clip the halo a fraction of a grid step early, which is a thin annulus of real flux at exactly the floor.