firwin function
dynamic
firwin(})
FIR filter design using the window method.
This function computes the coefficients of a finite impulse response
filter. The filter will have linear phase; it will be Type I if
numtaps
is odd and Type II if numtaps
is even.
Type II filters always have zero response at the Nyquist frequency, so a
ValueError exception is raised if firwin is called with numtaps
even and
having a passband whose right end is at the Nyquist frequency.
Parameters
numtaps
: int Length of the filter (number of coefficients, i.e. the filter order + 1).numtaps
must be odd if a passband includes the Nyquist frequency.cutoff
: Array Cutoff frequency of filter (expressed in the same units asfs
) OR an array of cutoff frequencies (that is, band edges). In the latter case, the frequencies incutoff
should be positive and monotonically increasing between 0 andfs/2
. The values 0 andfs/2
must not be included incutoff
.width
: double or None, optional Ifwidth
is not None, then assume it is the approximate width of the transition region (expressed in the same units asfs
) for use in Kaiser FIR filter design. In this case, thewindow
argument is ignored.window
: string or tuple of string and parameter values, optional Desired window to use. Seescipy.signal.get_window
for a list of windows and required parameters.pass_zero
: {true, false, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional If True, the gain at the frequency 0 (i.e. the "DC gain") is 1. If False, the DC gain is 0. Can also be a string argument for the desired filter type (equivalent tobtype
in IIR design functions). .. versionadded:: 1.3.0 Support for string arguments.scale
: bool, optional Set to True to scale the coefficients so that the frequency response is exactly unity at a certain frequency. That frequency is either:- 0 (DC) if the first passband starts at 0 (i.e. pass_zero is True)
fs/2
(the Nyquist frequency) if the first passband ends at fs/2` (i.e the filter is a single band highpass filter); center of first passband otherwisenyq
: double, optional Deprecated. Usefs
instead. This is the Nyquist frequency. Each frequency incutoff
must be between 0 andnyq
. Default is 1.fs
: float, optional The sampling frequency of the signal. Each frequency incutoff
must be between 0 andfs/2
. Default is 2.
Returns
- h : Array
Coefficients of length
numtaps
FIR filter.
Raises
- FormatException If any value in
cutoff
is less than or equal to 0 or greater than or equal tofs/2
, if the values incutoff
are not strictly monotonically increasing, or ifnumtaps
is even but a passband includes the Nyquist frequency.
References
- "doc scipy.signal.firwin". https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.firwin.html#scipy.signal.firwin. Retrieved 2019-08-06.
- "source code scipy.signal.firwin". https://github.com/scipy/scipy/blob/v1.3.0/scipy/signal/fir_filter_design.py#L264-L482. Retrieved 2019-08-06.
- "fir filter design". https://scipy-cookbook.readthedocs.io/items/FIRFilter.html. Retrieved 2019-08-06.
Examples
var numtaps = 3;
var f = 0.1;
print(firwin(numtaps, Array([f])));
/* output:
Array([0.06799017, 0.86401967, 0.06799017]);
*/
Implementation
dynamic firwin(int numtaps, Array cutoff,
{double? width,
dynamic window = 'hamming',
dynamic pass_zero = true,
bool scale = true,
double? nyq,
double? fs}) {
nyq = 0.5 * _getFs(fs, nyq);
cutoff = arrayDivisionToScalar(cutoff, nyq);
// Check for invalid input.
if (cutoff.isEmpty) {
throw FormatException('At least one cutoff frequency must be given.');
}
if (arrayMin(cutoff) <= 0 || arrayMax(cutoff) >= 1) {
throw FormatException(
'Invalid cutoff frequency: frequencies must be greater than 0 and less than fs/2.');
}
if (arrayDiff(cutoff).any((i) => i <= 0)) {
throw FormatException(
'Invalid cutoff frequencies: the frequencies must be strictly increasing.');
}
if (width != null) {
// A width was given. Find the beta parameter of the Kaiser window
// and set `window`. This overrides the value of `window` passed in.
var atten = kaiserAtten(numtaps, width / nyq);
var beta = kaiserBeta(atten);
window = ['kaiser', beta];
}
if (pass_zero is String) {
if (pass_zero == 'bandstop' || pass_zero == 'lowpass') {
if (pass_zero == 'lowpass') {
if (cutoff.length != 1) {
throw FormatException(
'cutoff must have one element if pass_zero=="lowpass", got $cutoff');
} else if (cutoff.length <= 1) {
throw FormatException(
'cutoff must have at least two elements if pass_zero=="bandstop", got $cutoff');
}
pass_zero = true;
} else if (pass_zero == 'bandpass' || pass_zero == 'highpass') {
if (pass_zero == 'highpass') {
if (cutoff.length != 1) {
throw FormatException(
'cutoff must have one element if pass_zero=="highpass", got ${cutoff.length}');
}
} else if (cutoff.length <= 1) {
throw FormatException(
'cutoff must have at least two elements if pass_zero=="bandpass", got $cutoff');
}
pass_zero = false;
} else {
throw FormatException(
'pass_zero must be True, False, "bandpass", "lowpass", "highpass", or "bandstop", got $pass_zero');
}
}
}
// ensure bool-like
if (!(pass_zero is bool)) {
throw FormatException(
"pass_zero is not a bool, please, insert: true, false, 'bandpass', 'lowpass', 'highpass', 'bandstop'");
}
var pass_nyquist = intToBool(cutoff.length & 1 ^ boolToInt(pass_zero));
if (pass_nyquist && numtaps % 2 == 0) {
throw FormatException(
'A filter with an even number of coefficients must have zero response at the Nyquist frequency.');
}
// Insert 0 and/or 1 at the ends of cutoff so that the length of cutoff
// is even, and each pair in cutoff corresponds to passband.
if (pass_zero) {
cutoff = arrayConcat([
Array([0.0]),
cutoff
]);
}
if (pass_nyquist) {
cutoff = arrayConcat([
cutoff,
Array([1.0])
]);
}
// `bands` is a 2D array; each row gives the left and right edges of
// a passband.
var bands = arrayReshapeToMatrix(cutoff, 2);
// Build up the coefficients.
var alpha = 0.5 * (numtaps - 1);
var m = arraySubToScalar(createArrayRange(start: 0, stop: numtaps), alpha);
var h = Array.fixed(m.length, initialValue: 0);
for (var j = 0; j < bands.row; j++) {
// lr[0] - left; lr[1] - right
var left = bands[j][0];
var right = bands[j][1];
h += arrayMultiplyToScalar(
arraySinc(arrayMultiplyToScalar(m, right)), right);
h -= arrayMultiplyToScalar(arraySinc(arrayMultiplyToScalar(m, left)), left);
}
// Get and apply the window function.
var win = getWindow(window, numtaps, fftbins: false);
h *= win;
// Now handle scaling if desired.
if (scale) {
// Get the first passband.
var left = bands[0][0];
var right = bands[0][1];
var scale_frequency;
if (left == 0) {
scale_frequency = 0.0;
} else if (right == 1) {
scale_frequency = 1.0;
} else {
scale_frequency = 0.5 * (left + right);
}
var c = arrayCos(arrayMultiplyToScalar(m, pi * scale_frequency));
var s = arraySum(h * c);
h = arrayDivisionToScalar(h, s);
}
return h;
}