# Choosing the Cutoff Frequency for ESPectre’s Low‑Pass Filter: A Practical Guide

> Optimize your ESPectre lowpass filter by choosing the cutoff frequency between 5Hz and 20Hz. Learn how noise and motion speed impact this crucial setting.

- Repository: [Francesco Pace/espectre](https://github.com/francescopace/espectre)
- Tags: how-to-guide
- Published: 2026-06-10

---

**Set the `lowpass_cutoff` parameter between 5 Hz and 20 Hz depending on your noise environment and motion speed requirements, starting with the default 11 Hz for balanced detection performance.**

ESPectre is an open-source Wi‑Fi sensing framework that converts Channel State Information (CSI) into motion detection signals. When high-frequency RF noise causes false-positive motion events, the optional **low-pass filter** implemented in [`micro-espectre/src/filters.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/filters.py) attenuates frequencies above the configured cutoff while preserving the slower variations generated by human movement. Selecting the optimal cutoff frequency requires balancing noise suppression against the risk of filtering out rapid gestures.

## What the ESPectre Low‑Pass Filter Does

### Filter Architecture and Purpose

The `LowPassFilter` class implements a **first-order IIR Butterworth** design that removes fast-varying components from the turbulence signal. According to the source code in [`micro-espectre/src/filters.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/filters.py) (lines 18–65), the filter preserves the slower motion-related variations typical of human movement (approximately 0.5–10 Hz) while suppressing high-frequency RF bursts caused by Wi-Fi traffic or electrical interference.

The filter calculates its coefficients (`b0` and `a1`) at initialization using the bilinear transform (lines 58–65). During operation, the `filter()` method applies the difference equation:

```

y[n] = b0·x[n] + b0·x[n‑1] – a1·y[n‑1]

```

### Default Configuration State

By default, the filter is **disabled** (`lowpass_enabled: false`) to minimize processing overhead in quiet environments. When enabled, the default cutoff frequency is **11 Hz**, defined as `LOWPASS_CUTOFF` in [`micro-espectre/src/config.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/config.py) (line 73). This value was selected during empirical testing to remove the majority of RF spikes while preserving the 0.5–10 Hz motion band common to most human activities.

## How the Cutoff Frequency Affects Motion Detection

The `lowpass_cutoff` value determines which frequency components reach the motion detection algorithm. The repository documentation identifies three operational ranges:

| Cutoff Range | Signal Effect | Detection Impact |
|-------------|---------------|------------------|
| **5 – 8 Hz** | Strong filtering removes most high-frequency noise | Fewer false positives, but fast gestures may be smoothed out causing missed detections |
| **11 Hz (default)** | Balanced filtering preserves human-movement frequencies while reducing RF spikes | Optimal compromise with ~92% recall and <3% false-positive rate |
| **15 – 20 Hz** | Light filtering attenuates only the highest noise components | Higher recall for rapid movements but increased false positives in crowded RF environments |

Two opposing factors drive the selection:

- **Noise level**: Noisy RF environments (multiple Wi-Fi devices, microwaves) benefit from lower cutoffs (5–8 Hz) to suppress spurious high-frequency interference.
- **Motion speed**: Rapid gestures (sports, fast hand motions) require higher cutoffs (15–20 Hz) to retain high-frequency motion components.

## Selecting the Right Cutoff Frequency

Follow this iterative tuning process to optimize your deployment:

1. **Start with defaults**: Begin with `lowpass_enabled: false` or enabled at `lowpass_cutoff: 11` to establish a baseline.
2. **Address false positives**: If you observe motion events while the room is empty, enable the filter and reduce the cutoff to **5–8 Hz**.
3. **Recover missed movements**: If quick gestures go undetected after lowering the cutoff, raise the value gradually (12 Hz → 15 Hz) until detection recovers while monitoring the false-positive rate.
4. **Iterate and test**: Re-flash the device after each change, or adjust via Home Assistant for session-only testing, and monitor the motion binary sensor logs.

## Why the Default is 11 Hz

The 11 Hz default was selected during extensive empirical testing as documented in the repository’s Tuning Guide. At this frequency, the filter removes the majority of high-frequency RF bursts caused by Wi-Fi traffic while preserving the 0.5–10 Hz band that contains most human motion signatures. This setting achieves approximately 92% recall with a false-positive rate below 3%, making it suitable for general-purpose room occupancy detection.

## Interaction with Other Signal Processing Stages

Understanding the filter’s position in the processing chain prevents configuration conflicts. The low-pass filter sits **after** the optional Hampel outlier filter (which removes impulsive spikes) and **before** any normalization steps. It operates independently of the gain-lock and CV-normalization mechanisms, which process raw CSI amplitude before turbulence calculation.

## Code Examples

### Enabling the Filter via ESPHome Configuration

Configure the cutoff frequency in your ESPHome YAML file. The values are passed to [`micro-espectre/src/config.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/config.py), where `LOWPASS_CUTOFF` is read at line 73:

```yaml
espectre:
  lowpass_enabled: true          # Enable the IIR Butterworth filter

  lowpass_cutoff: 11.0           # Cut-off frequency in Hz

  segmentation_threshold: auto   # Maintain default adaptive threshold

```

### Direct Python Usage

Instantiate the `LowPassFilter` class directly for custom processing pipelines:

```python
from micro_espectre.src.filters import LowPassFilter

# Initialize with custom cutoff and 100 Hz sample rate

lp = LowPassFilter(cutoff_hz=8.0, sample_rate_hz=100.0, enabled=True)

# Process turbulence values stream

filtered_values = []
for value in turbulence_stream:
    filtered_values.append(lp.filter(value))

# Reset filter state between measurement sessions

lp.reset()

```

The constructor calculates coefficients using the bilinear transform, and the `filter()` method applies the IIR difference equation as implemented in lines 58–65 of [`micro-espectre/src/filters.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/filters.py).

### Validating Filter Configuration in Unit Tests

Verify filter initialization in your test suite as shown in [`micro-espectre/tests/test_segmentation.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/tests/test_segmentation.py) (lines 65–69):

```python
from micro_espectre.tests.test_segmentation import SegmentationContext

ctx = SegmentationContext(enable_lowpass=True, lowpass_cutoff=11.5)
assert ctx.lowpass_filter is not None
assert ctx.lowpass_filter.cutoff_hz == 11.5

```

## Summary

- The ESPectre low-pass filter is a **first-order IIR Butterworth** implementation in [`micro-espectre/src/filters.py`](https://github.com/francescopace/espectre/blob/main/micro-espectre/src/filters.py) that reduces high-frequency RF noise.
- The default **11 Hz cutoff** balances noise suppression with motion detection accuracy (~92% recall, <3% false positives).
- **Lower cutoffs (5–8 Hz)** suit noisy environments but may miss rapid gestures; **higher cutoffs (15–20 Hz)** capture fast movements but increase false positives.
- Configure the cutoff via ESPHome YAML (`lowpass_cutoff`) or programmatically through the `LowPassFilter` class constructor.
- The filter processes turbulence signals after Hampel outlier removal and before normalization stages.

## Frequently Asked Questions

### What is the optimal cutoff frequency for a noisy office environment?

For environments with high RF interference from multiple Wi-Fi devices or microwave ovens, set the cutoff to **5–8 Hz**. This aggressive filtering removes the high-frequency noise responsible for false-positive motion alerts while still preserving slower body movements.

### Can I disable the low-pass filter entirely?

Yes. Set `lowpass_enabled: false` in your ESPHome configuration or pass `enabled=False` to the `LowPassFilter` constructor. Disabling the filter reduces CPU usage and is appropriate for quiet RF environments where false positives are not problematic.

### How does the cutoff frequency interact with the sample rate?

The `LowPassFilter` class requires both `cutoff_hz` and `sample_rate_hz` parameters. The sample rate determines the Nyquist frequency (half the sample rate), which must be higher than your chosen cutoff. The filter coefficients (`b0` and `a1`) are calculated at initialization using the bilinear transform based on these two values, ensuring stable operation across different sampling configurations.

### Will lowering the cutoff frequency below 11 Hz introduce detection latency?

A first-order IIR filter introduces minimal phase delay, but very low cutoffs (below 5 Hz) may smooth rapid motion onsets enough to delay the threshold-crossing event by one or two samples (10–20 ms at 100 Hz). For most human motion detection scenarios, this delay is imperceptible compared to the inherent latency of the Wi-Fi sensing pipeline.