scipy 1.8.0 Pypi GitHub Homepage
Other Docs
NotesParametersReturnsBackRef
zpk2sos(z, p, k, pairing=None, *, analog=False)

Notes

The algorithm used to convert ZPK to SOS format is designed to minimize errors due to numerical precision issues. The pairing algorithm attempts to minimize the peak gain of each biquadratic section. This is done by pairing poles with the nearest zeros, starting with the poles closest to the unit circle for discrete-time systems, and poles closest to the imaginary axis for continuous-time systems.

pairing='minimal' outputs may not be suitable for sosfilt , and analog=True outputs will never be suitable for sosfilt .

Algorithms

The steps in the pairing='nearest' , pairing='keep_odd' , and pairing='minimal' algorithms are mostly shared. The 'nearest' algorithm attempts to minimize the peak gain, while 'keep_odd' minimizes peak gain under the constraint that odd-order systems should retain one section as first order. 'minimal' is similar to 'keep_odd' , but no additional poles or zeros are introduced

The algorithm steps are as follows:

As a pre-processing step for pairing='nearest' , pairing='keep_odd' , add poles or zeros to the origin as necessary to obtain the same number of poles and zeros for pairing. If pairing == 'nearest' and there are an odd number of poles, add an additional pole and a zero at the origin.

The following steps are then iterated over until no more poles or zeros remain:

  1. Take the (next remaining) pole (complex or real) closest to the unit circle (or imaginary axis, for analog=True ) to begin a new filter section.

  2. If the pole is real and there are no other remaining real poles , add the closest real zero to the section and leave it as a first order section. Note that after this step we are guaranteed to be left with an even number of real poles, complex poles, real zeros, and complex zeros for subsequent pairing iterations.

  3. Else:

    1. If the pole is complex and the zero is the only remaining real zero*, then pair the pole with the next closest zero (guaranteed to be complex). This is necessary to ensure that there will be a real zero remaining to eventually create a first-order section (thus keeping the odd order).

    2. Else pair the pole with the closest remaining zero (complex or real).

    3. Proceed to complete the second-order section by adding another pole and zero to the current pole and zero in the section:

      1. If the current pole and zero are both complex, add their conjugates.

      2. Else if the pole is complex and the zero is real, add the conjugate pole and the next closest real zero.

      3. Else if the pole is real and the zero is complex, add the conjugate zero and the real pole closest to those zeros.

      4. Else (we must have a real pole and real zero) add the next real pole closest to the unit circle, and then add the real zero closest to that pole.

            <Unimplemented 'footnote' ".. [#] This conditional can only be met for specific odd-order inputs\n       with the ``pairing = 'keep_odd'`` or ``'minimal'`` methods.">
           
versionadded

Parameters

z : array_like

Zeros of the transfer function.

p : array_like

Poles of the transfer function.

k : float

System gain.

pairing : {None, 'nearest', 'keep_odd', 'minimal'}, optional

The method to use to combine pairs of poles and zeros into sections. If analog is False and pairing is None, pairing is set to 'nearest'; if analog is True, pairing must be 'minimal', and is set to that if it is None.

analog : bool, optional

If True, system is analog, otherwise discrete.

versionadded

Returns

sos : ndarray

Array of second-order filter coefficients, with shape (n_sections, 6) . See sosfilt for the SOS filter format specification.

Return second-order sections from zeros, poles, and gain of a system

See Also

sosfilt

Examples

Design a 6th order low-pass elliptic digital filter for a system with a sampling rate of 8000 Hz that has a pass-band corner frequency of 1000 Hz. The ripple in the pass-band should not exceed 0.087 dB, and the attenuation in the stop-band should be at least 90 dB.

In the following call to ellip , we could use output='sos' , but for this example, we'll use output='zpk' , and then convert to SOS format with zpk2sos :

>>> from scipy import signal
... z, p, k = signal.ellip(6, 0.087, 90, 1000/(0.5*8000), output='zpk')

Now convert to SOS format.

>>> sos = signal.zpk2sos(z, p, k)

The coefficients of the numerators of the sections:

>>> sos[:, :3]
array([[ 0.0014154 ,  0.00248707,  0.0014154 ],
       [ 1.        ,  0.72965193,  1.        ],
       [ 1.        ,  0.17594966,  1.        ]])

The symmetry in the coefficients occurs because all the zeros are on the unit circle.

The coefficients of the denominators of the sections:

>>> sos[:, 3:]
array([[ 1.        , -1.32543251,  0.46989499],
       [ 1.        , -1.26117915,  0.6262586 ],
       [ 1.        , -1.25707217,  0.86199667]])

The next example shows the effect of the :None:None:`pairing` option. We have a system with three poles and three zeros, so the SOS array will have shape (2, 6). The means there is, in effect, an extra pole and an extra zero at the origin in the SOS representation.

>>> z1 = np.array([-1, -0.5-0.5j, -0.5+0.5j])
... p1 = np.array([0.75, 0.8+0.1j, 0.8-0.1j])

With pairing='nearest' (the default), we obtain

>>> signal.zpk2sos(z1, p1, 1)
array([[ 1.  ,  1.  ,  0.5 ,  1.  , -0.75,  0.  ],
       [ 1.  ,  1.  ,  0.  ,  1.  , -1.6 ,  0.65]])

The first section has the zeros {-0.5-0.05j, -0.5+0.5j} and the poles {0, 0.75}, and the second section has the zeros {-1, 0} and poles {0.8+0.1j, 0.8-0.1j}. Note that the extra pole and zero at the origin have been assigned to different sections.

With pairing='keep_odd' , we obtain:

>>> signal.zpk2sos(z1, p1, 1, pairing='keep_odd')
array([[ 1.  ,  1.  ,  0.  ,  1.  , -0.75,  0.  ],
       [ 1.  ,  1.  ,  0.5 ,  1.  , -1.6 ,  0.65]])

The extra pole and zero at the origin are in the same section. The first section is, in effect, a first-order section.

With pairing='minimal' , the first-order section doesn't have the extra pole and zero at the origin:

>>> signal.zpk2sos(z1, p1, 1, pairing='minimal')
array([[ 0.  ,  1.  ,  1.  ,  0.  ,  1.  , -0.75],
       [ 1.  ,  1.  ,  0.5 ,  1.  , -1.6 ,  0.65]])
See :

Back References

The following pages refer to to this document either explicitly or contain code examples using this.

scipy.signal._ltisys.StateSpace scipy.signal._filter_design.zpk2sos scipy.signal._ltisys.ZerosPolesGainDiscrete scipy.signal._ltisys.ZerosPolesGainContinuous scipy.signal._ltisys.StateSpaceContinuous scipy.signal._signaltools.sosfilt_zi papyri scipy.signal._signaltools.sosfilt scipy.signal._ltisys.ZerosPolesGain scipy.signal._filter_design.tf2sos scipy.signal._ltisys.StateSpaceDiscrete

Local connectivity graph

Hover to see nodes names; edges to Self not shown, Caped at 50 nodes.

Using a canvas is more power efficient and can get hundred of nodes ; but does not allow hyperlinks; , arrows or text (beyond on hover)

SVG is more flexible but power hungry; and does not scale well to 50 + nodes.

All aboves nodes referred to, (or are referred from) current nodes; Edges from Self to other have been omitted (or all nodes would be connected to the central node "self" which is not useful). Nodes are colored by the library they belong to, and scaled with the number of references pointing them


GitHub : /scipy/signal/_filter_design.py#1289
type: <class 'function'>
Commit: