1 # Copyright (C) 2008-2011 W. Trevor King <wking@drexel.edu>
3 # This file is part of tempcontrol.
5 # tempcontrol is free software: you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation, either
8 # version 3 of the License, or (at your option) any later version.
10 # tempcontrol is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with tempcontrol. If not, see
17 # <http://www.gnu.org/licenses/>.
19 "Basic testing for `Controller`\s and `Backend`\s"
23 from . import LOG as _LOG
24 from .backend import get_backend as _get_backend
25 from controller import Controller as _Controller
28 def test_backend(backend=None):
29 internal_backend = False
31 internal_backend = True
32 backend = _get_backend('test')()
34 sp = backend.get_setpoint()
35 _LOG.info('temperature = {:n} C'.format(backend.get_temp()))
36 _LOG.info('setpoint = {:n} C'.format(sp))
37 _LOG.info('current = {:n} A'.format(backend.get_current()))
39 _set_and_check_setpoint(backend=backend, setpoint=5.0)
40 _check_max_current(backend=backend)
41 _set_and_check_setpoint(backend=backend, setpoint=50.0)
42 _check_max_current(backend=backend)
43 _set_and_check_setpoint(backend=backend, setpoint=sp)
48 def _set_and_check_setpoint(backend, setpoint):
49 _LOG.info('setting setpoint to {:n} C'.format(setpoint))
50 c.set_setpoint(setpoint)
52 _LOG.info('setpoint = {:n} C'.format(sp))
54 msg = 'read setpoint {:n} != written setpoint {:n}'.format(
59 def _check_max_current(backend):
60 # give the backend some time to overcome any integral gain
63 _LOG.info('current = {:n} A'.format(cur))
64 mcur = c.get_max_current()
66 temp = backend.get_temp()
67 sp = backend.get_setpoint()
68 msg = ('current of {:n} A is not the max {:n} A, but the system is '
69 'at {:n} C while the setpoint is at {:n}').format(
74 def test_controller_step_response(backend=None, setpoint=25):
75 internal_backend = False
77 internal_backend = True
78 backend = _get_backend('test')()
80 backend.set_mode('PID')
81 c = _Controller(backend=backend)
82 max_current = backend.get_max_current()
83 current_a = 0.4 * max_current
84 current_b = 0.5 * max_current
85 step_response = c.get_step_response(
86 current_a=current_a, current_b=current_b, tolerance=0.5, stable_time=4.)
88 with open('step_response.dat', 'w') as d:
89 s = step_response[0][0]
90 for t,T in step_response:
91 d.write('{:n}\t{:n}\n'.format(t-s, T))
92 gain,dead_time,tau,max_rate = c.analyze_step_response(
93 step_response, current_shift=current_b-current_a)
94 _LOG.debug(('step response: dead time {:n}, gain {:n}, tau {:n}, '
95 'max-rate {:n}').format(dead_time, gain, tau, max_rate))
96 for name,response_fn,modes in [
97 ('Zeigler-Nichols', c.ziegler_nichols_step_response,
99 ('Cohen-Coon', c.cohen_coon_step_response,
100 ['P', 'PI', 'PID']), # 'PD'
101 ('Wang-Juan-Chan', c.wang_juang_chan_step_response,
106 gain=gain, dead_time=dead_time, tau=tau, mode=mode)
108 '{} step response {}: p {:n}, i {:n}, d {:n}'.format(
109 name, mode, p, i, d))
114 def test_controller_bang_bang_response(backend=None, setpoint=25):
115 internal_backend = False
117 internal_backend = True
118 backend = _get_backend('test')(log_stream=open('pid.log', 'w'))
119 # shift our noise-less system off its setpoint
120 backend.set_setpoint(backend.get_temp()+0.1)
122 c = _Controller(backend=backend)
123 dead_band = 3*c.estimate_temperature_sensitivity()
124 bang_bang_response = c.get_bang_bang_response(dead_band=dead_band, num_oscillations=4)
126 with open('bang_bang_response.dat', 'w') as d:
127 s = bang_bang_response[0][0]
128 for t,T in bang_bang_response:
129 d.write('{:n}\t{:n}\n'.format(t-s, T))
130 amplitude,period = c.analyze_bang_bang_response(bang_bang_response)
131 _LOG.debug('bang-bang response: amplitude {:n}, period {:n}'.format(
133 p,i,d = c.ziegler_nichols_bang_bang_response(
134 amplitude=amplitude, period=period, mode='PID')
135 _LOG.debug(('Zeigler-Nichols bang-bang response: '
136 'p {:n}, i {:n}, d {:n}').format(p, i, d))