# RuView Coherence Gate Decision-Making Process: How Accept, Reject, and Recalibrate Decisions Work

> Understand the RuView coherence gate's decision-making process. Learn how Accept, Reject, and Recalibrate decisions manage Kalman filter updates and SONA/AETHER recalibration.

- Repository: [rUv/RuView](https://github.com/ruvnet/RuView)
- Tags: deep-dive
- Published: 2026-03-08

---

**The RuView coherence gate evaluates Channel State Information (CSI) frame consistency against configurable thresholds to emit Accept, PredictOnly, Reject, or Recalibrate decisions that control Kalman filter updates and trigger SONA/AETHER recalibration when data quality degrades.**

The **Coherence Gate** in the `ruvnet/RuView` repository serves as a critical safety layer between raw WiFi sensing data and the pose estimation pipeline. Implemented in [`rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs), this component determines whether a newly computed CSI-based pose measurement should be applied to the Kalman filter, ignored, or used to trigger a full system recalibration based on runtime coherence scores and stale-frame counters.

## Policy Configuration and Thresholds

The gate's behavior is governed by the `GatePolicyConfig` struct, which defines static or adaptive thresholds that tune the sensitivity of the decision-making process.

### GatePolicyConfig Structure

Located at lines 62-75 in [`coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/coherence_gate.rs), the configuration struct exposes the following parameters:

```rust
pub struct GatePolicyConfig {
    pub accept_threshold: f32,      // ≥ 0.85 by default
    pub reject_threshold: f32,      // ≤ 0.5  by default
    pub max_stale_frames: u64,      // 200 frames ≈ 10 s at 20 Hz
    pub predict_only_noise: f32,    // 3.0 (noise inflation factor)
    pub adaptive: bool,             // not used in the default policy
}

```

A `GatePolicy` object is instantiated from this configuration via `GatePolicy::from_config` (lines 19-29). The policy holds mutable runtime state including `consecutive_low` (tracking how many frames in a row have been `PredictOnly` or `Reject`) and `last_decision` (the most recent `GateDecision` for debugging).

## Decision Types and Enumeration

The gate emits one of four distinct decisions defined in the `GateDecision` enum.

### GateDecision Enum Variants

The enumeration at lines 42-60 in [`coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/coherence_gate.rs) defines the possible outcomes:

```rust
pub enum GateDecision {
    Accept { noise_multiplier: f32 }, // full Kalman update, multiplier = 1.0
    PredictOnly,                      // only the predict step, inflated noise
    Reject,                           // drop measurement entirely
    Recalibrate { stale_frames: u64 } // trigger SONA/AETHER recalibration
}

```

Helper methods including `allows_update`, `is_rejected`, and `noise_multiplier` allow downstream code to query the decision without exhaustive pattern matching on every call (lines 42-60).

## Core Evaluation Algorithm

The decision-making logic centers on the `evaluate` method, which processes runtime inputs against configured thresholds.

### The evaluate Method Logic

The `evaluate` method (lines 31-50) implements a priority-ordered decision tree that processes three inputs: the `coherence_score` (a float ∈ [0, 1] computed by the [`coherence.rs`](https://github.com/ruvnet/RuView/blob/main/coherence.rs) module), the `stale_count` (consecutive low-coherence frames), and the configured thresholds.

### Decision Tree Flow

The algorithm evaluates conditions in the following strict order:

1. **Recalibration Override**: If `stale_count >= max_stale_frames`, the gate immediately returns `GateDecision::Recalibrate` regardless of the current coherence score.
   
   ```rust
   if stale_count >= self.max_stale_frames {
       GateDecision::Recalibrate { stale_frames: stale_count }
   }
   ```

2. **Accept Zone**: When `coherence_score >= accept_threshold`, the gate returns `GateDecision::Accept`, resets `consecutive_low` to zero, and applies a noise multiplier of `1.0`.
   
   ```rust
   else if coherence_score >= self.accept_threshold {
       self.consecutive_low = 0;
       GateDecision::Accept { noise_multiplier: 1.0 }
   }
   ```

3. **Predict-Only Zone**: If the score falls between the reject and accept thresholds, the gate issues `GateDecision::PredictOnly` and increments the low-coherence counter.
   
   ```rust
   else if coherence_score >= self.reject_threshold {
       self.consecutive_low += 1;
       GateDecision::PredictOnly
   }
   ```

4. **Reject Zone**: For scores below the reject threshold, the gate returns `GateDecision::Reject` and increments the low-coherence counter.
   
   ```rust
   else {
       self.consecutive_low += 1;
       GateDecision::Reject
   }
   ```

After the decision is selected, it is stored in `self.last_decision` (line 50) for later introspection.

## Adaptive Noise Handling

The gate supports optional adaptive noise scaling for intermediate coherence scores.

### Dynamic Noise Multipliers

Although the default policy uses a fixed `predict_only_noise` value, the module provides `adaptive_noise_multiplier` (lines 87-109) to compute a smooth noise inflation factor based on how far the coherence score lies between the accept and reject thresholds. Higher-level modules reference this function when `GatePolicyConfig.adaptive` is set to `true`, allowing the Kalman filter to receive proportionally scaled measurement noise rather than binary accept/reject behavior.

## State Management and Recalibration

The gate maintains internal counters to track data quality degradation over time.

### Stale Frame Tracking

The `consecutive_low` counter increments on every `PredictOnly` or `Reject` decision. When this counter exceeds `max_stale_frames` (default 200 frames, approximately 10 seconds at 20 Hz), the gate triggers the recalibration state regardless of the current coherence score.

### Reset and Recovery

The `reset()` method (lines 74-78) clears `consecutive_low` and `last_decision` after a successful SONA/AETHER recalibration, allowing the system to resume normal operation with fresh baseline measurements.

## Integration with the Kalman Filter

The coherence gate acts as a pre-filter for the pose estimation pipeline.

### From CSI Frame to Filter Update

The integration flow follows this sequence:

1. **Coherence computation**: The [`coherence.rs`](https://github.com/ruvnet/RuView/blob/main/coherence.rs) module processes raw CSI frames and returns a coherence score float ∈ [0, 1].
2. **Stale-frame bookkeeping**: `CoherenceState` increments the stale counter each time the gate returns a low-coherence decision.
3. **Gate evaluation**: `GatePolicy::evaluate(coherence, stale_counter)` returns a `GateDecision`.
4. **Kalman filter action**:
   - `Accept` → `filter.update(measurement, noise_multiplier = 1.0)`
   - `PredictOnly` → `filter.predict()` or update with inflated noise via `predict_only_noise`
   - `Reject` → No filter call; measurement dropped
   - `Recalibrate` → System freezes output, launches the SONA/AETHER recalibration pipeline, then calls `GatePolicy::reset()`

## Code Examples

### Creating a Gate with Custom Thresholds

```rust
use wifi_densepose_signal::ruvsense::{
    GatePolicy, GatePolicyConfig,
};

// Custom policy: stricter acceptance, longer stale timeout.
let cfg = GatePolicyConfig {
    accept_threshold: 0.90,
    reject_threshold: 0.40,
    max_stale_frames: 300,          // 15 s at 20 Hz
    predict_only_noise: 4.0,
    adaptive: false,
};

let mut gate = GatePolicy::from_config(&cfg);

```

### Running a Decision Cycle

```rust
// Suppose we have a fresh coherence score from the coherence module.
let coherence_score = 0.72_f32;

// The stale-frame counter is maintained elsewhere (e.g., in CoherenceState).
let stale_frames = 5_u64;

let decision = gate.evaluate(coherence_score, stale_frames);

match decision {
    GateDecision::Accept { noise_multiplier } => {
        println!("Accept – update with noise ×{noise_multiplier}");
        // kalman.update(meas, noise_multiplier);
    }
    GateDecision::PredictOnly => {
        println!("PredictOnly – run predict step, inflate noise");
        // kalman.predict(); // or update with gate.predict_only_noise
    }
    GateDecision::Reject => {
        println!("Reject – discard measurement");
        // ignore this frame
    }
    GateDecision::Recalibrate { stale_frames } => {
        println!("Recalibrate after {stale_frames} low-coherence frames");
        // launch SONA/AETHER pipeline, then gate.reset();
    }
}

```

### Adaptive Noise Example

```rust
// When adaptive mode is enabled, compute the noise multiplier dynamically:
let noise = adaptive_noise_multiplier(
    coherence_score,
    cfg.accept_threshold,
    cfg.reject_threshold,
    cfg.predict_only_noise,
);
println!("Adaptive noise multiplier: {noise}");

```

## Key Files and Components

| File | Role |
|------|------|
| [[`coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/coherence_gate.rs)](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs) | Core gate policy, decision enum, evaluation algorithm. |
| [[`coherence.rs`](https://github.com/ruvnet/RuView/blob/main/coherence.rs)](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence.rs) | Calculates the coherence score from CSI frames. |
| [[`pose_tracker.rs`](https://github.com/ruvnet/RuView/blob/main/pose_tracker.rs)](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/pose_tracker.rs) | Consumes `GateDecision` to drive Kalman filter updates. |
| [`GatePolicyConfig`](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs#L62-L75) | Configuration struct that tunes the gate thresholds. |
| [`adaptive_noise_multiplier`](https://github.com/ruvnet/RuView/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/ruvsense/coherence_gate.rs#L87-L109) | Helper for smooth noise inflation when `adaptive` is true. |

## Summary

- The **Coherence Gate** in [`coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/coherence_gate.rs) acts as a safety filter between CSI measurements and the Kalman filter, preventing noisy data from corrupting pose estimates.
- **Four distinct decisions**—`Accept`, `PredictOnly`, `Reject`, and `Recalibrate`—are determined by comparing coherence scores against configurable `accept_threshold` and `reject_threshold` values.
- **Recalibration triggers** automatically when `max_stale_frames` (default 200 frames) consecutive low-coherence measurements occur, initiating the SONA/AETHER pipeline to restore system accuracy.
- **Adaptive noise scaling** via `adaptive_noise_multiplier` allows smooth degradation of measurement confidence rather than binary filtering when `GatePolicyConfig.adaptive` is enabled.

## Frequently Asked Questions

### What inputs does the RuView coherence gate use to make decisions?

The coherence gate evaluates three primary inputs: the **coherence score** (a float between 0 and 1 computed by the [`coherence.rs`](https://github.com/ruvnet/RuView/blob/main/coherence.rs) module), the **stale-frame counter** (tracking consecutive low-coherence frames maintained in `CoherenceState`), and the **gate policy configuration** (`GatePolicyConfig` struct) containing threshold values and timeout limits.

### How does the coherence gate decide when to trigger a full system recalibration?

The gate triggers `GateDecision::Recalibrate` when the internal `stale_count` exceeds `max_stale_frames` (default 200 frames, approximately 10 seconds at 20 Hz), regardless of the current coherence score. This override takes precedence over all other threshold checks in the `evaluate` method, ensuring the system initiates the SONA/AETHER recalibration pipeline before pose estimates become unreliable.

### What is the difference between PredictOnly and Reject decisions in the coherence gate?

**PredictOnly** indicates the coherence score falls between the `reject_threshold` and `accept_threshold`, meaning the measurement is questionable but not useless; the Kalman filter runs only the prediction step (or updates with inflated noise using `predict_only_noise`), preserving temporal continuity while avoiding measurement corruption. **Reject** occurs when the coherence score falls below `reject_threshold`, indicating the frame is too unreliable; the measurement is discarded entirely with no Kalman filter update, though both decisions increment the `consecutive_low` counter toward potential recalibration.

### Can the coherence gate adapt its noise levels based on coherence quality?

Yes, when `GatePolicyConfig.adaptive` is set to `true`, the gate utilizes the `adaptive_noise_multiplier` helper function (lines 87-109 in [`coherence_gate.rs`](https://github.com/ruvnet/RuView/blob/main/coherence_gate.rs)) to compute a smooth noise inflation factor based on how far the coherence score lies between the accept and reject thresholds. This allows the Kalman filter to receive proportionally scaled measurement noise rather than binary accept/reject behavior, enabling graceful degradation of pose estimate confidence during marginal signal conditions.