test_dltisys.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. # Author: Jeffrey Armstrong <jeff@approximatrix.com>
  2. # April 4, 2011
  3. from __future__ import division, print_function, absolute_import
  4. import numpy as np
  5. from numpy.testing import (assert_equal,
  6. assert_array_almost_equal, assert_array_equal,
  7. assert_allclose, assert_, assert_almost_equal)
  8. from pytest import raises as assert_raises
  9. from scipy._lib._numpy_compat import suppress_warnings
  10. from scipy.signal import (dlsim, dstep, dimpulse, tf2zpk, lti, dlti,
  11. StateSpace, TransferFunction, ZerosPolesGain,
  12. dfreqresp, dbode, BadCoefficients)
  13. class TestDLTI(object):
  14. def test_dlsim(self):
  15. a = np.asarray([[0.9, 0.1], [-0.2, 0.9]])
  16. b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]])
  17. c = np.asarray([[0.1, 0.3]])
  18. d = np.asarray([[0.0, -0.1, 0.0]])
  19. dt = 0.5
  20. # Create an input matrix with inputs down the columns (3 cols) and its
  21. # respective time input vector
  22. u = np.hstack((np.asmatrix(np.linspace(0, 4.0, num=5)).transpose(),
  23. 0.01 * np.ones((5, 1)),
  24. -0.002 * np.ones((5, 1))))
  25. t_in = np.linspace(0, 2.0, num=5)
  26. # Define the known result
  27. yout_truth = np.asmatrix([-0.001,
  28. -0.00073,
  29. 0.039446,
  30. 0.0915387,
  31. 0.13195948]).transpose()
  32. xout_truth = np.asarray([[0, 0],
  33. [0.0012, 0.0005],
  34. [0.40233, 0.00071],
  35. [1.163368, -0.079327],
  36. [2.2402985, -0.3035679]])
  37. tout, yout, xout = dlsim((a, b, c, d, dt), u, t_in)
  38. assert_array_almost_equal(yout_truth, yout)
  39. assert_array_almost_equal(xout_truth, xout)
  40. assert_array_almost_equal(t_in, tout)
  41. # Make sure input with single-dimension doesn't raise error
  42. dlsim((1, 2, 3), 4)
  43. # Interpolated control - inputs should have different time steps
  44. # than the discrete model uses internally
  45. u_sparse = u[[0, 4], :]
  46. t_sparse = np.asarray([0.0, 2.0])
  47. tout, yout, xout = dlsim((a, b, c, d, dt), u_sparse, t_sparse)
  48. assert_array_almost_equal(yout_truth, yout)
  49. assert_array_almost_equal(xout_truth, xout)
  50. assert_equal(len(tout), yout.shape[0])
  51. # Transfer functions (assume dt = 0.5)
  52. num = np.asarray([1.0, -0.1])
  53. den = np.asarray([0.3, 1.0, 0.2])
  54. yout_truth = np.asmatrix([0.0,
  55. 0.0,
  56. 3.33333333333333,
  57. -4.77777777777778,
  58. 23.0370370370370]).transpose()
  59. # Assume use of the first column of the control input built earlier
  60. tout, yout = dlsim((num, den, 0.5), u[:, 0], t_in)
  61. assert_array_almost_equal(yout, yout_truth)
  62. assert_array_almost_equal(t_in, tout)
  63. # Retest the same with a 1-D input vector
  64. uflat = np.asarray(u[:, 0])
  65. uflat = uflat.reshape((5,))
  66. tout, yout = dlsim((num, den, 0.5), uflat, t_in)
  67. assert_array_almost_equal(yout, yout_truth)
  68. assert_array_almost_equal(t_in, tout)
  69. # zeros-poles-gain representation
  70. zd = np.array([0.5, -0.5])
  71. pd = np.array([1.j / np.sqrt(2), -1.j / np.sqrt(2)])
  72. k = 1.0
  73. yout_truth = np.asmatrix([0.0, 1.0, 2.0, 2.25, 2.5]).transpose()
  74. tout, yout = dlsim((zd, pd, k, 0.5), u[:, 0], t_in)
  75. assert_array_almost_equal(yout, yout_truth)
  76. assert_array_almost_equal(t_in, tout)
  77. # Raise an error for continuous-time systems
  78. system = lti([1], [1, 1])
  79. assert_raises(AttributeError, dlsim, system, u)
  80. def test_dstep(self):
  81. a = np.asarray([[0.9, 0.1], [-0.2, 0.9]])
  82. b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]])
  83. c = np.asarray([[0.1, 0.3]])
  84. d = np.asarray([[0.0, -0.1, 0.0]])
  85. dt = 0.5
  86. # Because b.shape[1] == 3, dstep should result in a tuple of three
  87. # result vectors
  88. yout_step_truth = (np.asarray([0.0, 0.04, 0.052, 0.0404, 0.00956,
  89. -0.036324, -0.093318, -0.15782348,
  90. -0.226628324, -0.2969374948]),
  91. np.asarray([-0.1, -0.075, -0.058, -0.04815,
  92. -0.04453, -0.0461895, -0.0521812,
  93. -0.061588875, -0.073549579,
  94. -0.08727047595]),
  95. np.asarray([0.0, -0.01, -0.013, -0.0101, -0.00239,
  96. 0.009081, 0.0233295, 0.03945587,
  97. 0.056657081, 0.0742343737]))
  98. tout, yout = dstep((a, b, c, d, dt), n=10)
  99. assert_equal(len(yout), 3)
  100. for i in range(0, len(yout)):
  101. assert_equal(yout[i].shape[0], 10)
  102. assert_array_almost_equal(yout[i].flatten(), yout_step_truth[i])
  103. # Check that the other two inputs (tf, zpk) will work as well
  104. tfin = ([1.0], [1.0, 1.0], 0.5)
  105. yout_tfstep = np.asarray([0.0, 1.0, 0.0])
  106. tout, yout = dstep(tfin, n=3)
  107. assert_equal(len(yout), 1)
  108. assert_array_almost_equal(yout[0].flatten(), yout_tfstep)
  109. zpkin = tf2zpk(tfin[0], tfin[1]) + (0.5,)
  110. tout, yout = dstep(zpkin, n=3)
  111. assert_equal(len(yout), 1)
  112. assert_array_almost_equal(yout[0].flatten(), yout_tfstep)
  113. # Raise an error for continuous-time systems
  114. system = lti([1], [1, 1])
  115. assert_raises(AttributeError, dstep, system)
  116. def test_dimpulse(self):
  117. a = np.asarray([[0.9, 0.1], [-0.2, 0.9]])
  118. b = np.asarray([[0.4, 0.1, -0.1], [0.0, 0.05, 0.0]])
  119. c = np.asarray([[0.1, 0.3]])
  120. d = np.asarray([[0.0, -0.1, 0.0]])
  121. dt = 0.5
  122. # Because b.shape[1] == 3, dimpulse should result in a tuple of three
  123. # result vectors
  124. yout_imp_truth = (np.asarray([0.0, 0.04, 0.012, -0.0116, -0.03084,
  125. -0.045884, -0.056994, -0.06450548,
  126. -0.068804844, -0.0703091708]),
  127. np.asarray([-0.1, 0.025, 0.017, 0.00985, 0.00362,
  128. -0.0016595, -0.0059917, -0.009407675,
  129. -0.011960704, -0.01372089695]),
  130. np.asarray([0.0, -0.01, -0.003, 0.0029, 0.00771,
  131. 0.011471, 0.0142485, 0.01612637,
  132. 0.017201211, 0.0175772927]))
  133. tout, yout = dimpulse((a, b, c, d, dt), n=10)
  134. assert_equal(len(yout), 3)
  135. for i in range(0, len(yout)):
  136. assert_equal(yout[i].shape[0], 10)
  137. assert_array_almost_equal(yout[i].flatten(), yout_imp_truth[i])
  138. # Check that the other two inputs (tf, zpk) will work as well
  139. tfin = ([1.0], [1.0, 1.0], 0.5)
  140. yout_tfimpulse = np.asarray([0.0, 1.0, -1.0])
  141. tout, yout = dimpulse(tfin, n=3)
  142. assert_equal(len(yout), 1)
  143. assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse)
  144. zpkin = tf2zpk(tfin[0], tfin[1]) + (0.5,)
  145. tout, yout = dimpulse(zpkin, n=3)
  146. assert_equal(len(yout), 1)
  147. assert_array_almost_equal(yout[0].flatten(), yout_tfimpulse)
  148. # Raise an error for continuous-time systems
  149. system = lti([1], [1, 1])
  150. assert_raises(AttributeError, dimpulse, system)
  151. def test_dlsim_trivial(self):
  152. a = np.array([[0.0]])
  153. b = np.array([[0.0]])
  154. c = np.array([[0.0]])
  155. d = np.array([[0.0]])
  156. n = 5
  157. u = np.zeros(n).reshape(-1, 1)
  158. tout, yout, xout = dlsim((a, b, c, d, 1), u)
  159. assert_array_equal(tout, np.arange(float(n)))
  160. assert_array_equal(yout, np.zeros((n, 1)))
  161. assert_array_equal(xout, np.zeros((n, 1)))
  162. def test_dlsim_simple1d(self):
  163. a = np.array([[0.5]])
  164. b = np.array([[0.0]])
  165. c = np.array([[1.0]])
  166. d = np.array([[0.0]])
  167. n = 5
  168. u = np.zeros(n).reshape(-1, 1)
  169. tout, yout, xout = dlsim((a, b, c, d, 1), u, x0=1)
  170. assert_array_equal(tout, np.arange(float(n)))
  171. expected = (0.5 ** np.arange(float(n))).reshape(-1, 1)
  172. assert_array_equal(yout, expected)
  173. assert_array_equal(xout, expected)
  174. def test_dlsim_simple2d(self):
  175. lambda1 = 0.5
  176. lambda2 = 0.25
  177. a = np.array([[lambda1, 0.0],
  178. [0.0, lambda2]])
  179. b = np.array([[0.0],
  180. [0.0]])
  181. c = np.array([[1.0, 0.0],
  182. [0.0, 1.0]])
  183. d = np.array([[0.0],
  184. [0.0]])
  185. n = 5
  186. u = np.zeros(n).reshape(-1, 1)
  187. tout, yout, xout = dlsim((a, b, c, d, 1), u, x0=1)
  188. assert_array_equal(tout, np.arange(float(n)))
  189. # The analytical solution:
  190. expected = (np.array([lambda1, lambda2]) **
  191. np.arange(float(n)).reshape(-1, 1))
  192. assert_array_equal(yout, expected)
  193. assert_array_equal(xout, expected)
  194. def test_more_step_and_impulse(self):
  195. lambda1 = 0.5
  196. lambda2 = 0.75
  197. a = np.array([[lambda1, 0.0],
  198. [0.0, lambda2]])
  199. b = np.array([[1.0, 0.0],
  200. [0.0, 1.0]])
  201. c = np.array([[1.0, 1.0]])
  202. d = np.array([[0.0, 0.0]])
  203. n = 10
  204. # Check a step response.
  205. ts, ys = dstep((a, b, c, d, 1), n=n)
  206. # Create the exact step response.
  207. stp0 = (1.0 / (1 - lambda1)) * (1.0 - lambda1 ** np.arange(n))
  208. stp1 = (1.0 / (1 - lambda2)) * (1.0 - lambda2 ** np.arange(n))
  209. assert_allclose(ys[0][:, 0], stp0)
  210. assert_allclose(ys[1][:, 0], stp1)
  211. # Check an impulse response with an initial condition.
  212. x0 = np.array([1.0, 1.0])
  213. ti, yi = dimpulse((a, b, c, d, 1), n=n, x0=x0)
  214. # Create the exact impulse response.
  215. imp = (np.array([lambda1, lambda2]) **
  216. np.arange(-1, n + 1).reshape(-1, 1))
  217. imp[0, :] = 0.0
  218. # Analytical solution to impulse response
  219. y0 = imp[:n, 0] + np.dot(imp[1:n + 1, :], x0)
  220. y1 = imp[:n, 1] + np.dot(imp[1:n + 1, :], x0)
  221. assert_allclose(yi[0][:, 0], y0)
  222. assert_allclose(yi[1][:, 0], y1)
  223. # Check that dt=0.1, n=3 gives 3 time values.
  224. system = ([1.0], [1.0, -0.5], 0.1)
  225. t, (y,) = dstep(system, n=3)
  226. assert_allclose(t, [0, 0.1, 0.2])
  227. assert_array_equal(y.T, [[0, 1.0, 1.5]])
  228. t, (y,) = dimpulse(system, n=3)
  229. assert_allclose(t, [0, 0.1, 0.2])
  230. assert_array_equal(y.T, [[0, 1, 0.5]])
  231. class TestDlti(object):
  232. def test_dlti_instantiation(self):
  233. # Test that lti can be instantiated.
  234. dt = 0.05
  235. # TransferFunction
  236. s = dlti([1], [-1], dt=dt)
  237. assert_(isinstance(s, TransferFunction))
  238. assert_(isinstance(s, dlti))
  239. assert_(not isinstance(s, lti))
  240. assert_equal(s.dt, dt)
  241. # ZerosPolesGain
  242. s = dlti(np.array([]), np.array([-1]), 1, dt=dt)
  243. assert_(isinstance(s, ZerosPolesGain))
  244. assert_(isinstance(s, dlti))
  245. assert_(not isinstance(s, lti))
  246. assert_equal(s.dt, dt)
  247. # StateSpace
  248. s = dlti([1], [-1], 1, 3, dt=dt)
  249. assert_(isinstance(s, StateSpace))
  250. assert_(isinstance(s, dlti))
  251. assert_(not isinstance(s, lti))
  252. assert_equal(s.dt, dt)
  253. # Number of inputs
  254. assert_raises(ValueError, dlti, 1)
  255. assert_raises(ValueError, dlti, 1, 1, 1, 1, 1)
  256. class TestStateSpaceDisc(object):
  257. def test_initialization(self):
  258. # Check that all initializations work
  259. dt = 0.05
  260. s = StateSpace(1, 1, 1, 1, dt=dt)
  261. s = StateSpace([1], [2], [3], [4], dt=dt)
  262. s = StateSpace(np.array([[1, 2], [3, 4]]), np.array([[1], [2]]),
  263. np.array([[1, 0]]), np.array([[0]]), dt=dt)
  264. s = StateSpace(1, 1, 1, 1, dt=True)
  265. def test_conversion(self):
  266. # Check the conversion functions
  267. s = StateSpace(1, 2, 3, 4, dt=0.05)
  268. assert_(isinstance(s.to_ss(), StateSpace))
  269. assert_(isinstance(s.to_tf(), TransferFunction))
  270. assert_(isinstance(s.to_zpk(), ZerosPolesGain))
  271. # Make sure copies work
  272. assert_(StateSpace(s) is not s)
  273. assert_(s.to_ss() is not s)
  274. def test_properties(self):
  275. # Test setters/getters for cross class properties.
  276. # This implicitly tests to_tf() and to_zpk()
  277. # Getters
  278. s = StateSpace(1, 1, 1, 1, dt=0.05)
  279. assert_equal(s.poles, [1])
  280. assert_equal(s.zeros, [0])
  281. class TestTransferFunction(object):
  282. def test_initialization(self):
  283. # Check that all initializations work
  284. dt = 0.05
  285. s = TransferFunction(1, 1, dt=dt)
  286. s = TransferFunction([1], [2], dt=dt)
  287. s = TransferFunction(np.array([1]), np.array([2]), dt=dt)
  288. s = TransferFunction(1, 1, dt=True)
  289. def test_conversion(self):
  290. # Check the conversion functions
  291. s = TransferFunction([1, 0], [1, -1], dt=0.05)
  292. assert_(isinstance(s.to_ss(), StateSpace))
  293. assert_(isinstance(s.to_tf(), TransferFunction))
  294. assert_(isinstance(s.to_zpk(), ZerosPolesGain))
  295. # Make sure copies work
  296. assert_(TransferFunction(s) is not s)
  297. assert_(s.to_tf() is not s)
  298. def test_properties(self):
  299. # Test setters/getters for cross class properties.
  300. # This implicitly tests to_ss() and to_zpk()
  301. # Getters
  302. s = TransferFunction([1, 0], [1, -1], dt=0.05)
  303. assert_equal(s.poles, [1])
  304. assert_equal(s.zeros, [0])
  305. class TestZerosPolesGain(object):
  306. def test_initialization(self):
  307. # Check that all initializations work
  308. dt = 0.05
  309. s = ZerosPolesGain(1, 1, 1, dt=dt)
  310. s = ZerosPolesGain([1], [2], 1, dt=dt)
  311. s = ZerosPolesGain(np.array([1]), np.array([2]), 1, dt=dt)
  312. s = ZerosPolesGain(1, 1, 1, dt=True)
  313. def test_conversion(self):
  314. # Check the conversion functions
  315. s = ZerosPolesGain(1, 2, 3, dt=0.05)
  316. assert_(isinstance(s.to_ss(), StateSpace))
  317. assert_(isinstance(s.to_tf(), TransferFunction))
  318. assert_(isinstance(s.to_zpk(), ZerosPolesGain))
  319. # Make sure copies work
  320. assert_(ZerosPolesGain(s) is not s)
  321. assert_(s.to_zpk() is not s)
  322. class Test_dfreqresp(object):
  323. def test_manual(self):
  324. # Test dfreqresp() real part calculation (manual sanity check).
  325. # 1st order low-pass filter: H(z) = 1 / (z - 0.2),
  326. system = TransferFunction(1, [1, -0.2], dt=0.1)
  327. w = [0.1, 1, 10]
  328. w, H = dfreqresp(system, w=w)
  329. # test real
  330. expected_re = [1.2383, 0.4130, -0.7553]
  331. assert_almost_equal(H.real, expected_re, decimal=4)
  332. # test imag
  333. expected_im = [-0.1555, -1.0214, 0.3955]
  334. assert_almost_equal(H.imag, expected_im, decimal=4)
  335. def test_auto(self):
  336. # Test dfreqresp() real part calculation.
  337. # 1st order low-pass filter: H(z) = 1 / (z - 0.2),
  338. system = TransferFunction(1, [1, -0.2], dt=0.1)
  339. w = [0.1, 1, 10, 100]
  340. w, H = dfreqresp(system, w=w)
  341. jw = np.exp(w * 1j)
  342. y = np.polyval(system.num, jw) / np.polyval(system.den, jw)
  343. # test real
  344. expected_re = y.real
  345. assert_almost_equal(H.real, expected_re)
  346. # test imag
  347. expected_im = y.imag
  348. assert_almost_equal(H.imag, expected_im)
  349. def test_freq_range(self):
  350. # Test that freqresp() finds a reasonable frequency range.
  351. # 1st order low-pass filter: H(z) = 1 / (z - 0.2),
  352. # Expected range is from 0.01 to 10.
  353. system = TransferFunction(1, [1, -0.2], dt=0.1)
  354. n = 10
  355. expected_w = np.linspace(0, np.pi, 10, endpoint=False)
  356. w, H = dfreqresp(system, n=n)
  357. assert_almost_equal(w, expected_w)
  358. def test_pole_one(self):
  359. # Test that freqresp() doesn't fail on a system with a pole at 0.
  360. # integrator, pole at zero: H(s) = 1 / s
  361. system = TransferFunction([1], [1, -1], dt=0.1)
  362. with suppress_warnings() as sup:
  363. sup.filter(RuntimeWarning, message="divide by zero")
  364. sup.filter(RuntimeWarning, message="invalid value encountered")
  365. w, H = dfreqresp(system, n=2)
  366. assert_equal(w[0], 0.) # a fail would give not-a-number
  367. def test_error(self):
  368. # Raise an error for continuous-time systems
  369. system = lti([1], [1, 1])
  370. assert_raises(AttributeError, dfreqresp, system)
  371. def test_from_state_space(self):
  372. # H(z) = 2 / z^3 - 0.5 * z^2
  373. system_TF = dlti([2], [1, -0.5, 0, 0])
  374. A = np.array([[0.5, 0, 0],
  375. [1, 0, 0],
  376. [0, 1, 0]])
  377. B = np.array([[1, 0, 0]]).T
  378. C = np.array([[0, 0, 2]])
  379. D = 0
  380. system_SS = dlti(A, B, C, D)
  381. w = 10.0**np.arange(-3,0,.5)
  382. with suppress_warnings() as sup:
  383. sup.filter(BadCoefficients)
  384. w1, H1 = dfreqresp(system_TF, w=w)
  385. w2, H2 = dfreqresp(system_SS, w=w)
  386. assert_almost_equal(H1, H2)
  387. def test_from_zpk(self):
  388. # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2),
  389. system_ZPK = dlti([],[0.2],0.3)
  390. system_TF = dlti(0.3, [1, -0.2])
  391. w = [0.1, 1, 10, 100]
  392. w1, H1 = dfreqresp(system_ZPK, w=w)
  393. w2, H2 = dfreqresp(system_TF, w=w)
  394. assert_almost_equal(H1, H2)
  395. class Test_bode(object):
  396. def test_manual(self):
  397. # Test bode() magnitude calculation (manual sanity check).
  398. # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2),
  399. dt = 0.1
  400. system = TransferFunction(0.3, [1, -0.2], dt=dt)
  401. w = [0.1, 0.5, 1, np.pi]
  402. w2, mag, phase = dbode(system, w=w)
  403. # Test mag
  404. expected_mag = [-8.5329, -8.8396, -9.6162, -12.0412]
  405. assert_almost_equal(mag, expected_mag, decimal=4)
  406. # Test phase
  407. expected_phase = [-7.1575, -35.2814, -67.9809, -180.0000]
  408. assert_almost_equal(phase, expected_phase, decimal=4)
  409. # Test frequency
  410. assert_equal(np.array(w) / dt, w2)
  411. def test_auto(self):
  412. # Test bode() magnitude calculation.
  413. # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2),
  414. system = TransferFunction(0.3, [1, -0.2], dt=0.1)
  415. w = np.array([0.1, 0.5, 1, np.pi])
  416. w2, mag, phase = dbode(system, w=w)
  417. jw = np.exp(w * 1j)
  418. y = np.polyval(system.num, jw) / np.polyval(system.den, jw)
  419. # Test mag
  420. expected_mag = 20.0 * np.log10(abs(y))
  421. assert_almost_equal(mag, expected_mag)
  422. # Test phase
  423. expected_phase = np.rad2deg(np.angle(y))
  424. assert_almost_equal(phase, expected_phase)
  425. def test_range(self):
  426. # Test that bode() finds a reasonable frequency range.
  427. # 1st order low-pass filter: H(s) = 0.3 / (z - 0.2),
  428. dt = 0.1
  429. system = TransferFunction(0.3, [1, -0.2], dt=0.1)
  430. n = 10
  431. # Expected range is from 0.01 to 10.
  432. expected_w = np.linspace(0, np.pi, n, endpoint=False) / dt
  433. w, mag, phase = dbode(system, n=n)
  434. assert_almost_equal(w, expected_w)
  435. def test_pole_one(self):
  436. # Test that freqresp() doesn't fail on a system with a pole at 0.
  437. # integrator, pole at zero: H(s) = 1 / s
  438. system = TransferFunction([1], [1, -1], dt=0.1)
  439. with suppress_warnings() as sup:
  440. sup.filter(RuntimeWarning, message="divide by zero")
  441. sup.filter(RuntimeWarning, message="invalid value encountered")
  442. w, mag, phase = dbode(system, n=2)
  443. assert_equal(w[0], 0.) # a fail would give not-a-number
  444. def test_imaginary(self):
  445. # bode() should not fail on a system with pure imaginary poles.
  446. # The test passes if bode doesn't raise an exception.
  447. system = TransferFunction([1], [1, 0, 100], dt=0.1)
  448. dbode(system, n=2)
  449. def test_error(self):
  450. # Raise an error for continuous-time systems
  451. system = lti([1], [1, 1])
  452. assert_raises(AttributeError, dbode, system)
  453. class TestTransferFunctionZConversion(object):
  454. """Test private conversions between 'z' and 'z**-1' polynomials."""
  455. def test_full(self):
  456. # Numerator and denominator same order
  457. num = [2, 3, 4]
  458. den = [5, 6, 7]
  459. num2, den2 = TransferFunction._z_to_zinv(num, den)
  460. assert_equal(num, num2)
  461. assert_equal(den, den2)
  462. num2, den2 = TransferFunction._zinv_to_z(num, den)
  463. assert_equal(num, num2)
  464. assert_equal(den, den2)
  465. def test_numerator(self):
  466. # Numerator lower order than denominator
  467. num = [2, 3]
  468. den = [5, 6, 7]
  469. num2, den2 = TransferFunction._z_to_zinv(num, den)
  470. assert_equal([0, 2, 3], num2)
  471. assert_equal(den, den2)
  472. num2, den2 = TransferFunction._zinv_to_z(num, den)
  473. assert_equal([2, 3, 0], num2)
  474. assert_equal(den, den2)
  475. def test_denominator(self):
  476. # Numerator higher order than denominator
  477. num = [2, 3, 4]
  478. den = [5, 6]
  479. num2, den2 = TransferFunction._z_to_zinv(num, den)
  480. assert_equal(num, num2)
  481. assert_equal([0, 5, 6], den2)
  482. num2, den2 = TransferFunction._zinv_to_z(num, den)
  483. assert_equal(num, num2)
  484. assert_equal([5, 6, 0], den2)