def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI+1): for outputs in range(1, self.maxO+1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff =\ tb04ad(states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) # print 'size(Trans_A)=',ssTransformed_A.shape if (verbose): print('===== Transformed SS ==========') print(matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D)) # print 'Trans_nr=',ssTransformed_nr # print 'tfOrig_index=',tfOriginal_index # print 'tfOrig_ucoeff=',tfOriginal_ucoeff # print 'tfOrig_dcoeff=',tfOriginal_dcoeff # print 'tfTrans_index=',tfTransformed_index # print 'tfTrans_ucoeff=',tfTransformed_ucoeff # print 'tfTrans_dcoeff=',tfTransformed_dcoeff # Compare the TF directly, must match # numerators np.testing.assert_array_almost_equal( tfOriginal_ucoeff, tfTransformed_ucoeff, decimal=3) # denominators np.testing.assert_array_almost_equal( tfOriginal_dcoeff, tfTransformed_dcoeff, decimal=3)
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI + 1): for outputs in range(1, self.maxO + 1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb, tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad(states,inputs,outputs,\ ssOriginal.A,ssOriginal.B,ssOriginal.C,ssOriginal.D,tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D\ = td04ad('R',inputs,outputs,tfOriginal_index,tfOriginal_dcoeff,tfOriginal_ucoeff,tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb, tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff, tfTransformed_ucoeff = tb04ad(ssTransformed_nr,\ inputs,outputs,ssTransformed_A, ssTransformed_B, ssTransformed_C,ssTransformed_D,tol1=0.0) #print 'size(Trans_A)=',ssTransformed_A.shape if (verbose): print('===== Transformed SS ==========') print( matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D)) # print 'Trans_nr=',ssTransformed_nr # print 'tfOrig_index=',tfOriginal_index # print 'tfOrig_ucoeff=',tfOriginal_ucoeff # print 'tfOrig_dcoeff=',tfOriginal_dcoeff # print 'tfTrans_index=',tfTransformed_index # print 'tfTrans_ucoeff=',tfTransformed_ucoeff # print 'tfTrans_dcoeff=',tfTransformed_dcoeff #Compare the TF directly, must match #numerators np.testing.assert_array_almost_equal( tfOriginal_ucoeff, tfTransformed_ucoeff, decimal=3) #denominators np.testing.assert_array_almost_equal( tfOriginal_dcoeff, tfTransformed_dcoeff, decimal=3)
def testFreqResp(self): """Compare the bode reponses of the SS systems and TF systems to the original SS They generally are different realizations but have same freq resp. Currently this test may only be applied to SISO systems. """ for states in range(1,self.maxStates): for testNum in range(self.numTests): for inputs in range(1,1): for outputs in range(1,1): ssOriginal = matlab.rss(states, inputs, outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb, tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad(states,inputs,outputs,\ ssOriginal.A,ssOriginal.B,ssOriginal.C,ssOriginal.D,tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D\ = td04ad('R',inputs,outputs,tfOriginal_index,tfOriginal_dcoeff,tfOriginal_ucoeff,tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb, tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff, tfTransformed_ucoeff = tb04ad(\ ssTransformed_nr,inputs,outputs,ssTransformed_A, ssTransformed_B, ssTransformed_C,\ ssTransformed_D,tol1=0.0) numTransformed = np.array(tfTransformed_ucoeff) denTransformed = np.array(tfTransformed_dcoeff) numOriginal = np.array(tfOriginal_ucoeff) denOriginal = np.array(tfOriginal_dcoeff) ssTransformed = matlab.ss(ssTransformed_A,ssTransformed_B,ssTransformed_C,ssTransformed_D) for inputNum in range(inputs): for outputNum in range(outputs): [ssOriginalMag,ssOriginalPhase,freq] = matlab.bode(ssOriginal,Plot=False) [tfOriginalMag,tfOriginalPhase,freq] = matlab.bode(matlab.tf(numOriginal[outputNum][inputNum],denOriginal[outputNum]),Plot=False) [ssTransformedMag,ssTransformedPhase,freq] = matlab.bode(ssTransformed,freq,Plot=False) [tfTransformedMag,tfTransformedPhase,freq] = matlab.bode(matlab.tf(numTransformed[outputNum][inputNum],denTransformed[outputNum]),freq,Plot=False) #print 'numOrig=',numOriginal[outputNum][inputNum] #print 'denOrig=',denOriginal[outputNum] #print 'numTrans=',numTransformed[outputNum][inputNum] #print 'denTrans=',denTransformed[outputNum] np.testing.assert_array_almost_equal(ssOriginalMag,tfOriginalMag,decimal=3) np.testing.assert_array_almost_equal(ssOriginalPhase,tfOriginalPhase,decimal=3) np.testing.assert_array_almost_equal(ssOriginalMag,ssTransformedMag,decimal=3) np.testing.assert_array_almost_equal(ssOriginalPhase,ssTransformedPhase,decimal=3) np.testing.assert_array_almost_equal(tfOriginalMag,tfTransformedMag,decimal=3) np.testing.assert_array_almost_equal(tfOriginalPhase,tfTransformedPhase,decimal=2)
def testTF(self, verbose=False): """ Directly tests the functions tb04ad and td04ad through direct comparison of transfer function coefficients. Similar to convert_test, but tests at a lower level. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for inputs in range(1, self.maxI + 1): for outputs in range(1, self.maxO + 1): for testNum in range(self.numTests): ssOriginal = matlab.rss(states, outputs, inputs) if (verbose): print('====== Original SS ==========') print(ssOriginal) print('states=', states) print('inputs=', inputs) print('outputs=', outputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff =\ tb04ad(states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) # print('size(Trans_A)=',ssTransformed_A.shape) if (verbose): print('===== Transformed SS ==========') print( matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D))
def testFreqResp(self): """Compare the bode reponses of the SS systems and TF systems to the original SS They generally are different realizations but have same freq resp. Currently this test may only be applied to SISO systems. """ from slycot import tb04ad, td04ad for states in range(1, self.maxStates): for testNum in range(self.numTests): for inputs in range(1, 1): for outputs in range(1, 1): ssOriginal = matlab.rss(states, outputs, inputs) tfOriginal_Actrb, tfOriginal_Bctrb, tfOriginal_Cctrb,\ tfOrigingal_nctrb, tfOriginal_index,\ tfOriginal_dcoeff, tfOriginal_ucoeff = tb04ad( states, inputs, outputs, ssOriginal.A, ssOriginal.B, ssOriginal.C, ssOriginal.D, tol1=0.0) ssTransformed_nr, ssTransformed_A, ssTransformed_B,\ ssTransformed_C, ssTransformed_D\ = td04ad('R', inputs, outputs, tfOriginal_index, tfOriginal_dcoeff, tfOriginal_ucoeff, tol=0.0) tfTransformed_Actrb, tfTransformed_Bctrb,\ tfTransformed_Cctrb, tfTransformed_nctrb,\ tfTransformed_index, tfTransformed_dcoeff,\ tfTransformed_ucoeff = tb04ad( ssTransformed_nr, inputs, outputs, ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D, tol1=0.0) numTransformed = np.array(tfTransformed_ucoeff) denTransformed = np.array(tfTransformed_dcoeff) numOriginal = np.array(tfOriginal_ucoeff) denOriginal = np.array(tfOriginal_dcoeff) ssTransformed = matlab.ss(ssTransformed_A, ssTransformed_B, ssTransformed_C, ssTransformed_D) for inputNum in range(inputs): for outputNum in range(outputs): [ssOriginalMag, ssOriginalPhase, freq] =\ matlab.bode(ssOriginal, plot=False) [tfOriginalMag, tfOriginalPhase, freq] =\ matlab.bode(matlab.tf( numOriginal[outputNum][inputNum], denOriginal[outputNum]), plot=False) [ssTransformedMag, ssTransformedPhase, freq] =\ matlab.bode(ssTransformed, freq, plot=False) [tfTransformedMag, tfTransformedPhase, freq] =\ matlab.bode(matlab.tf( numTransformed[outputNum][inputNum], denTransformed[outputNum]), freq, plot=False) # print('numOrig=', # numOriginal[outputNum][inputNum]) # print('denOrig=', # denOriginal[outputNum]) # print('numTrans=', # numTransformed[outputNum][inputNum]) # print('denTrans=', # denTransformed[outputNum]) np.testing.assert_array_almost_equal( ssOriginalMag, tfOriginalMag, decimal=3) np.testing.assert_array_almost_equal( ssOriginalPhase, tfOriginalPhase, decimal=3) np.testing.assert_array_almost_equal( ssOriginalMag, ssTransformedMag, decimal=3) np.testing.assert_array_almost_equal( ssOriginalPhase, ssTransformedPhase, decimal=3) np.testing.assert_array_almost_equal( tfOriginalMag, tfTransformedMag, decimal=3) np.testing.assert_array_almost_equal( tfOriginalPhase, tfTransformedPhase, decimal=2)
def _convertToStateSpace(sys, **kw): """Convert a system to state space form (if needed). If sys is already a state space, then it is returned. If sys is a transfer function object, then it is converted to a state space and returned. If sys is a scalar, then the number of inputs and outputs can be specified manually, as in: >>> sys = _convertToStateSpace(3.) # Assumes inputs = outputs = 1 >>> sys = _convertToStateSpace(1., inputs=3, outputs=2) In the latter example, A = B = C = 0 and D = [[1., 1., 1.] [1., 1., 1.]]. """ from .xferfcn import TransferFunction if isinstance(sys, StateSpace): if len(kw): raise TypeError("If sys is a StateSpace, _convertToStateSpace \ cannot take keywords.") # Already a state space system; just return it return sys elif isinstance(sys, TransferFunction): try: from slycot import td04ad if len(kw): raise TypeError("If sys is a TransferFunction, _convertToStateSpace \ cannot take keywords.") # Change the numerator and denominator arrays so that the transfer # function matrix has a common denominator. num, den = sys._common_den() # Make a list of the orders of the denominator polynomials. index = [len(den) - 1 for i in range(sys.outputs)] # Repeat the common denominator along the rows. den = array([den for i in range(sys.outputs)]) #! TODO: transfer function to state space conversion is still buggy! #print num #print shape(num) ssout = td04ad('R',sys.inputs, sys.outputs, index, den, num,tol=0.0) states = ssout[0] return StateSpace(ssout[1][:states, :states], ssout[2][:states, :sys.inputs], ssout[3][:sys.outputs, :states], ssout[4], sys.dt) except ImportError: # TODO: do we want to squeeze first and check dimenations? # I think this will fail if num and den aren't 1-D after # the squeeze lti_sys = lti(squeeze(sys.num), squeeze(sys.den)) return StateSpace(lti_sys.A, lti_sys.B, lti_sys.C, lti_sys.D, sys.dt) elif isinstance(sys, (int, float, complex)): if "inputs" in kw: inputs = kw["inputs"] else: inputs = 1 if "outputs" in kw: outputs = kw["outputs"] else: outputs = 1 # Generate a simple state space system of the desired dimension # The following Doesn't work due to inconsistencies in ltisys: # return StateSpace([[]], [[]], [[]], eye(outputs, inputs)) return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), sys * ones((outputs, inputs))) # If this is a matrix, try to create a constant feedthrough try: D = matrix(sys) outputs, inputs = D.shape return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), D) except Exception(e): print("Failure to assume argument is matrix-like in" \ " _convertToStateSpace, result %s" % e) raise TypeError("Can't convert given type to StateSpace system.")
def _convertToStateSpace(sys, **kw): """Convert a system to state space form (if needed). If sys is already a state space, then it is returned. If sys is a transfer function object, then it is converted to a state space and returned. If sys is a scalar, then the number of inputs and outputs can be specified manually, as in: >>> sys = _convertToStateSpace(3.) # Assumes inputs = outputs = 1 >>> sys = _convertToStateSpace(1., inputs=3, outputs=2) In the latter example, A = B = C = 0 and D = [[1., 1., 1.] [1., 1., 1.]]. """ from .xferfcn import TransferFunction import itertools if isinstance(sys, StateSpace): if len(kw): raise TypeError("If sys is a StateSpace, _convertToStateSpace \ cannot take keywords.") # Already a state space system; just return it return sys elif isinstance(sys, TransferFunction): try: from slycot import td04ad if len(kw): raise TypeError( "If sys is a TransferFunction, _convertToStateSpace \ cannot take keywords.") # Change the numerator and denominator arrays so that the transfer # function matrix has a common denominator. num, den = sys._common_den() # Make a list of the orders of the denominator polynomials. index = [len(den) - 1 for i in range(sys.outputs)] # Repeat the common denominator along the rows. den = array([den for i in range(sys.outputs)]) #! TODO: transfer function to state space conversion is still buggy! #print num #print shape(num) ssout = td04ad('R', sys.inputs, sys.outputs, index, den, num, tol=0.0) states = ssout[0] return StateSpace(ssout[1][:states, :states], ssout[2][:states, :sys.inputs], ssout[3][:sys.outputs, :states], ssout[4], sys.dt) except ImportError: # No Slycot. Scipy tf->ss can't handle MIMO, but static # MIMO is an easy special case we can check for here maxn = max(max(len(n) for n in nrow) for nrow in sys.num) maxd = max(max(len(d) for d in drow) for drow in sys.den) if 1 == maxn and 1 == maxd: D = empty((sys.outputs, sys.inputs), dtype=float) for i, j in itertools.product(range(sys.outputs), range(sys.inputs)): D[i, j] = sys.num[i][j][0] / sys.den[i][j][0] return StateSpace([], [], [], D, sys.dt) else: if (sys.inputs != 1 or sys.outputs != 1): raise TypeError("No support for MIMO without slycot") # TODO: do we want to squeeze first and check dimenations? # I think this will fail if num and den aren't 1-D after # the squeeze A, B, C, D = sp.signal.tf2ss(squeeze(sys.num), squeeze(sys.den)) return StateSpace(A, B, C, D, sys.dt) elif isinstance(sys, (int, float, complex, np.number)): if "inputs" in kw: inputs = kw["inputs"] else: inputs = 1 if "outputs" in kw: outputs = kw["outputs"] else: outputs = 1 # Generate a simple state space system of the desired dimension # The following Doesn't work due to inconsistencies in ltisys: # return StateSpace([[]], [[]], [[]], eye(outputs, inputs)) return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), sys * ones((outputs, inputs))) # If this is a matrix, try to create a constant feedthrough try: D = _matrix(sys) return StateSpace([], [], [], D) except Exception as e: print("Failure to assume argument is matrix-like in" \ " _convertToStateSpace, result %s" % e) raise TypeError("Can't convert given type to StateSpace system.")
def _convertToStateSpace(sys, **kw): """Convert a system to state space form (if needed). If sys is already a state space, then it is returned. If sys is a transfer function object, then it is converted to a state space and returned. If sys is a scalar, then the number of inputs and outputs can be specified manually, as in: >>> sys = _convertToStateSpace(3.) # Assumes inputs = outputs = 1 >>> sys = _convertToStateSpace(1., inputs=3, outputs=2) In the latter example, A = B = C = 0 and D = [[1., 1., 1.] [1., 1., 1.]]. """ from .xferfcn import TransferFunction import itertools if isinstance(sys, StateSpace): if len(kw): raise TypeError("If sys is a StateSpace, _convertToStateSpace \ cannot take keywords.") # Already a state space system; just return it return sys elif isinstance(sys, TransferFunction): try: from slycot import td04ad if len(kw): raise TypeError("If sys is a TransferFunction, " "_convertToStateSpace cannot take keywords.") # Change the numerator and denominator arrays so that the transfer # function matrix has a common denominator. # matrices are also sized/padded to fit td04ad num, den, denorder = sys.minreal()._common_den() # transfer function to state space conversion now should work! ssout = td04ad('C', sys.inputs, sys.outputs, denorder, den, num, tol=0) states = ssout[0] return StateSpace(ssout[1][:states, :states], ssout[2][:states, :sys.inputs], ssout[3][:sys.outputs, :states], ssout[4], sys.dt) except ImportError: # No Slycot. Scipy tf->ss can't handle MIMO, but static # MIMO is an easy special case we can check for here maxn = max(max(len(n) for n in nrow) for nrow in sys.num) maxd = max(max(len(d) for d in drow) for drow in sys.den) if 1 == maxn and 1 == maxd: D = empty((sys.outputs, sys.inputs), dtype=float) for i, j in itertools.product(range(sys.outputs), range(sys.inputs)): D[i, j] = sys.num[i][j][0] / sys.den[i][j][0] return StateSpace([], [], [], D, sys.dt) else: if sys.inputs != 1 or sys.outputs != 1: raise TypeError("No support for MIMO without slycot") # TODO: do we want to squeeze first and check dimenations? # I think this will fail if num and den aren't 1-D after # the squeeze A, B, C, D = sp.signal.tf2ss(squeeze(sys.num), squeeze(sys.den)) return StateSpace(A, B, C, D, sys.dt) elif isinstance(sys, (int, float, complex, np.number)): if "inputs" in kw: inputs = kw["inputs"] else: inputs = 1 if "outputs" in kw: outputs = kw["outputs"] else: outputs = 1 # Generate a simple state space system of the desired dimension # The following Doesn't work due to inconsistencies in ltisys: # return StateSpace([[]], [[]], [[]], eye(outputs, inputs)) return StateSpace(0., zeros((1, inputs)), zeros((outputs, 1)), sys * ones((outputs, inputs))) # If this is a matrix, try to create a constant feedthrough try: D = _matrix(sys) return StateSpace([], [], [], D) except Exception as e: print("Failure to assume argument is matrix-like in" \ " _convertToStateSpace, result %s" % e) raise TypeError("Can't convert given type to StateSpace system.")