Signal-to-noise and exposure time

wcc-etc offers two SNR paths that agree to ~1% for an in-focus point source, plus their exposure-time inverses.

Image-based SNR (the default)

get_snr() delegates to get_image_snr(). It renders the PSF on the detector grid, performs circular-aperture photometry, and returns a dictionary:

r = sim.get_snr(time=60)
r["snr"]                 # signal-to-noise ratio
r["signal_e"]            # source electrons inside the aperture
r["noise_e"]             # total noise (electrons)
r["enclosed_fraction"]   # PSF fraction inside the aperture
r["r_aper_mas"]          # aperture radius (mas)
r["n_pix"]               # number of pixels in the aperture
r["n_saturated"]         # pixels at/above full well in the per-frame image
r["saturated"]           # bool

Important

The return value is a dict — always index ["snr"]. (Earlier versions returned a Quantity with .value; that is gone.)

Choosing the aperture

Aperture precedence is optimize > r_aper_mas > ee_frac; if none is given, the Simulation’s stored r_aper_mas is used.

sim.get_snr(60, r_aper_mas=300)          # fixed aperture radius (mas)
sim.get_snr(60, ee_frac=0.8)             # aperture enclosing 80% of the PSF
sim.get_snr(60, optimize=True)           # radius that maximizes SNR

Sweeping time or magnitude

time may be a scalar or an array. get_image_snr additionally accepts mags to sweep the source brightness off a single cached render (host, background, dark, and read noise held fixed). time and mags may not both be arrays.

import numpy as np
sim.get_snr(time=np.array([10, 30, 60, 120]))["snr"]      # array of SNR
sim.get_image_snr(time=60, mags=np.arange(16, 24))["snr"] # SNR vs. magnitude

Specifying the PSF and jitter

Pass any PSFSource; override jitter per call:

from wcc_etc import DefocusPSF, DEFOCUS_2WAVE_PATH
sim.get_image_snr(time=60, psf=DefocusPSF(DEFOCUS_2WAVE_PATH))
sim.get_image_snr(time=60, jitter_sigma_mas=15)

See PSFs and image simulation for the PSF models and the npix / oversample grid parameters.

Exposure time for a target SNR

get_image_exptime_for_snr() inverts the image-based SNR; get_exptime_for_snr() is the analytic Airy inverse that pairs with get_snr_airy.

sim.get_image_exptime_for_snr(snr=100)        # seconds (PSF-aware)
sim.get_exptime_for_snr(snr=100)              # seconds (analytic Airy)

Multiple reads (n_reads)

Detectors that co-add or sample multiple reads per exposure reduce the effective read noise. All SNR and exposure-time methods accept n_reads; the default comes from the sensor/meta:

sim.get_snr(60, n_reads=4)
sim.get_image_exptime_for_snr(snr=100, n_reads=4)

The analytic Airy SNR

get_snr_airy() is the original closed-form Airy-disk approximation. It is retained for cross-checking the image-based path and emits a DeprecationWarning. To use it quietly:

import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore", DeprecationWarning)
    snr_airy = sim.get_snr_airy(60)

For an in-focus AiryPSF with the default aperture, the image-based and analytic results agree to about 1%.