|
83 | 83 | 'impulse_response'] |
84 | 84 |
|
85 | 85 |
|
| 86 | +class InputOutputResponse: |
| 87 | + """Class for returning time responses |
| 88 | +
|
| 89 | + This class maintains and manipulates the data corresponding to the |
| 90 | + temporal response of an input/output system. It is used as the return |
| 91 | + type for time domain simulations (step response, input/output response, |
| 92 | + etc). |
| 93 | +
|
| 94 | + Attributes |
| 95 | + ---------- |
| 96 | + t : array |
| 97 | + Time values of the output. |
| 98 | +
|
| 99 | + y : array |
| 100 | + Response of the system, indexed by the output number and time. |
| 101 | +
|
| 102 | + x : array |
| 103 | + Time evolution of the state vector, indexed by state number and time. |
| 104 | +
|
| 105 | + u : array |
| 106 | + Input to the system, indexed by the input number and time. |
| 107 | +
|
| 108 | + Methods |
| 109 | + ------- |
| 110 | + plot(**kwargs) |
| 111 | + Plot the input/output response. Keywords are passed to matplotlib. |
| 112 | +
|
| 113 | + Notes |
| 114 | + ----- |
| 115 | + 1. For backward compatibility with earlier versions of python-control, |
| 116 | + this class has an ``__iter__`` method that allows it to be assigned |
| 117 | + to a tuple with a variable number of elements. This allows the |
| 118 | + following patterns to work: |
| 119 | +
|
| 120 | + t, y = step_response(sys) |
| 121 | + t, y, x = step_response(sys, return_x=True) |
| 122 | + t, y, x, u = step_response(sys, return_x=True, return_u=True) |
| 123 | +
|
| 124 | + 2. For backward compatibility with earlier version of python-control, |
| 125 | + this class has ``__getitem__`` and ``__len__`` methods that allow the |
| 126 | + return value to be indexed: |
| 127 | +
|
| 128 | + response[0]: returns the time vector |
| 129 | + response[1]: returns the output vector |
| 130 | + response[2]: returns the state vector |
| 131 | +
|
| 132 | + If the index is two-dimensional, a new ``InputOutputResponse`` object |
| 133 | + is returned that corresponds to the specified subset of input/output |
| 134 | + responses. |
| 135 | +
|
| 136 | + """ |
| 137 | + |
| 138 | + def __init__( |
| 139 | + self, t, y, x, u, sys=None, dt=None, |
| 140 | + return_x=False, squeeze=None # for legacy interface |
| 141 | + ): |
| 142 | + # Store response attributes |
| 143 | + self.t, self.y, self.x = t, y, x |
| 144 | + |
| 145 | + # Store legacy keyword values (only used for legacy interface) |
| 146 | + self.return_x, self.squeeze = return_x, squeeze |
| 147 | + |
| 148 | + # Implement iter to allow assigning to a tuple |
| 149 | + def __iter__(self): |
| 150 | + if not self.return_x: |
| 151 | + return iter((self.t, self.y)) |
| 152 | + return iter((self.t, self.y, self.x)) |
| 153 | + |
| 154 | + # Implement (thin) getitem to allow access via legacy indexing |
| 155 | + def __getitem__(self, index): |
| 156 | + # See if we were passed a slice |
| 157 | + if isinstance(index, slice): |
| 158 | + if (index.start is None or index.start == 0) and index.stop == 2: |
| 159 | + return (self.t, self.y) |
| 160 | + |
| 161 | + # Otherwise assume we were passed a single index |
| 162 | + if index == 0: |
| 163 | + return self.t |
| 164 | + if index == 1: |
| 165 | + return self.y |
| 166 | + if index == 2: |
| 167 | + return self.x |
| 168 | + raise IndexError |
| 169 | + |
| 170 | + # Implement (thin) len to emulate legacy testing interface |
| 171 | + def __len__(self): |
| 172 | + return 3 if self.return_x else 2 |
| 173 | + |
| 174 | + |
86 | 175 | # Helper function for checking array-like parameters |
87 | 176 | def _check_convert_array(in_obj, legal_shapes, err_msg_start, squeeze=False, |
88 | 177 | transpose=False): |
@@ -534,21 +623,9 @@ def _process_time_response( |
534 | 623 |
|
535 | 624 | Returns |
536 | 625 | ------- |
537 | | - T : 1D array |
538 | | - Time values of the output |
539 | | -
|
540 | | - yout : ndarray |
541 | | - Response of the system. If the system is SISO and squeeze is not |
542 | | - True, the array is 1D (indexed by time). If the system is not SISO or |
543 | | - squeeze is False, the array is either 2D (indexed by output and time) |
544 | | - or 3D (indexed by input, output, and time). |
| 626 | + response: InputOutputResponse |
| 627 | + The input/output response of the system. |
545 | 628 |
|
546 | | - xout : array, optional |
547 | | - Individual response of each x variable (if return_x is True). For a |
548 | | - SISO system (or if a single input is specified), xout is a 2D array |
549 | | - indexed by the state index and time. For a non-SISO system, xout is a |
550 | | - 3D array indexed by the state, the input, and time. The shape of xout |
551 | | - is not affected by the ``squeeze`` keyword. |
552 | 629 | """ |
553 | 630 | # If squeeze was not specified, figure out the default (might remain None) |
554 | 631 | if squeeze is None: |
@@ -586,7 +663,8 @@ def _process_time_response( |
586 | 663 | xout = np.transpose(xout, np.roll(range(xout.ndim), 1)) |
587 | 664 |
|
588 | 665 | # Return time, output, and (optionally) state |
589 | | - return (tout, yout, xout) if return_x else (tout, yout) |
| 666 | + return InputOutputResponse( |
| 667 | + tout, yout, xout, None, return_x=return_x, squeeze=squeeze) |
590 | 668 |
|
591 | 669 |
|
592 | 670 | def _get_ss_simo(sys, input=None, output=None, squeeze=None): |
|
0 commit comments