1 # Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
3 # This file is part of pypid.
5 # pypid is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
10 # pypid is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pypid. If not, see <http://www.gnu.org/licenses/>.
17 "Basic testing for `Controller`\s and `Backend`\s"
21 from . import LOG as _LOG
22 from . import rules as _rules
23 from .backend import get_backend as _get_backend
24 from controller import Controller as _Controller
27 def test_backend(backend=None):
28 internal_backend = False
30 internal_backend = True
31 backend = _get_backend('test')()
33 sp = backend.get_setpoint()
34 _LOG.info('PV = {:n} {}'.format(backend.get_pv(), backend.pv_units))
35 _LOG.info('SP = {:n} {}'.format(sp, backend.pv_units))
36 _LOG.info('MV = {:n} {}'.format(backend.get_mv(), backend.mv_units))
38 _set_and_check_setpoint(backend=backend, setpoint=5.0)
39 _check_max_mv(backend=backend)
40 _set_and_check_setpoint(backend=backend, setpoint=50.0)
41 _check_max_mv(backend=backend)
42 _set_and_check_setpoint(backend=backend, setpoint=sp)
47 def _set_and_check_setpoint(backend, setpoint):
48 _LOG.info('setting setpoint to {:n} {}'.format(setpoint, backend.pv_units))
49 c.set_setpoint(setpoint)
51 _LOG.info('SP = {:n} {}'.format(sp, backend.pv_units))
53 msg = 'read setpoint {:n} != written setpoint {:n}'.format(
58 def _check_max_current(backend):
59 # give the backend some time to overcome any integral gain
62 _LOG.info('MV = {:n} {}'.format(MV, backend.mv_units))
66 SP = backend.get_setpoint()
67 PVu = backend.pv_units
68 MVu = backend.mv_units
69 msg = ('current of {:n} {} is not the max {:n} {}, but the system is '
70 'at {:n} {} while the setpoint is at {:n} {}').format(
71 MV, MVu, mMV, MVu, PV, PVu, SP, PVu)
75 def test_controller_step_response(backend=None, setpoint=1):
76 internal_backend = False
78 internal_backend = True
79 backend = _get_backend('test')()
81 backend.set_mode('PID')
82 c = _Controller(backend=backend)
83 max_MV = backend.get_max_mv()
86 step_response = c.get_step_response(
87 mv_a=MVa, mv_b=MVb, tolerance=0.5, stable_time=4.)
89 with open('step_response.dat', 'w') as d:
90 s = step_response[0][0]
91 for t,PV in step_response:
92 d.write('{:n}\t{:n}\n'.format(t-s, PV))
93 process_gain,dead_time,decay_time,max_rate = c.analyze_step_response(
94 step_response, mv_shift=MVb-MVa)
95 _LOG.debug(('step response: process gain {:n}, dead time {:n}, decay '
96 'time {:n}, max-rate {:n}').format(
97 process_gain, dead_time, decay_time, max_rate))
99 for name,response_fn,modes in [
100 ('Zeigler-Nichols', r.ziegler_nichols_step_response,
102 ('Cohen-Coon', r.cohen_coon_step_response,
103 ['P', 'PI', 'PID']), # 'PD'
104 ('Wang-Juan-Chan', r.wang_juang_chan_step_response,
109 process_gain=process_gain, dead_time=dead_time,
110 decay_time=decay_time, mode=mode)
112 '{} step response {}: p {:n}, i {:n}, d {:n}'.format(
113 name, mode, p, i, d))
118 def test_controller_bang_bang_response(backend=None, setpoint=1):
119 internal_backend = False
121 internal_backend = True
122 backend = _get_backend('test')()
124 backend.set_setpoint(setpoint)
125 c = _Controller(backend=backend)
126 dead_band = 3*c.estimate_pv_sensitivity()
127 bang_bang_response = c.get_bang_bang_response(dead_band=dead_band)
129 with open('bang_bang_response.dat', 'w') as d:
130 s = bang_bang_response[0][0]
131 for t,T in bang_bang_response:
132 d.write('{:n}\t{:n}\n'.format(t-s, T))
133 amplitude,period = c.analyze_bang_bang_response(bang_bang_response)
134 _LOG.debug('bang-bang response: amplitude {:n}, period {:n}'.format(
136 for name,response_fn,modes in [
137 ('Zeigler-Nichols', r.ziegler_nichols_bang_bang_response,
142 amplitude=amplitude, period=period, mode=mode)
144 '{} bang-bang response {}: p {:n}, i {:n}, d {:n}'.format(
145 name, mode, p, i, d))