FIRFilter FIRFilter Icon

FIRFilter

Finite impulse response (FIR) filter with the input-output relation

\[y(k) = \sum_{i=1}^{n} c_i u(k-i+1)\]

corresponding to the transfer function

\[H(z) = \sum_{i=1}^{n} c_i z^{-(i-1)}\]

where $c$ is coeffs. The filter has no feedback, so it is stable for any coefficients and its impulse response is coeffs, of length n. The sample period is that of the connected clock.

MovingAverageFilter is the special case in which all coefficients equal $1/n$, which is also the default here.

The initial condition is selected through the initialization enum. The state of the filter is the input history, so every variant describes the $n-1$ input samples that precede the first one:

  • InitialState(x0) sets all of them to x0. This is the default, with $x_0 = 0$, which is the usual convention for FIR filtering: the output is the convolution of the input with coeffs, the input being extended by zeros before its first sample.
  • InitialOutput(y0) sets them to the common level that makes the first output equal y0. This requires the coefficients other than the first to have a nonzero sum, $\sum_{i=2}^{n} c_i \neq 0$.
  • SteadyState sets them to the first input sample, as if the input had been constant up to that point, so the first output is the DC response $u(1) \sum_{i=1}^{n} c_i$.
  • InitialStateArray(x0) sets the sample delayed by $i$ samples to x0[i], which is the only variant that admits an input history that is not constant.

The single-tap case $n = 1$ has no input history and ignores initialization.

Usage

DiscreteComponents.FIRFilter(coeffs=fill(1.0 / n, n), u_init=_ic_state_level(initialization))

Parameters:

NameDescriptionUnitsDefault value
nNumber of filter taps–2
initializationInitial-condition specification–DiscreteCom...e(; x0=0.0)
ndNumber of delayed taps, one less than the number of coefficients–n - 1
coeffsFilter coefficients; coeffs[i] multiplies the input delayed by i-1 samples–fill(1.0 / n, n)

Connectors

  • u - This connector represents a real signal as an input to a component (RealInput)
  • y - This connector represents a real signal as an output from a component (RealOutput)

Variables

NameDescriptionUnits
cuProducts coeffs[i]*u(k-i+1)–

Behavior

using DiscreteComponents #hide
using ModelingToolkit #hide
@variables coeffs #hide
@named sys = DiscreteComponents.FIRFilter(coeffs=coeffs) #hide
full_equations(sys) #hide

Source

"""
Finite impulse response (FIR) filter with the input-output relation
```math
y(k) = \sum_{i=1}^{n} c_i u(k-i+1)
```
corresponding to the transfer function
```math
H(z) = \sum_{i=1}^{n} c_i z^{-(i-1)}
```
where ``c`` is `coeffs`. The filter has no feedback, so it is stable for any coefficients
and its impulse response is `coeffs`, of length `n`. The sample period is that of the
connected clock.

`MovingAverageFilter` is the special case in which all coefficients equal ``1/n``, which
is also the default here.

The initial condition is selected through the `initialization` enum. The state of the
filter is the input history, so every variant describes the ``n-1`` input samples that
precede the first one:
- `InitialState(x0)` sets all of them to `x0`. This is the default, with ``x_0 = 0``,
  which is the usual convention for FIR filtering: the output is the convolution of the
  input with `coeffs`, the input being extended by zeros before its first sample.
- `InitialOutput(y0)` sets them to the common level that makes the first output equal
  `y0`. This requires the coefficients other than the first to have a nonzero sum,
  ``\sum_{i=2}^{n} c_i \neq 0``.
- `SteadyState` sets them to the first input sample, as if the input had been constant up
  to that point, so the first output is the DC response ``u(1) \sum_{i=1}^{n} c_i``.
- `InitialStateArray(x0)` sets the sample delayed by ``i`` samples to `x0[i]`, which is
  the only variant that admits an input history that is not constant.

The single-tap case ``n = 1`` has no input history and ignores `initialization`.
"""
component FIRFilter@[input clk extends Discrete]
  "Input signal"
  u = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Output signal"
  y = RealOutput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Number of filter taps"
  structural parameter n::Integer = 2
  "Filter coefficients; coeffs[i] multiplies the input delayed by i-1 samples"
  parameter coeffs::Real[n] = fill(1.0 / n, n)
  "Initial-condition specification"
  structural parameter initialization::InitialCondition = DiscreteComponents.InitialCondition.InitialState(x0 = 0.0)
  "Number of delayed taps, one less than the number of coefficients"
  final structural parameter nd::Integer = n - 1
  "Common level of the input samples preceding the first one (only used by the InitialState variant)"
  final parameter u_init::Real = _ic_state_level(initialization)
  "Products coeffs[i]*u(k-i+1)"
  variable cu::Real[n]
relations
  # Dyad has no sum over a generator, so the dot product against the delayed inputs is
  # materialized as an array of products and reduced with `sum`.
  cu[1] = coeffs[1] * u@clk
  for i in 1:nd
    cu[i + 1] = coeffs[i + 1] * u@(clk-i)
  end
  y@clk = sum(cu)
  switch initialization
    case InitialState
      for i in 1:nd
        initial u@(clk-i) = u_init
      end
    case InitialOutput
      for i in 1:nd
        initial u@(clk-i) = (initialization.y0 - coeffs[1] * u@clk) / (sum(coeffs) - coeffs[1])
      end
    case SteadyState
      for i in 1:nd
        initial u@(clk-i) = u@clk
      end
    case InitialStateArray
      for i in 1:nd
        initial u@(clk-i) = initialization.x0[i]
      end
    case InitialOutputArray
      for i in 1:nd
        initial u@(clk-i) = error("InitialOutputArray is not supported for FIRFilter; use InitialOutput, InitialState, InitialStateArray, or SteadyState")
      end
  end
metadata {"Dyad": {"icons": {"default": "dyad://DiscreteComponents/FIRFilter.svg"}}}
end
Flattened Source
"""
Finite impulse response (FIR) filter with the input-output relation
```math
y(k) = \sum_{i=1}^{n} c_i u(k-i+1)
```
corresponding to the transfer function
```math
H(z) = \sum_{i=1}^{n} c_i z^{-(i-1)}
```
where ``c`` is `coeffs`. The filter has no feedback, so it is stable for any coefficients
and its impulse response is `coeffs`, of length `n`. The sample period is that of the
connected clock.

`MovingAverageFilter` is the special case in which all coefficients equal ``1/n``, which
is also the default here.

The initial condition is selected through the `initialization` enum. The state of the
filter is the input history, so every variant describes the ``n-1`` input samples that
precede the first one:
- `InitialState(x0)` sets all of them to `x0`. This is the default, with ``x_0 = 0``,
  which is the usual convention for FIR filtering: the output is the convolution of the
  input with `coeffs`, the input being extended by zeros before its first sample.
- `InitialOutput(y0)` sets them to the common level that makes the first output equal
  `y0`. This requires the coefficients other than the first to have a nonzero sum,
  ``\sum_{i=2}^{n} c_i \neq 0``.
- `SteadyState` sets them to the first input sample, as if the input had been constant up
  to that point, so the first output is the DC response ``u(1) \sum_{i=1}^{n} c_i``.
- `InitialStateArray(x0)` sets the sample delayed by ``i`` samples to `x0[i]`, which is
  the only variant that admits an input history that is not constant.

The single-tap case ``n = 1`` has no input history and ignores `initialization`.
"""
component FIRFilter
  "Input signal"
  u = RealInput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -100, "y1": 450, "x2": 0, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Output signal"
  y = RealOutput@[clk]() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 1000, "y1": 450, "x2": 1100, "y2": 550, "rot": 0}
      },
      "tags": []
    }
  }
  "Number of filter taps"
  structural parameter n::Integer = 2
  "Filter coefficients; coeffs[i] multiplies the input delayed by i-1 samples"
  parameter coeffs::Real[n] = fill(1.0 / n, n)
  "Initial-condition specification"
  structural parameter initialization::InitialCondition = DiscreteComponents.InitialCondition.InitialState(x0 = 0.0)
  "Number of delayed taps, one less than the number of coefficients"
  final structural parameter nd::Integer = n - 1
  "Common level of the input samples preceding the first one (only used by the InitialState variant)"
  final parameter u_init::Real = _ic_state_level(initialization)
  "Products coeffs[i]*u(k-i+1)"
  variable cu::Real[n]
relations
  # Dyad has no sum over a generator, so the dot product against the delayed inputs is
  # materialized as an array of products and reduced with `sum`.
  cu[1] = coeffs[1] * u@clk
  for i in 1:nd
    cu[i + 1] = coeffs[i + 1] * u@(clk-i)
  end
  y@clk = sum(cu)
  switch initialization
    case InitialState
      for i in 1:nd
        initial u@(clk-i) = u_init
      end
    case InitialOutput
      for i in 1:nd
        initial u@(clk-i) = (initialization.y0 - coeffs[1] * u@clk) / (sum(coeffs) - coeffs[1])
      end
    case SteadyState
      for i in 1:nd
        initial u@(clk-i) = u@clk
      end
    case InitialStateArray
      for i in 1:nd
        initial u@(clk-i) = initialization.x0[i]
      end
    case InitialOutputArray
      for i in 1:nd
        initial u@(clk-i) = error("InitialOutputArray is not supported for FIRFilter; use InitialOutput, InitialState, InitialStateArray, or SteadyState")
      end
  end
metadata {"Dyad": {"icons": {"default": "dyad://DiscreteComponents/FIRFilter.svg"}}}
end


Test Cases

No test cases defined.

  • Examples
  • Experiments
  • Analyses