@@ -92,6 +92,9 @@ class TransferFunction(LTI):
9292 time, positive number is discrete time with specified
9393 sampling time, None indicates unspecified timebase (either
9494 continuous or discrete time).
95+ display_format: None, 'poly' or 'zpk'
96+ Set the display format used in printing the TransferFunction object.
97+ Default behavior is polynomial display.
9598
9699 Attributes
97100 ----------
@@ -149,7 +152,7 @@ class TransferFunction(LTI):
149152 # Give TransferFunction._rmul_() priority for ndarray * TransferFunction
150153 __array_priority__ = 11 # override ndarray and matrix types
151154
152- def __init__ (self , * args , ** kwargs ):
155+ def __init__ (self , * args , display_format = None , ** kwargs ):
153156 """TransferFunction(num, den[, dt])
154157
155158 Construct a transfer function.
@@ -198,6 +201,14 @@ def __init__(self, *args, **kwargs):
198201 #
199202 # Process keyword arguments
200203 #
204+ if display_format is None :
205+ display_format = 'poly'
206+
207+ if display_format not in ('poly' , 'zpk' ):
208+ raise ValueError ("display_format must be 'poly' or 'zpk',"
209+ " got '%s'" % display_format )
210+
211+ self .display_format = display_format
201212
202213 # Determine if the transfer function is static (needed for dt)
203214 static = True
@@ -432,61 +443,16 @@ def _truncatecoeff(self):
432443 [self .num , self .den ] = data
433444
434445 def __str__ (self , var = None ):
435- """String representation of the transfer function."""
436-
437- mimo = self .ninputs > 1 or self .noutputs > 1
438- if var is None :
439- # TODO: replace with standard calls to lti functions
440- var = 's' if self .dt is None or self .dt == 0 else 'z'
441- outstr = ""
442-
443- for i in range (self .ninputs ):
444- for j in range (self .noutputs ):
445- if mimo :
446- outstr += "\n Input %i to output %i:" % (i + 1 , j + 1 )
447-
448- # 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 )
446+ """String representation of the transfer function.
451447
452- # Figure out the length of the separating line
453- dashcount = max (len (numstr ), len (denstr ))
454- dashes = '-' * dashcount
455-
456- # Center the numerator or denominator
457- if len (numstr ) < dashcount :
458- numstr = ' ' * ((dashcount - len (numstr )) // 2 ) + numstr
459- if len (denstr ) < dashcount :
460- denstr = ' ' * ((dashcount - len (denstr )) // 2 ) + denstr
461-
462- outstr += "\n " + numstr + "\n " + dashes + "\n " + denstr + "\n "
463-
464- # See if this is a discrete time system with specific sampling time
465- if not (self .dt is None ) and type (self .dt ) != bool and self .dt > 0 :
466- # TODO: replace with standard calls to lti functions
467- outstr += "\n dt = " + self .dt .__str__ () + "\n "
468-
469- return outstr
470-
471- def to_zpk (self , var = None ):
472- """Return string representation of the transfer function as factorized
473- polynomials.
474-
475- Examples
476- --------
477- >>> from control import tf
478- >>> G = tf([1],[1, -2, 1])
479- >>> G.to_zpk()
480- 1
481- ---------------
482- (s - 1) (s - 1)
448+ Based on the display_format property, the output will be formatted as
449+ either polynomials or in zpk form.
483450
484451 """
485452
486- mimo = self . ninputs > 1 or self .noutputs > 1
453+ mimo = not self .issiso ()
487454 if var is None :
488- # TODO: replace with standard calls to lti functions
489- var = 's' if self .dt is None or self .dt == 0 else 'z'
455+ var = 's' if self .isctime () else 'z'
490456 outstr = ""
491457
492458 for i in range (self .ninputs ):
@@ -495,34 +461,13 @@ def to_zpk(self, var=None):
495461 outstr += "\n Input %i to output %i:" % (i + 1 , j + 1 )
496462
497463 # Convert the numerator and denominator polynomials to strings.
498- dcgain = self .num [j ][i ][- 1 ] / self .den [j ][i ][- 1 ]
499-
500- num_roots = roots (self .num [j ][i ]).astype (complex )
501- den_roots = roots (self .den [j ][i ]).astype (complex )
502-
503- polygain = np .prod (num_roots ) / np .prod (den_roots )
504-
505- # Round imaginary part down to zero for values close to
506- # precision to prevent small errors to mess up things.
507- polygain = complex (polygain .real ,
508- round (polygain .imag , 12 ))
509-
510- if abs (polygain ) == 0 and abs (dcgain ) == 0 :
511- k = 1
512- else :
513- if abs (polygain ) == 0 :
514- raise ValueError (
515- f"Transfer function has infinite gain. "
516- "Please check polynomials." )
517- k = dcgain / polygain
518- if not np .isreal (k ):
519- raise ValueError (f"Transfer function has complex valued gain (k = { k } ). "
520- "Please check polynomials for non-complimentary poles." )
521-
522- k = np .abs (k )
523-
524- numstr = _tf_factorized_polynomial_to_string (num_roots , gain = k , var = var )
525- denstr = _tf_factorized_polynomial_to_string (den_roots , var = var )
464+ if self .display_format == 'poly' :
465+ numstr = _tf_polynomial_to_string (self .num [j ][i ], var = var )
466+ denstr = _tf_polynomial_to_string (self .den [j ][i ], var = var )
467+ elif self .display_format == 'zpk' :
468+ z , p , k = tf2zpk (self .num [j ][i ], self .den [j ][i ])
469+ numstr = _tf_factorized_polynomial_to_string (z , gain = k , var = var )
470+ denstr = _tf_factorized_polynomial_to_string (p , var = var )
526471
527472 # Figure out the length of the separating line
528473 dashcount = max (len (numstr ), len (denstr ))
@@ -538,8 +483,7 @@ def to_zpk(self, var=None):
538483
539484 # See if this is a discrete time system with specific sampling time
540485 if not (self .dt is None ) and type (self .dt ) != bool and self .dt > 0 :
541- # TODO: replace with standard calls to lti functions
542- outstr += "\n dt = " + self .dt .__str__ () + "\n "
486+ outstr += "\n dt = " + str (self .dt ) + "\n "
543487
544488 return outstr
545489
@@ -575,8 +519,13 @@ def _repr_latex_(self, var=None):
575519 for i in range (self .noutputs ):
576520 for j in range (self .ninputs ):
577521 # Convert the numerator and denominator polynomials to strings.
578- numstr = _tf_polynomial_to_string (self .num [i ][j ], var = var )
579- denstr = _tf_polynomial_to_string (self .den [i ][j ], var = var )
522+ if self .display_format == 'poly' :
523+ numstr = _tf_polynomial_to_string (self .num [j ][i ], var = var )
524+ denstr = _tf_polynomial_to_string (self .den [j ][i ], var = var )
525+ elif self .display_format == 'zpk' :
526+ z , p , k = tf2zpk (self .num [j ][i ], self .den [j ][i ])
527+ numstr = _tf_factorized_polynomial_to_string (z , gain = k , var = var )
528+ denstr = _tf_factorized_polynomial_to_string (p , var = var )
580529
581530 numstr = _tf_string_to_latex (numstr , var = var )
582531 denstr = _tf_string_to_latex (denstr , var = var )
@@ -1432,7 +1381,6 @@ def _tf_factorized_polynomial_to_string(roots, gain=1, var='s'):
14321381 factor = f"{ var } + ({ - root :.4g} )"
14331382 factors .append (factor )
14341383
1435-
14361384 multiplier = ''
14371385 if round (gain , 4 ) != 1.0 :
14381386 multiplier = f"{ gain :.4g} "
@@ -1605,6 +1553,9 @@ def tf(*args, **kwargs):
16051553 Polynomial coefficients of the numerator
16061554 den: array_like, or list of list of array_like
16071555 Polynomial coefficients of the denominator
1556+ display_format: None, 'poly' or 'zpk'
1557+ Set the display format used in printing the TransferFunction object.
1558+ Default behavior is polynomial display.
16081559
16091560 Returns
16101561 -------
@@ -1657,7 +1608,7 @@ def tf(*args, **kwargs):
16571608
16581609 >>> # Create a variable 's' to allow algebra operations for SISO systems
16591610 >>> s = tf('s')
1660- >>> G = (s + 1)/ (s**2 + 2*s + 1)
1611+ >>> G = (s + 1) / (s**2 + 2*s + 1)
16611612
16621613 >>> # Convert a StateSpace to a TransferFunction object.
16631614 >>> sys_ss = ss("1. -2; 3. -4", "5.; 7", "6. 8", "9.")
@@ -1728,14 +1679,27 @@ def zpk(zeros, poles, gain, *args, **kwargs):
17281679 name : string, optional
17291680 System name (used for specifying signals). If unspecified, a generic
17301681 name <sys[id]> is generated with a unique integer id.
1682+ display_format: None, 'poly' or 'zpk'
1683+ Set the display format used in printing the TransferFunction object.
1684+ Default behavior is zpk display.
17311685
17321686 Returns
17331687 -------
17341688 out: :class:`TransferFunction`
17351689 Transfer function with given zeros, poles, and gain.
17361690
1691+ Examples
1692+ --------
1693+ >>> from control import tf
1694+ >>> G = zpk([1],[2, 3], gain=6)
1695+ >>> G
1696+ s - 1
1697+ ---------------
1698+ (s - 2) (s - )
17371699 """
17381700 num , den = zpk2tf (zeros , poles , gain )
1701+ if 'display_format' not in kwargs :
1702+ kwargs ['display_format' ] = 'zpk'
17391703 return TransferFunction (num , den , * args , ** kwargs )
17401704
17411705
0 commit comments