Basic system identification sanity check

Tutorial goal

Run a minimal identification example to check package wiring and API behavior.

Note

New to the terminology? See the lattice DSP concept map and the causality/data-use guide for how online, offline, block, and MIMO examples should be read.

Context

This page is intentionally simple. It is a first script to run after installation to make sure the compiled extension and Python package import correctly.

Key idea and equations

The desired signal is generated by a known system from a reference input:

\[d[n] = H(q^{-1})x[n].\]

The model output \(\widehat d[n]\) is compared with \(d[n]\). This is the two-signal identification form, not causal self-prediction of x[n] from its own past.

Causality and data use

The example is a small causal system-identification sanity check: output is formed from the reference input and current filter state, then compared with the desired signal.

How to read the result

A successful run should complete without import/build errors and should report a finite identification error.

Run command

python examples/system_identification.py

Source code

 1"""Fixed stable lattice denominator + adaptive ladder taps demo.
 2
 3Run after installing the package:
 4    python examples/system_identification.py
 5"""
 6
 7import numpy as np
 8
 9from lattice_dsp import LatticeIIR, LatticeLadderNLMS
10
11
12rng = np.random.default_rng(42)
13n = 4000
14x = rng.normal(size=n)
15
16# Synthetic unknown stable lattice-ladder system.
17true_reflection = [0.35, -0.25]
18true_taps = [0.15, -0.2, 0.8]
19plant = LatticeIIR(true_reflection, true_taps)
20d = plant.process(x) + 0.01 * rng.normal(size=n)
21
22# Estimate ladder taps while keeping the stable denominator fixed.
23adaptive = LatticeLadderNLMS(true_reflection, [0.0, 0.0, 0.0], mu=0.2)
24errors = np.array(adaptive.adapt_block(x.tolist(), d.tolist()))
25
26print("true taps:     ", true_taps)
27print("estimated taps:", [round(v, 5) for v in adaptive.taps])
28print("initial MSE:   ", float(np.mean(errors[:500] ** 2)))
29print("final MSE:     ", float(np.mean(errors[-500:] ** 2)))