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).numtapsmust 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 incutoffshould be positive and monotonically increasing between 0 andfs/2. The values 0 andfs/2must not be included incutoff.width: double or None, optional Ifwidthis 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, thewindowargument is ignored.window: string or tuple of string and parameter values, optional Desired window to use. Seescipy.signal.get_windowfor 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 tobtypein 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. Usefsinstead. This is the Nyquist frequency. Each frequency incutoffmust be between 0 andnyq. Default is 1.fs: float, optional The sampling frequency of the signal. Each frequency incutoffmust be between 0 andfs/2. Default is 2.
Returns
- h : Array
Coefficients of length
numtapsFIR filter.
Raises
- FormatException If any value in
cutoffis less than or equal to 0 or greater than or equal tofs/2, if the values incutoffare not strictly monotonically increasing, or ifnumtapsis 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;
}