Repository navigation
2.2.0
[2.2.0] - 2026-08-24
Added
-
CameraConfig.read_noise_correlated_fraction. The part of read noise that
is common to every read of a nondestructive ramp, and so cancels when two
reads are differenced. Without it every read was an independent draw and
correlated double sampling could only ever cost a factor ofsqrt(2)— but a
real CDS frame is often quieter than a single read, because reference-level
drift, bias settling, and 1/f noise persist between the two reads and
differencing removes them. Defaults to0, which is the previous behaviour.
Only a differenced measurement can constrain it; single-read data cannot. -
Vega-system J, H, and Ks bands.
Bandpass.johnsonpreviously stopped at
I, so any near-infrared work had to leave the Vega system for AB — a
different magnitude for the same star, by +0.89 (J), +1.37 (H), and +1.84
(Ks). The zero points are derived from the 2MASS absolute calibration (Cohen,
Wheaton & Megeath 2003) through the same integral the AB path uses, so the
two systems now differ only in the reference spectrum and not in the
convention.SpectralBandpass.johnsongained the matching tophats, which are
the same shapes the AB survey table already used: a filter's shape is a
property of the filter, not of the magnitude system. -
Correlated double sampling as a first-class readout mode.
Camera.correlated_double_sampleresets, reads the pedestal, integrates, reads
again, and returns the signedint32ADU difference — the frame a camera in
CDS mode actually delivers. It shares one reset-correlated readout core with
nondestructive_series, so kTC noise and fixed bias structure cancel, read
noise grows bysqrt(2), common mode is partly removed through its AR(1)
correlation, and reset settling leaves its pedestal-to-signal residual. The
interval-proportional bias rate survives differencing by construction and is
documented as a dark-subtractable pedestal.pedestal_interval_smodels a
finite reset-to-read delay.correlated_double_sample_spectralis the
wavelength-resolved twin, standing to it asexpose_spectraldoes to
expose; both spectral entry points now share one QE-folding step, so a cube
cannot have QE applied twice or applied differently per readout mode. -
Global-reset nondestructive read series for eAPD arrays.
Camera.nondestructive_seriesanddark_nondestructive_seriesaccumulate
Poisson charge between resets, preserve a ramp's kTC realization, redraw read
noise on every sample, and expose ramp/read timing in frame metadata. -
A measured C-RED One preset and hybrid-array readout structure. The new
first_light_imaging_cred_onepreset separates the complete camera from the
bareleonardo_saphiradetector and models its 32 interleaved output channels,
edge-dependent pedestal/noise, stable per-channel offsets and noise scales, and
correlated frame-wide readout pedestal. Its spatial and temporal dark-frame
parameters are constrained by the July 2026 CRED1 dark stack. -
Reset-aware stack characterization.
nondestructive_stack_statistics
measures strong-reset cadence, ramp slopes, common mode, CDS/pixel noise,
interleaved channels, and edge structure without treating correlated reads as
independent exposures.ramp_photon_transferfits conversion gain and
split-ramp response nonuniformity from repeated NDR ramps. -
examples/16_detector_showcase.pyrenders an animated WebP that exposes one
incident photon field on a CCD, EMCCD, sCMOS, and eAPD side by side, each panel
overlaid with the frame rate that camera sustained on the running machine. The
clip is the README header image.
Changed
-
Refined the measured C-RED One response across gain and read rate. Fixed
avalanche-gain nonuniformity, sublinear gain noise, NDR read-interval scaling,
observed 42/3/5-read electronic cadences, reset settling, and separate broad
side/narrow row-edge pedestal terms now reproduce the local operating-point
stacks. The C-RED One and SAPHIRA QE curves were corrected to the manufacturer
plot: 60% through 950 nm, linear to 80% at 1450 nm, then 80% to 2500 nm. -
The README follows the documentation-first structure used across the sibling
projects: docs link, showcase clip, install, quickstart, benchmarks, then the
feature list. The preset table and the dark-frame model walkthrough now live
only in the presets and noise-model guides, which they already duplicated. -
Refreshed
benchmarks/device-results.{json,md}on the RTX 5090 / Ryzen 9
9950X3D reference machine against getframes 2.1.1, NumPy 2.2.6, and CuPy 14.1.1.
Fixed
-
Benchmark provenance now records the CuPy version when CuPy is installed
under a CUDA-specific wheel name (cupy-cuda12x/cupy-cuda11x). The metadata
block previously reportedcupy: nullon exactly the machines that had
produced the GPU column. -
Persistent cameras now cache the dark-signal expectation. Repeated exposures
at the same exposure time, temperature, precision, and detector configuration
reuse the full-detector DSNU/hot-pixel/glow map; changing either physical key
rebuilds it. Seeded stochastic frames and detector truth remain unchanged. -
sCMOS per-pixel read noise is now a fixed property of the sensor. The
per-pixel read-noise RMS map implied byread_noise_nonuniformitywas drawn from
the per-frame generator, so it was re-randomised in every frame. Single-frame
spatial statistics were unaffected, but every pixel ended up with the same
expected noise through time, which is not how an sCMOS behaves: each pixel has
its own source-follower and column ADC. The map is now built once from
fixed_pattern_seedand cached inFixedPatternMaps, alongside PRNU and DSNU.
Verified against dark stacks from three real back-illuminated sCMOS cameras
(KURO 1200B, Prime 95B, Marana 4.2B-11): splitting a stack in half and
correlating the two per-pixel temporal-variance maps gives r = 0.89–0.94 on the
real detectors and r = 0.004 with the old model, now r ≈ 0.96.
This changes generated pixel values for any configuration with
read_noise_nonuniformity > 0;read_noise_eand the spatial statistics of a
single frame are unchanged.
Added
DetectorWorkspaceplus optionalworkspace=/ caller-ownedout=execution
on scalar and spectral camera exposures. Full-detector ROI inputs, private
photo/total scratch, and digitised destinations can now be reused without
allowing returned truth or default frames to alias mutable scratch. The
workspace is device/shape/precision-bound and rejects concurrent use.CameraConfig.charge_diffusion_fwhm_px,charge_diffusion_kernel(), and
apply_charge_diffusion()make lateral detector charge spreading available to
focal-plane simulators at their own oversampling. The OCAM2K preset declares
its measured 0.37-pixel FWHM; under-resolved kernels fail explicitly, while
native-resolution camera frames state in metadata that diffusion was not
applied rather than silently becoming a numerical no-op.- Full-detector region-of-interest simulation through
CameraConfig.roi=(left, top, width, height). Cameras accept and return
ROI-shaped arrays while evaluating detector physics and fixed patterns on the
native sensor before cropping.Camera.sensor_resolution,
CameraConfig.output_resolution, and active amplifier-boundary properties make
the full-versus-ROI geometry explicit. Exact full-detector split pixels remain
available when an ROI is active. getframes.analysis.characterize: detector characterisation from frame
stacks. Wherephoton_transfer_curvedrives a simulated camera, this works
on stacks that already exist -- raw data off a real detector, or simulated
frames.stack_statisticsreduces any iterable of frames (arrays,Frames, a
dark_seriesgenerator, your own file reader) to per-pixel temporal mean and
variance in one streaming pass, so stacks larger than memory are fine.
characterize_darkthen measures conversion gain, read noise (with its
per-pixel map, log-normal width and RTS tail), dark current, bias and DSNU from
darks alone -- no flat field needed, because dark charge is Poisson and so
serves as the PTC charge source.characterize_flatadds full well, PRNU and
linearity.DarkCharacterization.to_config()returns aCameraConfig, closing
the loop: measure a real camera, then simulate it.StackStats.split=True
additionally givestemporal_repeatability, the split-half test that separates
genuine per-pixel noise structure from chi-squared sampling scatter.
New guide (docs/guides/characterization.md) and example
(examples/15_detector_characterization.py).read_noise_rts_fraction/read_noise_rts_factor: an optional second,
noisier read-noise population modelling the random-telegraph-signal (RTS) pixels
of a real sCMOS array. Measured on three real sensors, ~0.5% of pixels sit above
3x the median read noise where a single log-normal predicts ~0.01%; these are the
pixels that limit faint-source detection. Defaults to off.detector_glow_edge_scale_px: makesdetector_glow_e_per_sedge-concentrated
with an exponential falloff, instead of uniform, modelling amplifier glow emitted
at the array periphery. Renormalised so the array mean is unchanged; still fixed
and exposure-scaling, so an exposure-matched master dark removes it. Defaults to
0(uniform, the previous behaviour).
Changed
-
Float32 cameras now cache amplifier gain/offset and structured-bias maps in
float32 instead of retaining detector-sized float64 coefficients. The float64
reference path is unchanged. A matched 2048x2048 structured-digitization
benchmark improved by 1.328x on the local CPU and 1.382x on a Quadro P620 while
persistent coefficient storage fell from 96 MiB to 48 MiB. -
The
princeton_instruments_kuro_1200b,photometrics_prime_95b, and
andor_marana_4_2b_11presets now carry measured conversion gain, read noise,
dark current, bias offset, and non-uniformity terms, fitted from a per-pixel dark
photon-transfer analysis of real frames rather than taken from datasheets. The
largest corrections: conversion gain (1.25-1.3 -> 0.77-0.87 e-/ADU, the low-signal
leg of these dual-gain modes) anddark_current_nonuniformity(0.03 -> 0.11-0.33,
which had been roughly an order of magnitude too low). Each preset documents the
operating mode and temperature the values apply to. -
dark_current_nonuniformityraised to0.23on the remaining sCMOS presets
(generic_scmos,hamamatsu_orca_fusion,hamamatsu_orca_quest_2,
tucsen_aries_6504_pro,andor_cb1_0_5mp), which previously carried 0.02-0.03 or
omitted the field entirely.0.23is the median of the three cameras measured
against real dark stacks (0.11, 0.23, 0.33); each preset documents that it is a
realistic default carried over from characterised hardware rather than a figure
from that camera's datasheet. The same four conventional sCMOS presets also gain
the measured RTS population (read_noise_rts_fraction = 0.016, factor 2.65), and
andor_cb1_0_5mp/hamamatsu_orca_quest_2gain aread_noise_nonuniformityof
0.2 where they previously had none at all.hamamatsu_orca_quest_2deliberately
keeps no RTS population --- photon-number resolution depends on a tightly screened
read-noise distribution, and importing a tail measured on conventional 11 um sCMOS
would misrepresent it. -
andor_marana_4_2b_11gains its measured hot-pixel population
(hot_pixel_fraction = 1e-4above 10x the median dark rate). -
docs/guides/validation.mddocuments how to validate a preset against a real dark
stack: measuring conversion gain from darks alone (no flats needed), and the
split-half test for repeatable per-pixel read noise. -
examples/16_detector_showcase.pynow shows each detector in the regime it is
built for rather than putting all four on one shared V-band field at a shared
200 ms exposure. The old framing was a tidy controlled comparison but an unfair
showcase: onceleonardo_saphiracarried a realistic near-infrared
dark-plus-background ceiling, its panel was dark-noise dominated in a visible
200 ms exposure (star/noise 0.93 against 7.4-22.6 for the other three), and no
exposure time fixes that, because amplified dark noise grows assqrt(t)while
the star signal grows ast. The panels are now a 5 s deep-sky CCD field, a
500 Hz EMCCD wavefront sensor, 100 ms wide-field sCMOS, and the C-RED One eAPD
doing H-band AO in correlated double sampling at its 1750 Hz maximum frame
rate — which the clip measures at ~1,789 frames/s on an RTX 5090. The clip is
the README header image and exercises this release's CDS path, C-RED One preset,
and Vega H band.
Fixed
- The detector showcase no longer scales a panel to its own hot pixels. The
clip took its per-panel display maximum at a hardcoded 99.95th percentile, which
is exactly thehot_pixel_fractionthatleonardo_saphiradeclares, so the
display range was pinned to a defect population sitting near 20,000 ADU behind
the avalanche gain stage and every physical feature crushed to black. The
percentile is now taken from below the preset's declared defect fraction. - The detector showcase subtracts a measured dark pedestal rather than the bias
offset alone. Bias is not the zero point once a detector carries real dark
current through a gain stage — 80 e-/s at 50x avalanche gain is ~400 ADU per
200 ms frame — and a CDS frame sits on its own exposure-dependent bias-rate
pedestal rather than on the bias offset at all. - Declared the license as a PEP 639 SPDX expression (
license = "MIT"plus
license-files) instead of the deprecatedlicense = { text = "MIT" }table,
and dropped the now-redundantLicense ::classifier. The built distribution
carriesLicense-Expression: MITandLicense-File: LICENSE. No change to the
license itself.