Equations
Every numbered equation of Miller et al. (2017), in the form this package
implements: the 26 February 2020 erratum for Eqs. 7, 21, 22, 24 and 25, and the
three prose-based corrections argued out in Deviations, each
flagged below.
Brightness temperatures are written \(T_{\lambda}\) for the band centred near
\(\lambda\) µm; \(\theta\) is the solar zenith angle.
The normalization primitive
Almost every test is a clipped linear ramp between a MIN and a MAX bound
(Eq. 3), which is where the tuning constants enter:
\[
N(x; x_{\min}, x_{\max}) = \operatorname{clip}\!\left(
\frac{x - x_{\min}}{x_{\max} - x_{\min}},\; 0,\; 1 \right)
\]
MIN may exceed MAX, which reverses the ramp; the cos-zenith blends
below rely on that. → shachen.norm.normalize()
Clear-sky background (§3.2)
The dynamic background separates DEBRA from a fixed-threshold test. It is
estimated by one of two schemes.
Scheme A, semi-analytic. Surface emissivity \(\varepsilon_\lambda\) modifies
the Planck radiance of the reanalysis skin temperature, inverted back to a
brightness temperature:
\[
T^{\text{bg}}_{\lambda} = B^{-1}\!\left(\lambda,\; \varepsilon_\lambda \,
B(\lambda, T_{\text{skin}})\right),
\qquad
B(\lambda, T) = \frac{c_1 / \lambda^{5}}{\exp\!\left(c_2 / \lambda T\right) - 1}
\]
with \(c_1 = 2hc^2\) and \(c_2 = hc/k\). Missing emissivity (ocean) is treated as
\(\varepsilon = 1\), which makes the transform the identity.
→ shachen.background.background_signals()
Scheme B, cloud-cleared composite. Over a stack of \(n\) same-time-of-day
scenes, take the warmest window pixel and read all three bands from that same
day \(d^*\). Clouds are cold, so the warmest day is taken as the clear-sky
estimate:
\[
d^{*} = \operatorname*{arg\,max}_{d \in \mathcal{C}} \; T_{10.4}^{(d)},
\qquad
T^{\text{bg}}_{\lambda} = T_{\lambda}^{(d^{*})}
\]
where \(\mathcal{C}\) is the set of days finite in all three bands at that pixel.
→ shachen.composite.composite_background()
Either way, the two background signals fed to the dust tests are
\[
\mathrm{RSW}_{\text{bg}} = T^{\text{bg}}_{12.3} - T^{\text{bg}}_{10.4},
\qquad
\mathrm{BTD}_{\text{bg}} = T^{\text{bg}}_{8.6} - T^{\text{bg}}_{10.4}
\]
Cloud mask (Eqs. 1–12)
Four continuous cloud tests, damped by two dust-restoral terms so that dust is
not masked away as cloud. → shachen.cloudmask.cloud_mask()
\begin{align*}
\mathrm{CM1} &= 1 - N(T_{10.4};\; T_{\text{skin}} - 50,\; T_{\text{skin}})
& &\text{(Eqs. 1–2, cold relative to skin)} \\
\mathrm{CM2} &= 1 - N(T_{10.4} - T_{6.2};\; 0,\; 25)
& &\text{(Eq. 4, deep convection)}^{\dagger} \\
\mathrm{CM3} &= N(T_{10.4} - T_{12.3};\; 2.0,\; 4.5)
& &\text{(Eq. 5, thin cirrus, day + night)} \\
\mathrm{CM4} &= N(T_{3.9} - T_{10.4};\; 5.0,\; 8.0)
& &\text{(Eq. 6, thin cirrus, night only)}
\end{align*}
The restoral terms: a pixel that looks like dust in the reverse split window
subtracts from the cloud confidence.
\begin{align*}
R_1 &= N(T_{12.3} - T_{10.4};\; 0,\; 3.5)\,(1 - \mathrm{CM1})
& &\text{(Eq. 7, erratum)} \\
R_2^{\text{day}} &= N(T_{8.6} - T_{10.4};\; -1,\; 3)\,
(1 - \mathrm{CM2})(1 - \mathrm{CM3}) & &\text{(Eq. 8)} \\
R_2^{\text{ngt}} &= N(T_{8.6} - T_{10.4};\; -1,\; 3)\,
(1 - \mathrm{CM2})(1 - \mathrm{CM4}) & &\text{(Eq. 9)}
\end{align*}
Combined, then put on a common scale:
\begin{align*}
\mathrm{CM}^{\text{ngt}} &= (\mathrm{CM1} + \mathrm{CM2} + \mathrm{CM4})
\left(1 - \max(R_1, R_2^{\text{ngt}})\right) & &\text{(Eq. 10)} \\
\mathrm{CM}^{\text{day}} &= (\mathrm{CM1} + \mathrm{CM2} + \mathrm{CM3})
\left(1 - \max(R_1, R_2^{\text{day}})\right) & &\text{(Eq. 11)}^{\ddagger} \\
\mathrm{CM}_{\text{norm}} &= N(\mathrm{CM};\; 0.45,\; 0.80) & &\text{(Eq. 12)}
\end{align*}
\(^\dagger\) Eq. 4 is implemented magnitude-reversed; as printed it saturates the
mask over clear sky. \(^\ddagger\) Eq. 11 uses CM3 in place of the misprinted
CM4: the 3.9 µm test is night-only. Both are argued in
Deviations.
Dust tests (Eqs. 13–15)
DT1 and DT2 are the same ramp as Eq. 3 with the per-pixel background as the
MIN bound; that substitution is what makes the test dynamic. Writing the
observed signals \(\mathrm{RSW} = T_{12.3} - T_{10.4}\) and
\(\mathrm{BTD} = T_{8.6} - T_{10.4}\):
\begin{align*}
\mathrm{DT1} &= \operatorname{clip}\!\left(
\frac{\mathrm{RSW} - \mathrm{RSW}_{\text{bg}}}
{3.5 - \mathrm{RSW}_{\text{bg}}},\; 0,\; 1 \right) & &\text{(Eq. 13)} \\
\mathrm{DT2} &= \operatorname{clip}\!\left(
\frac{\mathrm{BTD} - \mathrm{BTD}_{\text{bg}}}
{3.0 - \mathrm{BTD}_{\text{bg}}},\; 0,\; 1 \right) & &\text{(Eq. 14)}
\end{align*}
DT3 is the thermal-contrast test, implemented magnitude-reversed per the
paper’s prose (“observations that are relatively cold compared to MERRA
produce high value for DT3”):
\[
\mathrm{DT3} = \operatorname{clip}\!\left(
\frac{(T_{\text{MERRA}} - S) - T_{10.4}}{50},\; 0,\; 1 \right),
\qquad
S = \begin{cases} -10\ \mathrm{K} & \text{land} \\ +5\ \mathrm{K} & \text{ocean} \end{cases}
\]
→ shachen.dust_tests.dust_tests()
Confidence factor (Eqs. 16–22)
Three illumination regimes, each suppressed by the matching cloud mask. Night
drops to \(\max(\mathrm{DT1}, \mathrm{DT2})\) because the two split-window tests
stop being independent without solar heating.
→ shachen.confidence.confidence()
\begin{align*}
\mathrm{CF}^{*}_{\text{day}} &= (\mathrm{DT1} + \mathrm{DT2} + \mathrm{DT3})
\left(1 - \mathrm{CM}^{\text{day}}_{\text{norm}}\right) & &\text{(Eq. 16)} \\
\mathrm{CF}^{*}_{\text{trm}} &= (\mathrm{DT1} + \mathrm{DT2} + \tfrac{1}{2}\mathrm{DT3})
\left(1 - \mathrm{CM}^{\text{day}}_{\text{norm}}\right) & &\text{(Eq. 17)} \\
\mathrm{CF}^{*}_{\text{ngt}} &= \left(\max(\mathrm{DT1}, \mathrm{DT2})
+ \tfrac{1}{2}\mathrm{DT3}\right)
\left(1 - \mathrm{CM}^{\text{ngt}}_{\text{norm}}\right) & &\text{(Eq. 18)} \\
\mathrm{CF}_{\text{day}} &= N(\mathrm{CF}^{*}_{\text{day}};\; 0.25,\; 2.50)
& &\text{(Eq. 19)}^{\dagger} \\
\mathrm{CF}_{\text{ngt}} &= N(\mathrm{CF}^{*}_{\text{ngt}};\; 0.125,\; 1.25)
& &\text{(Eq. 19)}^{\dagger} \\
\mathrm{CF}_{\text{trm}} &= N\!\left(\mathrm{CF}^{*}_{\text{trm}};\;
B_{\text{ngt}}^{\text{trm}}\,(0.25,\,2.50)
+ \left(1 - B_{\text{ngt}}^{\text{trm}}\right)(0.125,\,1.25)\right)
& &\text{(Eq. 19)}^{\dagger}
\end{align*}
\(\dagger\) Eq. 19 is printed with one interval, \((0.25,\,2.50)\), for all three
branches. It is split per branch here because Eqs. 16 and 18 have different
raw ceilings (3.0 and 1.5), which otherwise caps \(\mathrm{CF}_{\text{ngt}}\) at
0.556 — see Deviations. Pass
ConfidenceConstants(cf_norm=Bounds(0.25, 2.50)) to restore the printed form.
The terminator is crossed smoothly, with weights evaluated in cosine-zenith
space:
\begin{align*}
B_{\text{ngt}}^{\text{trm}} &= N(\cos\theta;\; \cos 105^{\circ},\; \cos 90^{\circ})^{1.5}
& &\text{(Eq. 20)} \\
B_{\text{trm}}^{\text{day}} &= N(\cos\theta;\; \cos 90^{\circ},\; \cos 75^{\circ})^{1.5}
& &\text{(Eq. 21, erratum)}
\end{align*}
\begin{equation*}
\mathrm{CF}_{\text{comb}} = B_{\text{trm}}^{\text{day}}\,\mathrm{CF}_{\text{day}}
+ \left(1 - B_{\text{trm}}^{\text{day}}\right)
\left[ B_{\text{ngt}}^{\text{trm}}\,\mathrm{CF}_{\text{trm}}
+ \left(1 - B_{\text{ngt}}^{\text{trm}}\right)\mathrm{CF}_{\text{ngt}} \right]
\tag{Eq. 22, erratum}
\end{equation*}
\(\mathrm{CF}_{\text{comb}} \in [0, 1]\) is the algorithm’s output field.
Enhanced imagery (Eqs. 23–29)
A greyscale baseline image, blended day-to-night, then modulated by the
confidence factor in each colour gun. Domain min/max are taken over the whole
scene, skipping NaN. → shachen.imagery.debra_imagery()
\begin{align*}
\mathrm{VIS}_{\text{bg}} &= \frac{\rho - \rho_{\min}}{\rho_{\max} - \rho_{\min}}
& &\text{(Eq. 23)} \\
\mathrm{IR}_{\text{bg}} &= 1 - \frac{T_{10.4} - T_{\min}}{T_{\max} - T_{\min}}
& &\text{(Eq. 24, erratum)} \\
B_{\text{bg}} &= 1 - N(\theta;\; 79^{\circ},\; 89^{\circ})^{1.5}
& &\text{(Eq. 25, erratum)} \\
\mathrm{BI} &= B_{\text{bg}}\,\mathrm{VIS}_{\text{bg}}
+ \left(1 - B_{\text{bg}}\right)\mathrm{IR}_{\text{bg}} & &\text{(Eq. 26)}
\end{align*}
Eq. 25 is evaluated in zenith degree space, unlike the cos-space Eqs. 20–21.
Each gun then gets the same form, differing only in the coefficient \(D_G\)
applied to the confidence factor:
\[
G = N\!\left(\mathrm{BI}\left(1 - \min(\mathrm{CF}_{\text{comb}}, 0.5)\right)
+ D_G\,\mathrm{CF}_{\text{comb}};\; 0,\; 1.2\right),
\qquad G \in \{R, G, B\}
\]
The printed Eqs. 27–29 are \(D_R = D_G = 1\) and \(D_B = 0.10\), which paints dust
yellow. The paper’s §4.2 alternatives are generalised in
shachen.constants.COLOR_DIMMING:
Preset |
\((D_R, D_G, D_B)\) |
yellow (Eqs. 27–29)
|
\((1.0,\ 1.0,\ 0.10)\) |
pink
|
\((1.0,\ 0.25,\ 0.25)\) |
green
|
\((0.10,\ 1.0,\ 0.10)\) |
blue
|
\((0.25,\ 0.25,\ 1.0)\) |
The \(\min(\mathrm{CF}, 0.5)\) cap keeps dust translucent: even at full
confidence, half the underlying scene still shows through.