@@ -561,3 +561,36 @@ def test_c2d_matched(num, den, dt, method):
561561 for czero in sys_ct .zeros ():
562562 zzero = zzeros [(np .abs (zzeros - cmath .exp (czero * dt ))).argmin ()]
563563 assert cmath .isclose (cmath .exp (czero * dt ), zzero )
564+
565+
566+ @pytest .mark .parametrize ("num, den" , [
567+ ([1. ], [1. , 0. ]), # integrator (pole at s = 0)
568+ ([1. ], [1. , 0. , 0. ]), # double integrator
569+ ([2. , 5. ], [1. , 0. ]), # PI controller
570+ ([1. ], [1. , 1. , 0. ]), # type 1 plant, 1/(s(s+1))
571+ ([1. , 0. ], [1. , 2. , 5. ]), # differentiator (zero at s = 0)
572+ ([2. , 0. , 0. ], [1. , 3. , 3. , 1. ]), # double zero at s = 0
573+ ([1. , 0. ], [1. , 0. , 0. ]), # zero and pole at s = 0
574+ ])
575+ @pytest .mark .parametrize ("dt" , [0.1 , 0.5 ])
576+ def test_c2d_matched_origin (num , den , dt ):
577+ # A pole or zero at s = 0 (integrators, PI/PID, type-1/2 plants) used to
578+ # give an all-NaN numerator: the DC-gain match divides by the vanishing
579+ # 1 - z factor of the origin pole/zero (#950, #951).
580+ sys_ct = ct .tf (num , den )
581+ sys_dt = ct .sample_system (sys_ct , dt , method = 'matched' )
582+ assert np .all (np .isfinite (sys_dt .num [0 ][0 ]))
583+ assert np .all (np .isfinite (sys_dt .den [0 ][0 ]))
584+ # the gain is matched just off the origin, so |G_d(e^jwT)| -> |G_c(jw)|
585+ w = 1e-3 / dt
586+ assert np .isclose (abs (sys_ct (1j * w )),
587+ abs (sys_dt (cmath .exp (1j * w * dt ))), rtol = 1e-4 )
588+
589+
590+ @pytest .mark .parametrize ("dt" , [0.1 , 0.5 , 2 ])
591+ @pytest .mark .parametrize ("k" , [1 , 2 , 3 ])
592+ def test_c2d_matched_integrator (k , dt ):
593+ # matched discretization of 1/s**k is the textbook Ts**k / (z - 1)**k
594+ sys_dt = ct .tf ([1. ], [1. ] + [0. ] * k ).sample (dt , method = 'matched' )
595+ np .testing .assert_allclose (sys_dt .num [0 ][0 ], [dt ** k ])
596+ np .testing .assert_allclose (sys_dt .den [0 ][0 ], np .poly ([1. ] * k ))
0 commit comments