@@ -120,9 +120,12 @@ class InputOutputResponse:
120120
121121 Methods
122122 -------
123- plot(**kwargs)
123+ plot(**kwargs) [NOT IMPLEMENTED]
124124 Plot the input/output response. Keywords are passed to matplotlib.
125125
126+ set_defaults(**kwargs) [NOT IMPLEMENTED]
127+ Set the default values for accessing the input/output data.
128+
126129 Examples
127130 --------
128131 >>> sys = ct.rss(4, 2, 2)
@@ -144,7 +147,6 @@ class InputOutputResponse:
144147
145148 t, y = step_response(sys)
146149 t, y, x = step_response(sys, return_x=True)
147- t, y, x, u = step_response(sys, return_x=True, return_u=True)
148150
149151 2. For backward compatibility with earlier version of python-control,
150152 this class has ``__getitem__`` and ``__len__`` methods that allow the
@@ -154,9 +156,9 @@ class InputOutputResponse:
154156 response[1]: returns the output vector
155157 response[2]: returns the state vector
156158
157- If the index is two-dimensional, a new ``InputOutputResponse`` object
158- is returned that corresponds to the specified subset of input/output
159- responses.
159+ 3. If a response is indexed using a two-dimensional tuple , a new
160+ ``InputOutputResponse`` object is returned that corresponds to the
161+ specified subset of input/output responses. [NOT IMPLEMENTED]
160162
161163 """
162164
@@ -169,26 +171,46 @@ def __init__(
169171
170172 Parameters
171173 ----------
172- sys : LTI or InputOutputSystem
173- System that generated the data (used to check if SISO/MIMO).
174-
175- T : 1D array
174+ t : 1D array
176175 Time values of the output. Ignored if None.
177176
178- yout : ndarray
179- Response of the system. This can either be a 1D array indexed
180- by time (for SISO systems), a 2D array indexed by output and
181- time (for MIMO systems with no input indexing, such as
182- initial_response or forced response) or a 3D array indexed by
183- output, input, and time.
177+ y : ndarray
178+ Output response of the system. This can either be a 1D array
179+ indexed by time (for SISO systems or MISO systems with a specified
180+ input), a 2D array indexed by output and time (for MIMO systems
181+ with no input indexing, such as initial_response or forced
182+ response) or a 3D array indexed by output, input, and time.
183+
184+ x : array, optional
185+ Individual response of each state variable. This should be a 2D
186+ array indexed by the state index and time (for single input
187+ systems) or a 3D array indexed by state, input, and time.
188+
189+ u : array, optional
190+ Inputs used to generate the output. This can either be a 1D array
191+ indexed by time (for SISO systems or MISO/MIMO systems with a
192+ specified input) or a 2D array indexed by input and time.
184193
185- xout : array, optional
186- Individual response of each x variable (if return_x is
187- True). For a SISO system (or if a single input is specified),
188- this should be a 2D array indexed by the state index and time
189- (for single input systems) or a 3D array indexed by state,
190- input, and time. Ignored if None.
194+ sys : LTI or InputOutputSystem, optional
195+ System that generated the data. If desired, the system used to
196+ generate the data can be stored along with the data.
191197
198+ squeeze : bool, optional
199+ By default, if a system is single-input, single-output (SISO) then
200+ the inputs and outputs are returned as a 1D array (indexed by
201+ time) and if a system is multi-input or multi-output, the the
202+ inputs are returned as a 2D array (indexed by input and time) and
203+ the outputs are returned as a 3D array (indexed by output, input,
204+ and time). If squeeze=True, access to the output response will
205+ remove single-dimensional entries from the shape of the inputs and
206+ outputs even if the system is not SISO. If squeeze=False, keep the
207+ input as a 2D array (indexed by the input and time) and the output
208+ as a 3D array (indexed by the output, input, and time) even if the
209+ system is SISO. The default value can be set using
210+ config.defaults['control.squeeze_time_response'].
211+
212+ Additional parameters
213+ ---------------------
192214 transpose : bool, optional
193215 If True, transpose all input and output arrays (for backward
194216 compatibility with MATLAB and :func:`scipy.signal.lsim`).
@@ -197,15 +219,6 @@ def __init__(
197219 return_x : bool, optional
198220 If True, return the state vector (default = False).
199221
200- squeeze : bool, optional
201- By default, if a system is single-input, single-output (SISO) then
202- the output response is returned as a 1D array (indexed by time).
203- If squeeze=True, remove single-dimensional entries from the shape
204- of the output even if the system is not SISO. If squeeze=False,
205- keep the output as a 3D array (indexed by the output, input, and
206- time) even if the system is SISO. The default value can be set
207- using config.defaults['control.squeeze_time_response'].
208-
209222 input : int, optional
210223 If present, the response represents only the listed input.
211224
@@ -216,10 +229,6 @@ def __init__(
216229 #
217230 # Process and store the basic input/output elements
218231 #
219- t , y , x = _process_time_response (
220- sys , t , y , x ,
221- transpose = transpose , return_x = True , squeeze = squeeze ,
222- input = input , output = output )
223232
224233 # Time vector
225234 self .t = np .atleast_1d (t )
@@ -229,30 +238,36 @@ def __init__(
229238 # Output vector
230239 self .yout = np .array (y )
231240 self .noutputs = 1 if len (self .yout .shape ) < 2 else self .yout .shape [0 ]
232- self . ninputs = 1 if len ( self .yout .shape ) < 3 else self .yout .shape [- 2 ]
233- # TODO: Check to make sure time points match
241+ if self .t .shape [ - 1 ] != self .yout .shape [- 1 ]:
242+ raise ValueError ( "Output vector does not match time vector" )
234243
235244 # State vector
236245 self .xout = np .array (x )
237- self .nstates = self .xout .shape [0 ]
238- # TODO: Check to make sure time points match
246+ self .nstates = 0 if self .xout is None else self .xout .shape [0 ]
247+ if self .t .shape [- 1 ] != self .xout .shape [- 1 ]:
248+ raise ValueError ("State vector does not match time vector" )
239249
240250 # Input vector
241251 self .uout = np .array (u )
242- # TODO: Check to make sure input shape is OK
243- # TODO: Check to make sure time points match
252+ if len (self .uout .shape ) != 0 :
253+ self .ninputs = 1 if len (self .uout .shape ) < 2 \
254+ else self .uout .shape [- 2 ]
255+ if self .t .shape [- 1 ] != self .uout .shape [- 1 ]:
256+ raise ValueError ("Input vector does not match time vector" )
257+ else :
258+ self .ninputs = 0
244259
245260 # If the system was specified, make sure it is compatible
246261 if sys is not None :
247- if sys .ninputs != self .ninputs :
248- ValueError ("System inputs do not match response data" )
249262 if sys .noutputs != self .noutputs :
250263 ValueError ("System outputs do not match response data" )
251264 if sys .nstates != self .nstates :
252265 ValueError ("System states do not match response data" )
253266 self .sys = sys
254267
255268 # Keep track of whether to squeeze inputs, outputs, and states
269+ if not (squeeze is True or squeeze is None or squeeze is False ):
270+ raise ValueError ("unknown squeeze value" )
256271 self .squeeze = squeeze
257272
258273 # Store legacy keyword values (only needed for legacy interface)
@@ -263,12 +278,29 @@ def __init__(
263278 # Getter for output (implements squeeze processing)
264279 @property
265280 def y (self ):
266- return self .yout
281+ t , y = _process_time_response (
282+ self .sys , self .t , self .yout , None ,
283+ transpose = self .transpose , return_x = False , squeeze = self .squeeze ,
284+ input = self .input , output = self .output )
285+ return y
267286
268287 # Getter for state (implements squeeze processing)
269288 @property
270289 def x (self ):
271- return self .xout
290+ t , y , x = _process_time_response (
291+ self .sys , self .t , self .yout , self .xout ,
292+ transpose = self .transpose , return_x = True , squeeze = self .squeeze ,
293+ input = self .input , output = self .output )
294+ return x
295+
296+ # Getter for state (implements squeeze processing)
297+ @property
298+ def u (self ):
299+ t , y = _process_time_response (
300+ self .sys , self .t , self .uout , None ,
301+ transpose = self .transpose , return_x = False , squeeze = self .squeeze ,
302+ input = self .input , output = self .output )
303+ return x
272304
273305 # Implement iter to allow assigning to a tuple
274306 def __iter__ (self ):
@@ -685,6 +717,9 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
685717 tout = T # Return exact list of time steps
686718 yout = yout [::inc , :]
687719 xout = xout [::inc , :]
720+ else :
721+ # Interpolate the input to get the right number of points
722+ U = sp .interpolate .interp1d (T , U )(tout )
688723
689724 # Transpose the output and state vectors to match local convention
690725 xout = np .transpose (xout )
0 commit comments