def tb05ad_example(): """ Example of calculating the frequency response using tb05ad on a second-order system with a natural frequency of 10 rad/s and damping ratio of 1.05. """ import numpy as np A = np.array([[0.0, 1.0], [-100.0, -20.1]]) B = np.array([[0.],[100]]) C = np.array([[1., 0.]]) n = np.shape(A)[0] m = np.shape(B)[1] p = np.shape(C)[0] jw_s = [1j*11, 1j*15] at, bt, ct, g_1, hinvb,info = slycot.tb05ad(n, m, p, jw_s[0], A, B, C, job='NG') g_2, hinv2, info = slycot.tb05ad(n, m, p, jw_s[1], at, bt, ct, job='NH') print('--- Example for tb05ad...') print('Frequency response for (A, B, C)') print('-------------------------') print('Frequency | Response') print('%s | %s '%(jw_s[0], g_1[0, 0])) print('%s | %s '%(jw_s[1], g_2[0, 0]))
def freqresp(self, omega): """Evaluate the system's transfer func. at a list of freqs, omega. mag, phase, omega = self.freqresp(omega) Reports the frequency response of the system, G(j*omega) = mag*exp(j*phase) for continuous time. For discrete time systems, the response is evaluated around the unit circle such that G(exp(j*omega*dt)) = mag*exp(j*phase). Inputs: ------ omega: A list of frequencies in radians/sec at which the system should be evaluated. The list can be either a python list or a numpy array and will be sorted before evaluation. Returns: ------- mag: The magnitude (absolute value, not dB or log10) of the system frequency response. phase: The wrapped phase in radians of the system frequency response. omega: The list of sorted frequencies at which the response was evaluated. """ # In case omega is passed in as a list, rather than a proper array. omega = np.asarray(omega) numFreqs = len(omega) Gfrf = np.empty((self.outputs, self.inputs, numFreqs), dtype=np.complex128) # Sort frequency and calculate complex frequencies on either imaginary # axis (continuous time) or unit circle (discrete time). omega.sort() if isdtime(self, strict=True): dt = timebase(self) cmplx_freqs = exp(1.j * omega * dt) if ((omega * dt).any() > pi): warn_message = ("evalfr: frequency evaluation" " above Nyquist frequency") warnings.warn(warn_message) else: cmplx_freqs = omega * 1.j # Do the frequency response evaluation. Use TB05AD from Slycot # if it's available, otherwise use the built-in horners function. try: from slycot import tb05ad n = np.shape(self.A)[0] m = self.inputs p = self.outputs # The first call both evalates C(sI-A)^-1 B and also returns # hessenberg transformed matrices at, bt, ct. result = tb05ad(n, m, p, cmplx_freqs[0], self.A, self.B, self.C, job='NG') # When job='NG', result = (at, bt, ct, g_i, hinvb, info) at = result[0] bt = result[1] ct = result[2] # TB05AD freqency evaluation does not include direct feedthrough. Gfrf[:, :, 0] = result[3] + self.D # Now, iterate through the remaining frequencies using the # transformed state matrices, at, bt, ct. # Start at the second frequency, already have the first. for kk, cmplx_freqs_kk in enumerate(cmplx_freqs[1:numFreqs]): result = tb05ad(n, m, p, cmplx_freqs_kk, at, bt, ct, job='NH') # When job='NH', result = (g_i, hinvb, info) # kk+1 because enumerate starts at kk = 0. # but zero-th spot is already filled. Gfrf[:, :, kk + 1] = result[0] + self.D except ImportError: # Slycot unavailable. Fall back to horner. for kk, cmplx_freqs_kk in enumerate(cmplx_freqs): Gfrf[:, :, kk] = self.horner(cmplx_freqs_kk) # mag phase omega return np.abs(Gfrf), np.angle(Gfrf), omega
def freqresp(self, omega): """ Evaluate the system's transfer func. at a list of freqs, omega. mag, phase, omega = self.freqresp(omega) Reports the frequency response of the system, G(j*omega) = mag*exp(j*phase) for continuous time. For discrete time systems, the response is evaluated around the unit circle such that G(exp(j*omega*dt)) = mag*exp(j*phase). Inputs ------ omega: A list of frequencies in radians/sec at which the system should be evaluated. The list can be either a python list or a numpy array and will be sorted before evaluation. Returns ------- mag: The magnitude (absolute value, not dB or log10) of the system frequency response. phase: The wrapped phase in radians of the system frequency response. omega: The list of sorted frequencies at which the response was evaluated. """ # In case omega is passed in as a list, rather than a proper array. omega = np.asarray(omega) numFreqs = len(omega) Gfrf = np.empty((self.outputs, self.inputs, numFreqs), dtype=np.complex128) # Sort frequency and calculate complex frequencies on either imaginary # axis (continuous time) or unit circle (discrete time). omega.sort() if isdtime(self, strict=True): dt = timebase(self) cmplx_freqs = exp(1.j * omega * dt) if max(np.abs(omega)) * dt > math.pi: warn("freqresp: frequency evaluation above Nyquist frequency") else: cmplx_freqs = omega * 1.j # Do the frequency response evaluation. Use TB05AD from Slycot # if it's available, otherwise use the built-in horners function. try: from slycot import tb05ad n = np.shape(self.A)[0] m = self.inputs p = self.outputs # The first call both evaluates C(sI-A)^-1 B and also returns # Hessenberg transformed matrices at, bt, ct. result = tb05ad(n, m, p, cmplx_freqs[0], self.A, self.B, self.C, job='NG') # When job='NG', result = (at, bt, ct, g_i, hinvb, info) at = result[0] bt = result[1] ct = result[2] # TB05AD frequency evaluation does not include direct feedthrough. Gfrf[:, :, 0] = result[3] + self.D # Now, iterate through the remaining frequencies using the # transformed state matrices, at, bt, ct. # Start at the second frequency, already have the first. for kk, cmplx_freqs_kk in enumerate(cmplx_freqs[1:numFreqs]): result = tb05ad(n, m, p, cmplx_freqs_kk, at, bt, ct, job='NH') # When job='NH', result = (g_i, hinvb, info) # kk+1 because enumerate starts at kk = 0. # but zero-th spot is already filled. Gfrf[:, :, kk+1] = result[0] + self.D except ImportError: # Slycot unavailable. Fall back to horner. for kk, cmplx_freqs_kk in enumerate(cmplx_freqs): Gfrf[:, :, kk] = self.horner(cmplx_freqs_kk) # mag phase omega return np.abs(Gfrf), np.angle(Gfrf), omega