Python Detection Statistics API¶
The doppler.detection module is the detection-theory layer over the C
detection core: closed-form relationships between probability of detection
(Pd), probability of false alarm (Pfa), SNR, and coherent dwell length for a
square-law detector. Pair it with the streaming
CorrDetector — detection tells you
what threshold and dwell to use, CorrDetector runs the detection.
Every quantity comes in two forms: an amplitude-SNR version (det_*, where
SNR is the linear signal/noise amplitude ratio) and a power-SNR version
(det_*_power, the linear power ratio = amplitude²). The two are equivalent
detectors — det_pd(s, ...) equals det_pd_power(s**2, ...).
The threshold depends only on the target false-alarm rate; Pd then depends on
the SNR and the coherent dwell. The whole chain is closed-form and stateless:
>>> from doppler.detection import det_threshold, det_pd, det_dwell, det_snr
>>> thr = det_threshold(pfa=1e-6) # threshold for Pfa = 1e-6
>>> round(thr, 4)
5.2565
>>> round(det_pd(snr=1.613, dwell=8, threshold=thr), 2)
0.9
>>> det_dwell(snr=0.5, pd_min=0.9, pfa=1e-6, max_dwell=256)
84
>>> round(det_snr(dwell=8, pd_min=0.9, pfa=1e-6), 3) # inverse of det_pd
1.613
The underlying Marcum Q-function is exposed directly — under H0 (a = 0) it is
the Rayleigh tail exp(-b²/2):
>>> from doppler.detection import marcum_q
>>> round(marcum_q(m=1, a=0.0, b=1.0), 5) # P(Rayleigh > 1) = exp(-0.5)
0.60653
>>> round(marcum_q(m=1, a=2.0, b=1.0), 5) # signal present (a = 2)
0.91811
Amplitude-SNR (dB)¶
det_threshold
¶
Threshold eta for a given false-alarm probability.
Exact closed-form inversion of Pfa = exp(-eta^2/2):
eta = sqrt(-2 * ln(pfa))
The threshold is independent of dwell and SNR; it depends only on the desired Pfa.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pfa
|
float
|
Desired false-alarm probability; must be in (0, 1). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Threshold eta > 0. |
Examples:
det_pd
¶
Detection probability for given per-sample amplitude SNR and dwell.
Computes Pd = Q_1(a, eta) where a = sqrt(2 * dwell) * snr.
At snr = 0, det_pd returns Pfa (the false-alarm rate, as expected for a noise-only input). As snr or dwell increase, Pd approaches 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr
|
float
|
Per-sample amplitude SNR (signal / noise amplitude, linear). snr = 0 gives Pd = Pfa. |
required |
dwell
|
int
|
Coherent integration depth; must be >= 1. |
required |
threshold
|
float
|
Test-stat threshold eta, e.g. from det_threshold(). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Detection probability in [0, 1]. |
Examples:
det_dwell
¶
Minimum dwell such that Pd >= pd_min for the given SNR and Pfa.
Iterates dwell = 1, 2, ..., max_dwell, computing det_pd() at each step. Returns the first dwell that satisfies the Pd requirement, or -1 if none is found within max_dwell iterations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr
|
float
|
Per-sample amplitude SNR (linear). |
required |
pd_min
|
float
|
Required detection probability, e.g. 0.9. |
required |
pfa
|
float
|
False-alarm probability; used to derive eta. |
required |
max_dwell
|
int
|
Search upper bound; prevents infinite loops for low SNR. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Minimum dwell >= 1, or -1 if not achievable. |
Examples:
det_snr
¶
Minimum per-sample amplitude SNR achieving Pd >= pd_min.
Binary search over SNR in [0, hi] where hi is doubled from 1.0 until det_pd(hi, dwell, threshold) >= pd_min. 64 bisection iterations yield ~1e-19 relative precision on the final interval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dwell
|
int
|
Coherent integration depth; must be >= 1. |
required |
pd_min
|
float
|
Required detection probability. |
required |
pfa
|
float
|
False-alarm probability; used to derive eta. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Minimum amplitude SNR >= 0. |
Examples:
Non-coherent integration¶
When coherent integration is capped (Doppler walk, data bits, oscillator drift,
Doppler rate), N_nc coherent looks are combined by summing squared
magnitude. The detector becomes the order-N_nc Marcum-Q; these helpers package
it and reduce to the coherent (order-1) versions above at n_noncoh = 1. They
drive the doppler.dsss.Acquisition engine's coherent/non-coherent split.
det_threshold_noncoherent
¶
CFAR threshold eta_nc for a non-coherent detector of n_noncoh looks.
Solves marcum_q(n_noncoh, 0, eta_nc) = pfa (the order-M central tail, monotone decreasing in eta_nc) by bisection. For n_noncoh = 1 this is the exact closed form sqrt(-2 ln pfa) (== det_threshold).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pfa
|
float
|
Per-test false-alarm probability in (0, 1). |
required |
n_noncoh
|
int
|
Number of non-coherent looks; must be >= 1. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Threshold eta_nc on the normalized statistic R. |
Examples:
det_pd_noncoherent
¶
Detection probability for n_noncoh non-coherent looks.
Computes Pd = Q_{n_noncoh}(a, threshold) with the non-centrality a = sqrt(2 * n_coh * n_noncoh) * snr. At n_noncoh = 1 this is exactly det_pd(snr, n_coh, threshold); at snr = 0 it returns the per-test Pfa.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr
|
float
|
Per-sample amplitude SNR (signal / noise amplitude). |
required |
n_coh
|
int
|
Coherent integration length in samples (dwell * N). |
required |
n_noncoh
|
int
|
Number of non-coherent looks; must be >= 1. |
required |
threshold
|
float
|
Threshold eta_nc, e.g. from det_threshold_noncoherent(). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Detection probability in [0, 1]. |
Examples:
>>> from doppler.detection import det_pd_noncoherent, det_pd
>>> from doppler.detection import det_threshold_noncoherent
>>> from doppler.detection import det_threshold
>>> eta = det_threshold(pfa=1e-6)
>>> det_pd_noncoherent(snr=0.5, n_coh=8, n_noncoh=1, threshold=eta) \
... == det_pd(snr=0.5, dwell=8, threshold=eta) # -> coherent
True
>>> eta4 = det_threshold_noncoherent(pfa=1e-3, n_noncoh=4)
>>> round(det_pd_noncoherent(
... snr=0.3, n_coh=16, n_noncoh=4, threshold=eta4), 2)
0.19
det_n_noncoh
¶
Minimum non-coherent looks achieving Pd >= pd_min at fixed n_coh.
Iterates n_noncoh = 1, 2, ..., max_n_noncoh, recomputing the threshold (det_threshold_noncoherent, which grows with the look count) at each step. Returns the first look count that meets the Pd requirement, or -1 if none does within max_n_noncoh. Used by the acquisition engine's (M, N_nc) split.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr
|
float
|
Per-sample amplitude SNR (linear). |
required |
n_coh
|
int
|
Coherent integration length in samples (dwell * N). |
required |
pd_min
|
float
|
Required detection probability, e.g. 0.9. |
required |
pfa
|
float
|
Per-test false-alarm probability. |
required |
max_n_noncoh
|
int
|
Search upper bound on the look count. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Minimum n_noncoh >= 1, or -1 if not achievable. |
Examples:
Estimator smoothing¶
det_ema_alpha sizes a first-order EMA probabilistically: treat the
quantity being smoothed as a DC level in noise with a per-sample
estimator SNR (mean² / variance), pick the output SNR the decision
needs, and the coefficient follows from the EMA's variance reduction
(2 − α)/α. It is how the DLL's code-lock detector sizes its CFAR
noise-reference bandwidth (Dll.configure_lock(..., ref_snr_db=...)),
and the same call sizes any lock-metric smoother when the per-look SNR
is known from C/N0:
from doppler.detection import det_ema_alpha
# signal-free power reference: exponential samples = 0 dB per sample;
# a 20 dB estimator SNR needs an ~50-look EMA
assert round(1 / det_ema_alpha(0.0, 20.0), 1) == 50.5
# only the requested gain matters, not where the pair sits in dB
assert abs(det_ema_alpha(10.0, 30.0) - det_ema_alpha(0.0, 20.0)) < 1e-15
# already good enough -> no averaging
assert det_ema_alpha(6.0, 3.0) == 1.0
det_ema_alpha
¶
EMA coefficient for a target estimator SNR (DC level in noise).
Sizes a first-order EMA y = (1-alpha)*y + alpha*x that estimates a DC
level from noisy i.i.d. measurements x. Per sample the estimator SNR
(mean^2 / variance) is snr_in; the EMA improves it by its variance
reduction (2-alpha)/alpha, so the output SNR is snr_out = snr_in *
(2-alpha)/alpha. Solving for the coefficient:
alpha = 2 * snr_in / (snr_in + snr_out) (SNRs linear)
Returns 1.0 (no averaging) when snr_out_db <= snr_in_db. Typical inputs: a signal-free power reference |n|^2 is exponential (0 dB per sample); a lock signal at known C/N0 has per-look SNR from its coherent integration (minus squaring loss), and this picks the smoothing bandwidth that makes the lock decision variable meet a chosen decision SNR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr_in_db
|
float
|
Per-sample estimator SNR, dB (mean^2 / variance). |
required |
snr_out_db
|
float
|
Desired EMA-output estimator SNR, dB. |
required |
Returns:
| Type | Description |
|---|---|
float
|
EMA coefficient alpha in (0, 1]. |
Examples:
Lock verification¶
A loop that computes a lock statistic still needs a decision rule: when is
the statistic high enough, long enough, to declare lock — and low enough,
long enough, to drop it? LockDet is that rule factored out once: separate
declare/drop thresholds (level hysteresis) plus consecutive-look verify
counts (time hysteresis). Consecutive looks compound probabilistically —
n looks at per-look probability p reach p^n — so the verify counts are
derived, not guessed: det_verify_count sizes them from a per-look rate
and a compound budget, and det_verify_delay predicts the declare latency
they cost. The DLL's code-lock latch and the M-PSK receiver's two-way
acquisition↔tracking handover both run on an embedded C lockdet.
from doppler.detection import LockDet, det_verify_count, det_verify_delay
# declare side: per-decision pfa 1e-3, false-declare budget 1e-9 -> 3 straight
n_up = det_verify_count(1e-3, 1e-9)
assert n_up == 3
# drop side: per-look miss rate 1-pd = 0.2, false-drop budget 1e-4 -> 6
n_down = det_verify_count(0.2, 1e-4)
assert n_down == 6
# the price in latency: mean looks to a declare at pd = 0.9
assert round(det_verify_delay(0.9, n_up), 2) == 3.72
d = LockDet(up_thresh=8.5, down_thresh=7.0, n_up=n_up, n_down=n_down)
assert [d.step(9.0), d.step(9.0), d.step(9.0)] == [0, 0, 1] # 3rd hit locks
assert d.step(7.5) == 1 # inside the hysteresis band: sticky
det_verify_count
¶
Verify count: consecutive looks needed to compound to a budget.
n consecutive independent looks at per-look probability p compound to
p^n, so the smallest n with p_look^n <= p_target is ceil(ln p_target
/ ln p_look) (clamped to >= 1). One function serves both sides of a
lock detector (lockdet_core.h): the declare count from (per-look pfa,
false-declare budget) and the drop count from (per-look miss rate 1 -
pd, false-drop budget). Degenerate inputs resolve naturally: a target
already met by one look returns 1; p_look >= 1 can never compound below
a smaller target and returns INT_MAX.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p_look
|
float
|
Per-look probability (pfa or 1 - pd), in (0, 1). |
required |
p_target
|
float
|
Compound probability budget, in (0, 1). |
required |
Returns:
| Type | Description |
|---|---|
int
|
Smallest verify count n with p_look^n <= p_target. |
Examples:
det_verify_delay
¶
Expected looks until a run of n consecutive successes completes.
The mean waiting time of the consecutive-run process a lockdet verify counter implements: at per-look success probability p, the first run of n straight successes takes on average
E[T] = (1 - p^n) / (p^n * (1 - p)) looks,
which is the declare latency bought by a verify count of n (multiply by the look period for time). Limits are handled exactly: p = 1 gives n (the run completes immediately), p = 0 gives infinity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p_look
|
float
|
Per-look success probability (e.g. pd), in [0, 1]. |
required |
n
|
int
|
Run length (the verify count); clamped to >= 1. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Expected number of looks to the first length-n run. |
Examples:
One-shot (burst) decisions use the same machinery with one twist: when
the noise reference is estimated from as many samples as the signal
sum (the BurstDespreader lock test), the exact H0 law is F(n, n),
not chi-square — det_threshold_f prices that gate exactly, for every
n:
from doppler.detection import det_threshold_f
# F(2,2) tail is 1/(1+g): the quantile is exactly (1-pfa)/pfa
assert round(det_threshold_f(1e-3, 2), 6) == 999.0
# the estimate hardens with dof: the gate approaches the known-noise one
assert det_threshold_f(1e-3, 16) > det_threshold_f(1e-3, 64) > 1.0
det_threshold_f
¶
Upper quantile of F(n, n) — the exact H0 law for a ratio test whose noise reference is estimated from as many samples as the signal sum.
A chi-square threshold (det_threshold_noncoherent) prices a statistic
normalised by a KNOWN noise power. When the noise power is instead
estimated from n same-burst samples (the BurstDespreader lock test: sum
Re^2 against sum Im^2), the ratio's tail fattens to F(n, n) and the
chi-square gate realizes tens of times the priced pfa (41x at n = 16,
pfa = 1e-3). This helper returns the exact gate: P(chi2_n / chi2_n > g)
= I_{1/(1+g)}(n/2, n/2) = pfa, solved on the regularized incomplete
beta — valid for every n >= 1, odd included. As n grows the estimate
hardens and g approaches the known-noise value. Threshold a
BurstDespreader as lock_stat > sqrt(stat_n * det_threshold_f(pfa,
stat_n)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pfa
|
float
|
Tail probability budget, in (0, 1). |
required |
n
|
int
|
Degrees of freedom on each side (>= 1). |
required |
Returns:
| Type | Description |
|---|---|
float
|
The F(n, n) upper-pfa quantile; 0 on invalid input. |
Examples:
LockDet
¶
LockDet component.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
up_thresh
|
float
|
up_thresh constructor parameter. |
1.0
|
down_thresh
|
float
|
down_thresh constructor parameter. |
1.0
|
n_up
|
int
|
n_up constructor parameter. |
1
|
n_down
|
int
|
n_down constructor parameter. |
1
|
Examples:
Create with defaults:
>>> from doppler.detection import LockDet
>>> obj = LockDet(up_thresh=1.0, down_thresh=1.0, n_up=1, n_down=1)
cnt
property
¶
Running consecutive-look verify counter: hits toward a declare while unlocked, misses toward a drop while locked.
step
¶
Feed one look of the lock metric; return the current decision.
Unlocked: a hit (x > up_thresh) advances the verify run and the
n_up-th consecutive hit declares lock; any miss resets the run. Locked:
a miss (x < down_thresh) advances the run and the n_down-th
consecutive miss drops the lock; any hit (x >= down_thresh) resets
it. A metric inside the [down_thresh, up_thresh] band is sticky — it
neither advances a declare nor a drop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float
|
Lock metric for this look. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Decision after this look (1 = locked, 0 = not). |
Examples:
>>> from doppler.detection import LockDet
>>> d = LockDet(up_thresh=1.5, down_thresh=1.2, n_up=2, n_down=3)
>>> [d.step(2.0), d.step(2.0)] # declared on the 2nd straight hit
[0, 1]
>>> d.step(1.3) # in the hysteresis band: stays up
1
>>> [d.step(1.0), d.step(1.0), d.step(1.0)] # 3rd straight miss drops
[1, 1, 0]
steps
¶
Run a block of lock-metric looks through the detector. Applies lockdet_step() to each look in turn, so the decision flag and the in-flight verify run carry across the block exactly as they would look by look — a signal can be processed in frames of any size with no seam.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[float64]
|
Lock-metric looks, one scalar per look (length >= n). |
required |
Returns:
| Type | Description |
|---|---|
NDArray[int32]
|
Output. |
Examples:
configure
¶
Re-tune thresholds and verify counts; a live lock survives, the in-flight verify run restarts under the new config.
The current locked flag survives (a live lock is not dropped by a re-tune); the in-flight verify counter is cleared so the next run is counted entirely under the new config.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
up_thresh
|
float
|
Declare threshold (hit when metric > up_thresh). |
required |
down_thresh
|
float
|
Drop threshold (miss when metric < down_thresh). |
required |
n_up
|
int
|
Consecutive hits to declare; clamped to >= 1. |
required |
n_down
|
int
|
Consecutive misses to drop; clamped to >= 1. |
required |
Examples:
reset
¶
state_bytes
¶
Size in bytes of this object's serialized state.
The exact length get_state returns and set_state requires. It
depends on how the object was constructed (state arrays are sized at
construction), so read it from the instance rather than assuming a
constant.
Raises RuntimeError if the LockDet has already been destroyed.
Returns:
| Type | Description |
|---|---|
int
|
Byte length of one serialized state blob. |
get_state
¶
Serialize this object's mutable state to bytes.
Captures exactly the state that evolves as the object runs, so a blob taken now and restored later resumes from this point. Construction parameters are not included: restore into an object built the same way.
The blob is opaque and always state_bytes() long. Its layout is an
implementation detail of the C core and is not a stable format across
builds.
Raises RuntimeError if the LockDet has already been destroyed.
Returns:
| Type | Description |
|---|---|
bytes
|
Opaque snapshot, |
set_state
¶
Restore mutable state from a get_state() blob.
Overwrites the live state in place; the object keeps the parameters it
was constructed with. Length is validated against state_bytes()
before the blob is handed to the C core, and the core may reject it as
well.
Raises TypeError if blob is not bytes, ValueError if its
length differs from state_bytes() or the core rejects it, and
RuntimeError if the LockDet has already been destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blob
|
bytes
|
A |
required |
destroy
¶
Release the underlying C resources immediately.
Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.
Idempotent: calling it again on an already-released object does
nothing. Every other method raises RuntimeError once it has run.
__enter__
¶
Enter a context manager, returning this object.
Lets a LockDet be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
LockDet
|
This same object, not a copy. |
__exit__
¶
__exit__(
exc_type: object | None = ...,
exc: object | None = ...,
tb: object | None = ...,
) -> None
Exit a context manager, releasing the LockDet.
Equivalent to calling destroy(). Returns None, so an exception
raised inside the with body propagates normally; this never
suppresses one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
object | None
|
Exception class, or None. Ignored. |
...
|
exc
|
object | None
|
Exception instance, or None. Ignored. |
...
|
tb
|
object | None
|
Traceback object, or None. Ignored. |
...
|
Power-SNR (linear)¶
det_threshold_power
¶
Power threshold p from Pfa for the power detector.
Exact closed-form: P(Exponential(1) > p) = exp(-p) = Pfa, so
p = -ln(Pfa)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pfa
|
float
|
Desired false-alarm probability; must be in (0, 1). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Threshold p > 0. |
Examples:
det_pd_power
¶
Detection probability for the power detector.
Pd = Q_1(sqrt(2·dwell·snr_power), sqrt(2·power_threshold))
The result equals det_pd() at the equivalent amplitude SNR: power SNR
s corresponds to amplitude SNR sqrt(s), and the Q_1 arguments
match.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr_power
|
float
|
Per-sample power SNR (signal power / noise power at the correlator output, linear). 0 gives Pd = Pfa. |
required |
dwell
|
int
|
Coherent integration depth; must be >= 1. |
required |
power_threshold
|
float
|
Threshold p, e.g. from det_threshold_power(). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Detection probability in [0, 1]. |
Examples:
det_dwell_power
¶
Minimum dwell such that Pd >= pd_min for the power detector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snr_power
|
float
|
Per-sample power SNR (linear). |
required |
pd_min
|
float
|
Required detection probability. |
required |
pfa
|
float
|
False-alarm probability; used to derive p. |
required |
max_dwell
|
int
|
Search upper bound. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Minimum dwell >= 1, or -1 if not achievable. |
Examples:
det_snr_power
¶
Minimum per-sample power SNR achieving Pd >= pd_min.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dwell
|
int
|
Coherent integration depth; must be >= 1. |
required |
pd_min
|
float
|
Required detection probability. |
required |
pfa
|
float
|
False-alarm probability. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Minimum power SNR >= 0. |
Examples:
>>> from doppler.detection import (det_snr_power, det_pd_power,
... det_threshold_power)
>>> sp = det_snr_power(dwell=8, pd_min=0.9, pfa=1e-6)
>>> round(sp, 4)
2.6017
>>> pd = det_pd_power(snr_power=sp, dwell=8,
... power_threshold=det_threshold_power(pfa=1e-6))
>>> abs(pd - 0.9) < 1e-9 # det_snr_power inverts det_pd_power
True
Primitive¶
marcum_q
¶
Marcum Q function Q_M(a, b) for integer M >= 1.
Probability that a Rice(a, sigma=1) random variable exceeds b. For M=1: Q_1(a, b) = P(Rice(a,1) > b). General integer M relates to the noncentral chi-squared CDF with 2M degrees of freedom.
Computed via the Poisson-weighted chi-squared series (exact for M=1, converges in ~60 terms for practical a, b <= 15):
Q_M(a, b) = sum_{k=0}^inf w_k * Q_{M+k}(0, b)
where: w_k = exp(-u) * u^k/k! (u = a^2/2) Q_n(0,b) = exp(-v) * sum_{j=0}^{n-1} v^j/j! (v = b^2/2)
Each iteration advances both the Poisson weight and the chi-sum in O(1) using the recurrences w_{k+1} = w_k * u/(k+1) and Q_{n+1}(0,b) = Q_n(0,b) + exp(-v)*v^n/n!. Total cost: O(K) where K ~ max(u, M) + safety margin.
Special cases:
- a = 0: Q_M(0, b) = exp(-b^2/2) * sum_{j=0}^{M-1} (b^2/2)^j/j!
- b <= 0: Q_M(a, b) = 1.0
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
m
|
int
|
Integration order; must be >= 1. |
required |
a
|
float
|
Non-centrality parameter (signal strength). a = 0 for H0. |
required |
b
|
float
|
Threshold (same units as test_stat). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Q_M(a, b) in [0, 1]. |
Examples:
>>> from doppler.detection import marcum_q
>>> round(marcum_q(m=1, a=0.0, b=1.0), 5) # P(Rayleigh>1) = exp(-.5)
0.60653
>>> round(marcum_q(m=1, a=0.0, b=2.0), 5) # exp(-2)
0.13534
>>> round(marcum_q(m=2, a=0.0, b=2.0), 5) # 3*exp(-2)
0.40601
>>> round(marcum_q(m=1, a=2.0, b=1.0), 5) # signal present (a=2)
0.91811
Related pages¶
Gallery — Streaming Async Despreader, Measuring an Error Rate, Defensibly, CarrierAcquisition: RRC Pulse Shaping, Detection Theory Curves, Monte Carlo vs Marcum Q Theory, Lock Detection: Verify Counts + Hysteresis, M-PSK Receiver — Pull-in, Lock, and BER
Guides — DSSS Burst Acquisition, Lock Detection Across doppler.track
Design — Asynchronous symbol/code despreading, DSSS acquisition: stateless, parallel, dynamics-capable, MPSK Receiver