Skip to content

Commit b6a2a22

Browse files
author
George
committed
Initial commit
1 parent 08fd8ef commit b6a2a22

27 files changed

+2002727
-0
lines changed

LICENCE.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2025, SIMply developers
2+
All rights reserved
3+
4+
Subject to full compliance with conditions 1-3 listed below, this licence permits the redistribution and use of SIMply
5+
in source or binary forms, with or without modification.
6+
7+
- 1: The above copyright notice, this list of conditions, and the following disclaimer shall be included in all
8+
copies or substantial portions of the software.
9+
10+
- 2: Neither the software name, the names of its developers nor the names of any contributors may be used to
11+
endorse or promote products derived from this software without specific prior written permission.
12+
13+
- 3: The software shall not be used for military applications.
14+
15+
DISCLAIMER:
16+
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides
17+
its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
18+
including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS
19+
FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing
20+
the Work and assume any risks associated with Your exercise of permissions under this License.

__init__.py

Whitespace-only changes.

cameras/__init__.py

Whitespace-only changes.

cameras/cameras.py

Lines changed: 1024 additions & 0 deletions
Large diffs are not rendered by default.

cameras/simple_funcs.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# copyright (c) 2025, SIMply developers
2+
# this file is part of the SIMply package, see LICENCE.txt for licence.
3+
"""Module containing simple functions for performing basic calculations and modelling of camera performance"""
4+
5+
import numpy as np
6+
from coremaths import math2
7+
from radiometry import radiometry as rd
8+
9+
10+
def diffractionLimit(d, w):
11+
"""The diffraction limit of an optical camera of given aperture diameter when observing the given wavelength
12+
(calculated according to the Rayleigh criterion).
13+
14+
:param d: aperture (entrance pupil) diameter of the camera [m]
15+
:param w: wavelength of observation [nm]
16+
:return: diffraction limit [radians]
17+
"""
18+
return np.arcsin(1.22 * w * 1e-9 / d)
19+
20+
21+
def diffractionLimitingAperture(a, w):
22+
"""Calculates the entrance pupil diameter associated with the given diffraction-limited angular resolution and
23+
observation wavelength.
24+
25+
:param a: diffraction-limited angular resolution [radians]
26+
:param w: observation wavelength [nm]
27+
:return: entrance pupil diameter [m]
28+
"""
29+
return 1.22 * w * 1e-9 / np.sin(a)
30+
31+
32+
def pixelSignalToSNR(signal, bg, dark, read):
33+
""" Gives the signal-to-noise ratio (SNR) in a pixel's measurement
34+
35+
:param signal: the electron count due to true signal [e-]
36+
:param bg: the electron count due to background noise [e-]
37+
:param dark: the electron count due to dark current [e-]
38+
:param read: the rms read noise [e-]
39+
:return: the SNR of the pixel's measurement
40+
"""
41+
return signal / (signal + bg + dark + read ** 2) ** 0.5
42+
43+
44+
def pixelSignalFromSNR(snr, bg, dark, read):
45+
""" The number of electrons due to true signal counted by a pixel to achieve a given signal-to-noise ratio (SNR)
46+
47+
:param snr: the desired SNR
48+
:param bg: the electron count due to background noise [e-]
49+
:param dark: the electron count due to dark current [e-]
50+
:param read: the rms read noise [e-]
51+
:return: the electron count due to true signal [e-]
52+
"""
53+
_a = 1
54+
_b = -(snr ** 2)
55+
_c = -((snr ** 2) * ((bg + dark) + (read ** 2)))
56+
return math2.quadSolve(_a, _b, _c)[0]
57+
58+
59+
def fluxToElectronCount(flux, t, epd, tr, qe, w):
60+
""" Calculates the number of signal electrons counted due to a given at-aperture flux
61+
62+
:param flux: the observed flux [W m^-2]
63+
:param t: exposure time [s]
64+
:param epd: entrance pupil diameter [m]
65+
:param tr: system optical transmission
66+
:param qe: detector quantum efficiency
67+
:param w: the effective wavelength of the measurement [nm]
68+
:return: number of signal electrons measured due to flux
69+
"""
70+
a = 0.25 * 3.14159 * epd ** 2 # entrance pupil area [m^2]
71+
energy = flux * a * tr * t # total energy incident on pixel due to flux [J]
72+
n_photons = energy / rd.photonEnergy(w) # total number of photons incident on pixel due to flux
73+
return n_photons * qe
74+
75+
76+
def fluxFromElectronCount(ne, t, epd, tr, qe, w):
77+
""" Calculates the at-aperture flux required to result in a given number of electrons in a pixel
78+
79+
:param ne: number of electrons
80+
:param t: exposure time [s]
81+
:param epd: entrance pupil diameter [m]
82+
:param tr: system optical transmission
83+
:param qe: detector quantum efficiency
84+
:param w: the effective wavelength of the measurement [nm]
85+
:return: the observed flux [W m^-2]
86+
"""
87+
a = 0.25 * 3.14159 * epd ** 2 # entrance pupil area [m^2]
88+
n_photons = ne / qe # number of photons incident on pixel due to flux
89+
energy = n_photons * rd.photonEnergy(w) # total energy incident on pixel due to flux [J]
90+
return energy / a / tr / t
91+
92+
93+
def radianceToElectronCount(radiance, t, epd, ifov, tr, qe, w):
94+
""" Calculates the number of signal electrons counted due to a given observed radiance (assumed uniform over pixel)
95+
96+
:param radiance: the observed radiance (assumed uniform over pixel) [W m^-2 sr^-1]
97+
:param t: exposure time [s]
98+
:param epd: entrance pupil diameter [m]
99+
:param ifov: the pixel's IFOV [rad]
100+
:param tr: system optical transmission
101+
:param qe: detector quantum efficiency
102+
:param w: the effective wavelength of the measurement [nm]
103+
:return: number of signal electrons measured due to radiance
104+
"""
105+
flux = radiance * ifov ** 2
106+
return fluxToElectronCount(flux, t, epd, tr, qe, w)
107+
108+
109+
def radianceFromElectronCount(ne, t, epd, ifov, tr, qe, w):
110+
""" Calculates the observed radiance required to result in a given number of electrons in a pixel
111+
112+
:param ne: number of electrons
113+
:param t: exposure time [s]
114+
:param epd: entrance pupil diameter [m]
115+
:param ifov: pixel IFOV [rad]
116+
:param tr: system optical transmission
117+
:param qe: detector quantum efficiency
118+
:param w: the effective wavelength of the measurement [nm]
119+
:return: the observed radiance [W m^-2 sr^-1]
120+
"""
121+
a = 0.25 * 3.14159 * epd ** 2 # entrance pupil area [m^2]
122+
omega = ifov ** 2 # solid angle viewed by pixel [str]
123+
n_photons = ne / qe # number of photons incident on pixel due to flux
124+
energy = n_photons * rd.photonEnergy(w) # total energy incident on pixel due to flux [J]
125+
return energy / a / omega / tr / t
126+
127+
128+
def fluxToSNR(flux, t, epd, ifov, tr, qe, w, bgrad, jd, nr, dwell=None):
129+
""" Calculates the SNR of a pixel's measurement as a function of observed flux and noise sources
130+
131+
:param flux: The flux arriving at the instrument's aperture due to signal [W m^-2]
132+
:param t: exposure time [s]
133+
:param epd: entrance pupil diameter [m]
134+
:param ifov: pixel IFOV [rad]
135+
:param tr: system optical transmission
136+
:param qe: detector quantum efficiency
137+
:param w: the effective wavelength of the measurement [nm]
138+
:param bgrad: total radiance of background sources [W m^-2 sr^-1]
139+
:param jd: detector dark current [e- s^-1]
140+
:param nr: read noise RMS [e-]
141+
:param dwell: dwell time of flux source [s]. If None is passed, tDwell is set equal to t
142+
:return: SNR of measurement
143+
"""
144+
if dwell is None:
145+
dwell = t
146+
sigcount = fluxToElectronCount(flux, dwell, epd, tr, qe, w)
147+
bgcount = radianceToElectronCount(bgrad, t, epd, ifov, tr, qe, w)
148+
return pixelSignalToSNR(sigcount, bgcount, jd * t, nr)
149+
150+
151+
def fluxFromSNR(snr, t, epd, ifov, tr, qe, w, bgrad, jd, nr, dwell=None):
152+
""" Calculates the at-aperture flux required to achieve a given SNR in a pixel's measurement
153+
154+
:param snr: the pixel's SNR
155+
:param t: exposure time [s]
156+
:param epd: entrance pupil diameter [m]
157+
:param ifov: pixel IFOV [rad]
158+
:param tr: system optical transmission
159+
:param qe: detector quantum efficiency
160+
:param w: the effective wavelength of the measurement [nm]
161+
:param bgrad: total radiance of background sources [W m^-2 sr^-1]
162+
:param jd: detector dark current [e- s^-1]
163+
:param nr: read noise RMS [e-]
164+
:param dwell: dwell time of flux source [s]. If None is passed, tDwell is set equal to t
165+
:return: the at-aperture flux observed by the pixel [W m^-2]
166+
"""
167+
if dwell is None:
168+
dwell = t
169+
bgcount = radianceToElectronCount(bgrad, t, epd, ifov, tr, qe, w)
170+
sigcount = pixelSignalFromSNR(snr, bgcount, jd * t, nr)
171+
return fluxFromElectronCount(sigcount, dwell, epd, tr, qe, w)
172+
173+
174+
def radianceToSNR(radiance, t, epd, ifov, tr, qe, w, bgrad, jd, nr, dwell=None):
175+
""" Calculates the SNR of a pixel's measurement as a function of observed flux and noise sources
176+
177+
:param radiance: The radiance observed by the pixel (assumed uniform over whole pixel) [W m^-2 sr^-1]
178+
:param t: exposure time [s]
179+
:param epd: entrance pupil diameter [m]
180+
:param ifov: pixel IFOV [rad]
181+
:param tr: system optical transmission
182+
:param qe: detector quantum efficiency
183+
:param w: the effective wavelength of the measurement [nm]
184+
:param bgrad: total radiance of background sources [W m^-2 sr^-1]
185+
:param jd: detector dark current [e- s^-1]
186+
:param nr: read noise RMS [e-]
187+
:param dwell: dwell time of radiance [s]. If None is passed, tDwell is set equal to t
188+
:return: SNR of measurement
189+
"""
190+
if dwell is None:
191+
dwell = t
192+
sigcount = radianceToElectronCount(radiance, dwell, epd, ifov, tr, qe, w)
193+
bgcount = radianceToElectronCount(bgrad, t, epd, ifov, tr, qe, w)
194+
return pixelSignalToSNR(sigcount, bgcount, jd * t, nr)
195+
196+
197+
def radianceFromSNR(snr, t, epd, ifov, tr, qe, w, bgrad, jd, nr, dwell=None):
198+
""" Calculates the observed radiance required to achieve a given SNR in a pixel's measurement
199+
200+
:param snr: the pixel's SNR
201+
:param t: exposure time [s]
202+
:param epd: entrance pupil diameter [m]
203+
:param ifov: pixel IFOV [rad]
204+
:param tr: system optical transmission
205+
:param qe: detector quantum efficiency
206+
:param w: the effective wavelength of the measurement [nm]
207+
:param bgrad: total radiance of background sources [W m^-2 sr^-1]
208+
:param jd: detector dark current [e- s^-1]
209+
:param nr: read noise RMS [e-]
210+
:param dwell: dwell time of flux source [s]. If None is passed, tDwell is set equal to t
211+
:return: the observed radiance [W m^-2 sr^-1]
212+
"""
213+
if dwell is None:
214+
dwell = t
215+
bgcount = radianceToElectronCount(bgrad, t, epd, ifov, tr, qe, w)
216+
sigcount = pixelSignalFromSNR(snr, bgcount, jd * t, nr)
217+
return radianceFromElectronCount(sigcount, dwell, epd, ifov, tr, qe, w)
3.81 MB
Binary file not shown.

0 commit comments

Comments
 (0)