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.