Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit26c44e1

Browse files
authored
Merge pull request#869 from henklaak/feature_print_zpk
Add zpk display option for transfer functions
2 parents177cba9 +6933973 commit26c44e1

File tree

2 files changed

+238
-24
lines changed

2 files changed

+238
-24
lines changed

‎control/tests/xferfcn_test.py

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
importcontrolasct
1111
fromcontrolimportStateSpace,TransferFunction,rss,evalfr
12-
fromcontrolimportss,ss2tf,tf,tf2ss
13-
fromcontrolimportisctime,isdtime,sample_system,defaults
12+
fromcontrolimportss,ss2tf,tf,tf2ss,zpk
13+
fromcontrolimportisctime,isdtime,sample_system
14+
fromcontrolimportdefaults,reset_defaults,set_defaults
1415
fromcontrol.statespimport_convert_to_statespace
1516
fromcontrol.xferfcnimport_convert_to_transfer_function
1617
fromcontrol.tests.conftestimportslycotonly,matrixfilter
@@ -906,6 +907,128 @@ def test_printing_mimo(self):
906907
assertisinstance(str(sys),str)
907908
assertisinstance(sys._repr_latex_(),str)
908909

910+
@pytest.mark.parametrize(
911+
"zeros, poles, gain, output",
912+
[([0], [-1],1,
913+
'\n'
914+
' s\n'
915+
'-----\n'
916+
's + 1\n'),
917+
([-1], [-1],1,
918+
'\n'
919+
's + 1\n'
920+
'-----\n'
921+
's + 1\n'),
922+
([-1], [1],1,
923+
'\n'
924+
's + 1\n'
925+
'-----\n'
926+
's - 1\n'),
927+
([1], [-1],1,
928+
'\n'
929+
's - 1\n'
930+
'-----\n'
931+
's + 1\n'),
932+
([-1], [-1],2,
933+
'\n'
934+
'2 (s + 1)\n'
935+
'---------\n'
936+
' s + 1\n'),
937+
([-1], [-1],0,
938+
'\n'
939+
'0\n'
940+
'-\n'
941+
'1\n'),
942+
([-1], [1j,-1j],1,
943+
'\n'
944+
' s + 1\n'
945+
'-----------------\n'
946+
'(s - 1j) (s + 1j)\n'),
947+
([4j,-4j], [2j,-2j],2,
948+
'\n'
949+
'2 (s - 4j) (s + 4j)\n'
950+
'-------------------\n'
951+
' (s - 2j) (s + 2j)\n'),
952+
([1j,-1j], [-1,-4],2,
953+
'\n'
954+
'2 (s - 1j) (s + 1j)\n'
955+
'-------------------\n'
956+
' (s + 1) (s + 4)\n'),
957+
([1], [-1+1j,-1-1j],1,
958+
'\n'
959+
' s - 1\n'
960+
'-------------------------\n'
961+
'(s + (1-1j)) (s + (1+1j))\n'),
962+
([1], [1+1j,1-1j],1,
963+
'\n'
964+
' s - 1\n'
965+
'-------------------------\n'
966+
'(s - (1+1j)) (s - (1-1j))\n'),
967+
])
968+
deftest_printing_zpk(self,zeros,poles,gain,output):
969+
"""Test _tf_polynomial_to_string for constant systems"""
970+
G=zpk(zeros,poles,gain,display_format='zpk')
971+
res=str(G)
972+
assertres==output
973+
974+
@pytest.mark.parametrize(
975+
"zeros, poles, gain, format, output",
976+
[([1], [1+1j,1-1j],1,".2f",
977+
'\n'
978+
' 1.00\n'
979+
'-------------------------------------\n'
980+
'(s + (1.00-1.41j)) (s + (1.00+1.41j))\n'),
981+
([1], [1+1j,1-1j],1,".3f",
982+
'\n'
983+
' 1.000\n'
984+
'-----------------------------------------\n'
985+
'(s + (1.000-1.414j)) (s + (1.000+1.414j))\n'),
986+
([1], [1+1j,1-1j],1,".6g",
987+
'\n'
988+
' 1\n'
989+
'-------------------------------------\n'
990+
'(s + (1-1.41421j)) (s + (1+1.41421j))\n')
991+
])
992+
deftest_printing_zpk_format(self,zeros,poles,gain,format,output):
993+
"""Test _tf_polynomial_to_string for constant systems"""
994+
G=tf([1], [1,2,3],display_format='zpk')
995+
996+
set_defaults('xferfcn',floating_point_format=format)
997+
res=str(G)
998+
reset_defaults()
999+
1000+
assertres==output
1001+
1002+
@pytest.mark.parametrize(
1003+
"num, den, output",
1004+
[([[[11], [21]], [[12], [22]]],
1005+
[[[1,-3,2], [1,1,-6]], [[1,0,1], [1,-1,-20]]],
1006+
('\n'
1007+
'Input 1 to output 1:\n'
1008+
' 11\n'
1009+
'---------------\n'
1010+
'(s - 2) (s - 1)\n'
1011+
'\n'
1012+
'Input 1 to output 2:\n'
1013+
' 12\n'
1014+
'-----------------\n'
1015+
'(s - 1j) (s + 1j)\n'
1016+
'\n'
1017+
'Input 2 to output 1:\n'
1018+
' 21\n'
1019+
'---------------\n'
1020+
'(s - 2) (s + 3)\n'
1021+
'\n'
1022+
'Input 2 to output 2:\n'
1023+
' 22\n'
1024+
'---------------\n'
1025+
'(s - 5) (s + 4)\n'))])
1026+
deftest_printing_zpk_mimo(self,num,den,output):
1027+
"""Test _tf_polynomial_to_string for constant systems"""
1028+
G=tf(num,den,display_format='zpk')
1029+
res=str(G)
1030+
assertres==output
1031+
9091032
@slycotonly
9101033
deftest_size_mismatch(self):
9111034
"""Test size mismacht"""

‎control/xferfcn.py

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,14 @@
6969

7070

7171
# Define module default parameter values
72-
_xferfcn_defaults= {}
72+
_xferfcn_defaults= {
73+
'xferfcn.display_format':'poly',
74+
'xferfcn.floating_point_format':'.4g'
75+
}
76+
77+
def_float2str(value):
78+
_num_format=config.defaults.get('xferfcn.floating_point_format',':.4g')
79+
returnf"{value:{_num_format}}"
7380

7481

7582
classTransferFunction(LTI):
@@ -92,6 +99,10 @@ class TransferFunction(LTI):
9299
time, positive number is discrete time with specified
93100
sampling time, None indicates unspecified timebase (either
94101
continuous or discrete time).
102+
display_format: None, 'poly' or 'zpk'
103+
Set the display format used in printing the TransferFunction object.
104+
Default behavior is polynomial display and can be changed by
105+
changing config.defaults['xferfcn.display_format'].
95106
96107
Attributes
97108
----------
@@ -198,6 +209,17 @@ def __init__(self, *args, **kwargs):
198209
#
199210
# Process keyword arguments
200211
#
212+
# During module init, TransferFunction.s and TransferFunction.z
213+
# get initialized when defaults are not fully initialized yet.
214+
# Use 'poly' in these cases.
215+
216+
self.display_format=kwargs.pop(
217+
'display_format',
218+
config.defaults.get('xferfcn.display_format','poly'))
219+
220+
ifself.display_formatnotin ('poly','zpk'):
221+
raiseValueError("display_format must be 'poly' or 'zpk',"
222+
" got '%s'"%self.display_format)
201223

202224
# Determine if the transfer function is static (needed for dt)
203225
static=True
@@ -432,22 +454,29 @@ def _truncatecoeff(self):
432454
[self.num,self.den]=data
433455

434456
def__str__(self,var=None):
435-
"""String representation of the transfer function."""
457+
"""String representation of the transfer function.
436458
437-
mimo=self.ninputs>1orself.noutputs>1
459+
Based on the display_format property, the output will be formatted as
460+
either polynomials or in zpk form.
461+
"""
462+
mimo=notself.issiso()
438463
ifvarisNone:
439-
# TODO: replace with standard calls to lti functions
440-
var='s'ifself.dtisNoneorself.dt==0else'z'
464+
var='s'ifself.isctime()else'z'
441465
outstr=""
442466

443-
foriinrange(self.ninputs):
444-
forjinrange(self.noutputs):
467+
forniinrange(self.ninputs):
468+
fornoinrange(self.noutputs):
445469
ifmimo:
446-
outstr+="\nInput %i to output %i:"% (i+1,j+1)
470+
outstr+="\nInput %i to output %i:"% (ni+1,no+1)
447471

448472
# Convert the numerator and denominator polynomials to strings.
449-
numstr=_tf_polynomial_to_string(self.num[j][i],var=var)
450-
denstr=_tf_polynomial_to_string(self.den[j][i],var=var)
473+
ifself.display_format=='poly':
474+
numstr=_tf_polynomial_to_string(self.num[no][ni],var=var)
475+
denstr=_tf_polynomial_to_string(self.den[no][ni],var=var)
476+
elifself.display_format=='zpk':
477+
z,p,k=tf2zpk(self.num[no][ni],self.den[no][ni])
478+
numstr=_tf_factorized_polynomial_to_string(z,gain=k,var=var)
479+
denstr=_tf_factorized_polynomial_to_string(p,var=var)
451480

452481
# Figure out the length of the separating line
453482
dashcount=max(len(numstr),len(denstr))
@@ -461,10 +490,9 @@ def __str__(self, var=None):
461490

462491
outstr+="\n"+numstr+"\n"+dashes+"\n"+denstr+"\n"
463492

464-
# See if this is a discrete time system with specific sampling time
465-
ifnot (self.dtisNone)andtype(self.dt)!=boolandself.dt>0:
466-
# TODO: replace with standard calls to lti functions
467-
outstr+="\ndt = "+self.dt.__str__()+"\n"
493+
# If this is a strict discrete time system, print the sampling time
494+
iftype(self.dt)!=boolandself.isdtime(strict=True):
495+
outstr+="\ndt = "+str(self.dt)+"\n"
468496

469497
returnoutstr
470498

@@ -485,7 +513,7 @@ def __repr__(self):
485513
def_repr_latex_(self,var=None):
486514
"""LaTeX representation of transfer function, for Jupyter notebook"""
487515

488-
mimo=self.ninputs>1orself.noutputs>1
516+
mimo=notself.issiso()
489517

490518
ifvarisNone:
491519
# ! TODO: replace with standard calls to lti functions
@@ -496,18 +524,23 @@ def _repr_latex_(self, var=None):
496524
ifmimo:
497525
out.append(r"\begin{bmatrix}")
498526

499-
foriinrange(self.noutputs):
500-
forjinrange(self.ninputs):
527+
fornoinrange(self.noutputs):
528+
forniinrange(self.ninputs):
501529
# Convert the numerator and denominator polynomials to strings.
502-
numstr=_tf_polynomial_to_string(self.num[i][j],var=var)
503-
denstr=_tf_polynomial_to_string(self.den[i][j],var=var)
530+
ifself.display_format=='poly':
531+
numstr=_tf_polynomial_to_string(self.num[no][ni],var=var)
532+
denstr=_tf_polynomial_to_string(self.den[no][ni],var=var)
533+
elifself.display_format=='zpk':
534+
z,p,k=tf2zpk(self.num[no][ni],self.den[no][ni])
535+
numstr=_tf_factorized_polynomial_to_string(z,gain=k,var=var)
536+
denstr=_tf_factorized_polynomial_to_string(p,var=var)
504537

505538
numstr=_tf_string_to_latex(numstr,var=var)
506539
denstr=_tf_string_to_latex(denstr,var=var)
507540

508541
out+= [r"\frac{",numstr,"}{",denstr,"}"]
509542

510-
ifmimoandj<self.noutputs-1:
543+
ifmimoandni<self.ninputs-1:
511544
out.append("&")
512545

513546
ifmimo:
@@ -1285,7 +1318,7 @@ def _tf_polynomial_to_string(coeffs, var='s'):
12851318
N=len(coeffs)-1
12861319

12871320
forkinrange(len(coeffs)):
1288-
coefstr='%.4g'%abs(coeffs[k])
1321+
coefstr=_float2str(abs(coeffs[k]))
12891322
power= (N-k)
12901323
ifpower==0:
12911324
ifcoefstr!='0':
@@ -1323,6 +1356,48 @@ def _tf_polynomial_to_string(coeffs, var='s'):
13231356
returnthestr
13241357

13251358

1359+
def_tf_factorized_polynomial_to_string(roots,gain=1,var='s'):
1360+
"""Convert a factorized polynomial to a string"""
1361+
1362+
ifroots.size==0:
1363+
return_float2str(gain)
1364+
1365+
factors= []
1366+
forrootinsorted(roots,reverse=True):
1367+
ifnp.isreal(root):
1368+
ifroot==0:
1369+
factor=f"{var}"
1370+
factors.append(factor)
1371+
elifroot>0:
1372+
factor=f"{var} -{_float2str(np.abs(root))}"
1373+
factors.append(factor)
1374+
else:
1375+
factor=f"{var} +{_float2str(np.abs(root))}"
1376+
factors.append(factor)
1377+
elifnp.isreal(root*1j):
1378+
ifroot.imag>0:
1379+
factor=f"{var} -{_float2str(np.abs(root))}j"
1380+
factors.append(factor)
1381+
else:
1382+
factor=f"{var} +{_float2str(np.abs(root))}j"
1383+
factors.append(factor)
1384+
else:
1385+
ifroot.real>0:
1386+
factor=f"{var} - ({_float2str(root)})"
1387+
factors.append(factor)
1388+
else:
1389+
factor=f"{var} + ({_float2str(-root)})"
1390+
factors.append(factor)
1391+
1392+
multiplier=''
1393+
ifround(gain,4)!=1.0:
1394+
multiplier=_float2str(gain)+" "
1395+
1396+
iflen(factors)>1ormultiplier:
1397+
factors= [f"({factor})"forfactorinfactors]
1398+
1399+
returnmultiplier+" ".join(factors)
1400+
13261401
def_tf_string_to_latex(thestr,var='s'):
13271402
""" make sure to superscript all digits in a polynomial string
13281403
and convert float coefficients in scientific notation
@@ -1486,6 +1561,10 @@ def tf(*args, **kwargs):
14861561
Polynomial coefficients of the numerator
14871562
den: array_like, or list of list of array_like
14881563
Polynomial coefficients of the denominator
1564+
display_format: None, 'poly' or 'zpk'
1565+
Set the display format used in printing the TransferFunction object.
1566+
Default behavior is polynomial display and can be changed by
1567+
changing config.defaults['xferfcn.display_format']..
14891568
14901569
Returns
14911570
-------
@@ -1538,7 +1617,7 @@ def tf(*args, **kwargs):
15381617
15391618
>>> # Create a variable 's' to allow algebra operations for SISO systems
15401619
>>> s = tf('s')
1541-
>>> G = (s + 1)/(s**2 + 2*s + 1)
1620+
>>> G = (s + 1) /(s**2 + 2*s + 1)
15421621
15431622
>>> # Convert a StateSpace to a TransferFunction object.
15441623
>>> sys_ss = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
@@ -1609,12 +1688,24 @@ def zpk(zeros, poles, gain, *args, **kwargs):
16091688
name : string, optional
16101689
System name (used for specifying signals). If unspecified, a generic
16111690
name <sys[id]> is generated with a unique integer id.
1691+
display_format: None, 'poly' or 'zpk'
1692+
Set the display format used in printing the TransferFunction object.
1693+
Default behavior is polynomial display and can be changed by
1694+
changing config.defaults['xferfcn.display_format'].
16121695
16131696
Returns
16141697
-------
16151698
out: :class:`TransferFunction`
16161699
Transfer function with given zeros, poles, and gain.
16171700
1701+
Examples
1702+
--------
1703+
>>> from control import tf
1704+
>>> G = zpk([1],[2, 3], gain=1, display_format='zpk')
1705+
>>> G
1706+
s - 1
1707+
---------------
1708+
(s - 2) (s - 3)
16181709
"""
16191710
num,den=zpk2tf(zeros,poles,gain)
16201711
returnTransferFunction(num,den,*args,**kwargs)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp