@@ -468,6 +468,73 @@ def __str__(self, var=None):
468468
469469 return outstr
470470
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)
483+
484+ """
485+
486+ mimo = self .ninputs > 1 or self .noutputs > 1
487+ 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'
490+ outstr = ""
491+
492+ for i in range (self .ninputs ):
493+ for j in range (self .noutputs ):
494+ if mimo :
495+ outstr += "\n Input %i to output %i:" % (i + 1 , j + 1 )
496+
497+ # 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+ if abs (polygain ) == 0 and abs (dcgain ) == 0 :
506+ k = 1
507+ else :
508+ k = dcgain / polygain
509+ if not np .isreal (k ):
510+ raise ValueError ("Transfer function has complex valued gain. "
511+ "Please check polynomials for non-complimentary poles." )
512+
513+ k = np .abs (k )
514+
515+ numstr = _tf_factorized_polynomial_to_string (num_roots , gain = k , var = var )
516+ denstr = _tf_factorized_polynomial_to_string (den_roots , var = var )
517+
518+ # Figure out the length of the separating line
519+ dashcount = max (len (numstr ), len (denstr ))
520+ dashes = '-' * dashcount
521+
522+ # Center the numerator or denominator
523+ if len (numstr ) < dashcount :
524+ numstr = ' ' * ((dashcount - len (numstr )) // 2 ) + numstr
525+ if len (denstr ) < dashcount :
526+ denstr = ' ' * ((dashcount - len (denstr )) // 2 ) + denstr
527+
528+ outstr += "\n " + numstr + "\n " + dashes + "\n " + denstr + "\n "
529+
530+ # See if this is a discrete time system with specific sampling time
531+ if not (self .dt is None ) and type (self .dt ) != bool and self .dt > 0 :
532+ # TODO: replace with standard calls to lti functions
533+ outstr += "\n dt = " + self .dt .__str__ () + "\n "
534+
535+ return outstr
536+
537+
471538 # represent to implement a re-loadable version
472539 def __repr__ (self ):
473540 """Print transfer function in loadable form"""
@@ -1323,6 +1390,49 @@ def _tf_polynomial_to_string(coeffs, var='s'):
13231390 return thestr
13241391
13251392
1393+ def _tf_factorized_polynomial_to_string (roots , gain = 1 , var = 's' ):
1394+ """Convert a factorized polynomial to a string"""
1395+
1396+ if roots .size == 0 :
1397+ return f"{ gain :.4g} "
1398+
1399+ factors = []
1400+ for root in sorted (roots , reverse = True ):
1401+ if np .isreal (root ):
1402+ if root == 0 :
1403+ factor = f"{ var } "
1404+ factors .append (factor )
1405+ elif root > 0 :
1406+ factor = f"{ var } - { np .abs (root ):.4g} "
1407+ factors .append (factor )
1408+ else :
1409+ factor = f"{ var } + { np .abs (root ):.4g} "
1410+ factors .append (factor )
1411+ elif np .isreal (root * 1j ):
1412+ if root .imag > 0 :
1413+ factor = f"{ var } - { np .abs (root ):.4g} j"
1414+ factors .append (factor )
1415+ else :
1416+ factor = f"{ var } + { np .abs (root ):.4g} j"
1417+ factors .append (factor )
1418+ else :
1419+ if root .real > 0 :
1420+ factor = f"{ var } - ({ root :.4g} )"
1421+ factors .append (factor )
1422+ else :
1423+ factor = f"{ var } + ({ - root :.4g} )"
1424+ factors .append (factor )
1425+
1426+
1427+ multiplier = ''
1428+ if round (gain , 4 ) != 1.0 :
1429+ multiplier = f"{ gain :.4g} "
1430+
1431+ if len (factors ) > 1 or multiplier :
1432+ factors = [f"({ factor } )" for factor in factors ]
1433+
1434+ return multiplier + " " .join (factors )
1435+
13261436def _tf_string_to_latex (thestr , var = 's' ):
13271437 """ make sure to superscript all digits in a polynomial string
13281438 and convert float coefficients in scientific notation
0 commit comments