Repository: python-control/python-control
Pull request #909 (2023-06-08 17:23:14)

Fixed spelling mistake in description
Typo in both H2 and Hinf headers. 


Patch:
diff --git a/control/__init__.py b/control/__init__.py
index cfc23ed1..340370c8 100644
--- a/control/__init__.py
+++ b/control/__init__.py
@@ -54,15 +54,10 @@ The docstring examples assume that the following import commands::
 
 Available subpackages
 ---------------------
-
-The main control package includes the most commpon functions used in
-analysis, design, and simulation of feedback control systems.  Several
-additional subpackages are available that provide more specialized
-functionality:
-
-* :mod:`~control.flatsys`: Differentially flat systems
-* :mod:`~control.matlab`: MATLAB compatibility module
-* :mod:`~control.optimal`: Optimization-based control
+flatsys
+    Differentially flat systems
+optimal
+    Optimization-based control
 
 """
 
diff --git a/control/dtime.py b/control/dtime.py
index 38fcf805..6197ae8a 100644
--- a/control/dtime.py
+++ b/control/dtime.py
@@ -72,12 +72,22 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None,
             otherwise. See :func:`scipy.signal.cont2discrete`.
     prewarp_frequency : float within [0, infinity)
         The frequency [rad/s] at which to match with the input continuous-
-        time system's magnitude and phase (only valid for method='bilinear',
-        'tustin', or 'gbt' with alpha=0.5)
+        time system's magnitude and phase (only valid for method='bilinear')
+    name : string, optional
+        Set the name of the sampled system.  If not specified and
+        if `copy_names` is `False`, a generic name <sys[id]> is generated
+        with a unique integer id.  If `copy_names` is `True`, the new system
+        name is determined by adding the prefix and suffix strings in
+        config.defaults['namedio.sampled_system_name_prefix'] and
+        config.defaults['namedio.sampled_system_name_suffix'], with the
+        default being to add the suffix '$sampled'.
+    copy_names : bool, Optional
+        If True, copy the names of the input signals, output
+        signals, and states to the sampled system.
 
     Returns
     -------
-    sysd : LTI of the same class (:class:`StateSpace` or :class:`TransferFunction`)
+    sysd : linsys
         Discrete time system, with sampling rate Ts
 
     Other Parameters
@@ -91,17 +101,6 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None,
     states : int, list of str, or None, optional
         Description of the system states.  Same format as `inputs`. Only
         available if the system is :class:`StateSpace`.
-    name : string, optional
-        Set the name of the sampled system.  If not specified and
-        if `copy_names` is `False`, a generic name <sys[id]> is generated
-        with a unique integer id.  If `copy_names` is `True`, the new system
-        name is determined by adding the prefix and suffix strings in
-        config.defaults['namedio.sampled_system_name_prefix'] and
-        config.defaults['namedio.sampled_system_name_suffix'], with the
-        default being to add the suffix '$sampled'.
-    copy_names : bool, Optional
-        If True, copy the names of the input signals, output
-        signals, and states to the sampled system.
 
     Notes
     -----
@@ -127,4 +126,46 @@ def sample_system(sysc, Ts, method='zoh', alpha=None, prewarp_frequency=None,
         method=method, alpha=alpha, prewarp_frequency=prewarp_frequency,
         name=name, copy_names=copy_names, **kwargs)
 
-c2d = sample_system
\ No newline at end of file
+
+def c2d(sysc, Ts, method='zoh', prewarp_frequency=None):
+    """
+    Convert a continuous time system to discrete time by sampling
+
+    Parameters
+    ----------
+    sysc : LTI (:class:`StateSpace` or :class:`TransferFunction`)
+        Continuous time system to be converted
+    Ts : float > 0
+        Sampling period
+    method : string
+        Method to use for conversion, e.g. 'bilinear', 'zoh' (default)
+    prewarp_frequency : real within [0, infinity)
+        The frequency [rad/s] at which to match with the input continuous-
+        time system's magnitude and phase (only valid for method='bilinear')
+
+    Returns
+    -------
+    sysd : LTI of the same class
+        Discrete time system, with sampling rate Ts
+
+    Notes
+    -----
+    See :meth:`StateSpace.sample` or :meth:`TransferFunction.sample`` for
+    further details.
+
+    Examples
+    --------
+    >>> Gc = ct.tf([1], [1, 2, 1])
+    >>> Gc.isdtime()
+    False
+    >>> Gd = ct.sample_system(Gc, 1, method='bilinear')
+    >>> Gd.isdtime()
+    True
+
+    """
+
+    #  Call the sample_system() function to do the work
+    sysd = sample_system(sysc, Ts,
+        method=method, prewarp_frequency=prewarp_frequency)
+
+    return sysd
diff --git a/control/flatsys/systraj.py b/control/flatsys/systraj.py
index 0fbd4e98..c9bde6d7 100644
--- a/control/flatsys/systraj.py
+++ b/control/flatsys/systraj.py
@@ -40,7 +40,7 @@ import numpy as np
 from ..timeresp import TimeResponseData
 
 class SystemTrajectory:
-    """Class representing a trajectory for a flat system.
+    """Class representing a system trajectory.
 
     The `SystemTrajectory` class is used to represent the
     trajectory of a (differentially flat) system.  Used by the
diff --git a/control/freqplot.py b/control/freqplot.py
index 1cedbf68..25de3c11 100644
--- a/control/freqplot.py
+++ b/control/freqplot.py
@@ -819,29 +819,29 @@ def nyquist_plot(
         splane_contour = 1j * omega_sys
 
         # Bend the contour around any poles on/near the imaginary axis
+        # TODO: smarter indent radius that depends on dcgain of system
+        # and timebase of discrete system.
         if isinstance(sys, (StateSpace, TransferFunction)) \
                 and indent_direction != 'none':
             if sys.isctime():
                 splane_poles = sys.poles()
                 splane_cl_poles = sys.feedback().poles()
             else:
-                # map z-plane poles to s-plane. We ignore any at the origin
-                # to avoid numerical warnings because we know we
-                # don't need to indent for them
+                # map z-plane poles to s-plane, ignoring any at the origin
+                # because we don't need to indent for them
                 zplane_poles = sys.poles()
                 zplane_poles = zplane_poles[~np.isclose(abs(zplane_poles), 0.)]
                 splane_poles = np.log(zplane_poles) / sys.dt
 
                 zplane_cl_poles = sys.feedback().poles()
-                # eliminate z-plane poles at the origin to avoid warnings
                 zplane_cl_poles = zplane_cl_poles[
-                    ~np.isclose(abs(zplane_cl_poles), 0.)]
+                    ~np.isclose(abs(zplane_poles), 0.)]
                 splane_cl_poles = np.log(zplane_cl_poles) / sys.dt
 
             #
             # Check to make sure indent radius is small enough
             #
-            # If there is a closed loop pole that is near the imaginary axis
+            # If there is a closed loop pole that is near the imaginary access
             # at a point that is near an open loop pole, it is possible that
             # indentation might skip or create an extraneous encirclement.
             # We check for that situation here and generate a warning if that
@@ -851,16 +851,15 @@ def nyquist_plot(
                 # See if any closed loop poles are near the imaginary axis
                 if abs(p_cl.real) <= indent_radius:
                     # See if any open loop poles are close to closed loop poles
-                    if len(splane_poles) > 0:
-                        p_ol = splane_poles[
-                            (np.abs(splane_poles - p_cl)).argmin()]
+                    p_ol = splane_poles[
+                        (np.abs(splane_poles - p_cl)).argmin()]
 
-                        if abs(p_ol - p_cl) <= indent_radius and \
-                                warn_encirclements:
-                            warnings.warn(
-                                "indented contour may miss closed loop pole; "
-                                "consider reducing indent_radius to below "
-                                f"{abs(p_ol - p_cl):5.2g}", stacklevel=2)
+                    if abs(p_ol - p_cl) <= indent_radius and \
+                       warn_encirclements:
+                        warnings.warn(
+                            "indented contour may miss closed loop pole; "
+                            "consider reducing indent_radius to be less than "
+                            f"{abs(p_ol - p_cl):5.2g}", stacklevel=2)
 
             #
             # See if we should add some frequency points near imaginary poles
@@ -898,30 +897,29 @@ def nyquist_plot(
                     splane_contour[last_point:]))
 
             # Indent points that are too close to a pole
-            if len(splane_poles) > 0: # accomodate no splane poles if dtime sys
-                for i, s in enumerate(splane_contour):
-                    # Find the nearest pole
-                    p = splane_poles[(np.abs(splane_poles - s)).argmin()]
-
-                    # See if we need to indent around it
-                    if abs(s - p) < indent_radius:
-                        # Figure out how much to offset (simple trigonometry)
-                        offset = np.sqrt(indent_radius ** 2 - (s - p).imag ** 2) \
-                            - (s - p).real
-
-                        # Figure out which way to offset the contour point
-                        if p.real < 0 or (p.real == 0 and
-                                        indent_direction == 'right'):
-                            # Indent to the right
-                            splane_contour[i] += offset
-
-                        elif p.real > 0 or (p.real == 0 and
-                                            indent_direction == 'left'):
-                            # Indent to the left
-                            splane_contour[i] -= offset
+            for i, s in enumerate(splane_contour):
+                # Find the nearest pole
+                p = splane_poles[(np.abs(splane_poles - s)).argmin()]
+
+                # See if we need to indent around it
+                if abs(s - p) < indent_radius:
+                    # Figure out how much to offset (simple trigonometry)
+                    offset = np.sqrt(indent_radius ** 2 - (s - p).imag ** 2) \
+                        - (s - p).real
+
+                    # Figure out which way to offset the contour point
+                    if p.real < 0 or (p.real == 0 and
+                                      indent_direction == 'right'):
+                        # Indent to the right
+                        splane_contour[i] += offset
+
+                    elif p.real > 0 or (p.real == 0 and
+                                         indent_direction == 'left'):
+                        # Indent to the left
+                        splane_contour[i] -= offset
 
-                        else:
-                            raise ValueError("unknown value for indent_direction")
+                    else:
+                        raise ValueError("unknown value for indent_direction")
 
         # change contour to z-plane if necessary
         if sys.isctime():
diff --git a/control/iosys.py b/control/iosys.py
index 2881c5d6..78444f7c 100644
--- a/control/iosys.py
+++ b/control/iosys.py
@@ -591,8 +591,13 @@ class InputOutputSystem(NamedIOSystem):
                 DeprecationWarning)
 
         if copy_names:
-            linsys._copy_names(self, prefix_suffix_name='linearized')
-            if name is not None:
+            linsys._copy_names(self)
+            if name is None:
+                linsys.name = \
+                    config.defaults['namedio.linearized_system_name_prefix']+\
+                    linsys.name+\
+                    config.defaults['namedio.linearized_system_name_suffix']
+            else:
                 linsys.name = name
 
         # re-init to include desired signal names if names were provided
@@ -1699,15 +1704,6 @@ def input_output_response(
        start with zero initial condition since this can be specified as
        [xsys_0, 0].  A warning is issued if the initial conditions are padded
        and and the final listed initial state is not zero.
-    
-    2. If discontinuous inputs are given, the underlying SciPy numerical
-       integration algorithms can sometimes produce erroneous results due
-       to the default tolerances that are used.  The `ivp_method` and
-       `ivp_keywords` parameters can be used to tune the ODE solver and
-       produce better results.  In particular, using 'LSODA' as the
-       `ivp_method` or setting the `rtol` parameter to a smaller value
-       (e.g. using `ivp_kwargs={'rtol': 1e-4}`) can provide more accurate
-       results.
 
     """
     #
@@ -1866,7 +1862,7 @@ def input_output_response(
 
         return TimeResponseData(
             t_eval, y, None, u, issiso=sys.issiso(),
-            output_labels=sys.output_labels, input_labels=sys.input_labels,
+            output_labels=sys.output_index, input_labels=sys.input_index,
             transpose=transpose, return_x=return_x, squeeze=squeeze)
 
     # Create a lambda function for the right hand side
@@ -1945,8 +1941,8 @@ def input_output_response(
 
     return TimeResponseData(
         soln.t, y, soln.y, u, issiso=sys.issiso(),
-        output_labels=sys.output_labels, input_labels=sys.input_labels,
-        state_labels=sys.state_labels,
+        output_labels=sys.output_index, input_labels=sys.input_index,
+        state_labels=sys.state_index,
         transpose=transpose, return_x=return_x, squeeze=squeeze)
 
 
@@ -2404,9 +2400,7 @@ def ss(*args, **kwargs):
                      "non-unique state space realization")
 
             # Create a state space system from an LTI system
-            sys = LinearIOSystem(
-                _convert_to_statespace(sys, use_prefix_suffix=True), **kwargs)
-
+            sys = LinearIOSystem(_convert_to_statespace(sys), **kwargs)
         else:
             raise TypeError("ss(sys): sys must be a StateSpace or "
                             "TransferFunction object.  It is %s." % type(sys))
@@ -2887,7 +2881,7 @@ def interconnect(
 
             # Look for the signal name as a system input
             for sys in syslist:
-                if signal_name in sys.input_labels:
+                if signal_name in sys.input_index.keys():
                     connection.append(sign + sys.name + "." + signal_name)
 
             # Make sure we found the name
diff --git a/control/lti.py b/control/lti.py
index c904c150..216332e9 100644
--- a/control/lti.py
+++ b/control/lti.py
@@ -2,14 +2,22 @@
 
 The lti module contains the LTI parent class to the child classes StateSpace
 and TransferFunction.  It is designed for use in the python-control library.
+
+Routines in this module:
+
+LTI.__init__
+isdtime()
+isctime()
+timebase()
+common_timebase()
 """
 
 import numpy as np
 
-from numpy import real, angle, abs
+from numpy import absolute, real, angle, abs
 from warnings import warn
 from . import config
-from .namedio import NamedIOSystem
+from .namedio import NamedIOSystem, isdtime
 
 __all__ = ['poles', 'zeros', 'damp', 'evalfr', 'frequency_response',
            'freqresp', 'dcgain', 'bandwidth', 'pole', 'zero']
@@ -102,11 +110,11 @@ class LTI(NamedIOSystem):
         Returns
         -------
         wn : array
-            Natural frequency for each system pole
+            Natural frequencies for each system pole
         zeta : array
             Damping ratio for each system pole
         poles : array
-            System pole locations
+            Array of system poles
         '''
         poles = self.poles()
 
@@ -114,9 +122,9 @@ class LTI(NamedIOSystem):
             splane_poles = np.log(poles.astype(complex))/self.dt
         else:
             splane_poles = poles
-        wn = abs(splane_poles)
-        zeta = -real(splane_poles)/wn
-        return wn, zeta, poles
+        wn = absolute(splane_poles)
+        Z = -real(splane_poles)/wn
+        return wn, Z, poles
 
     def frequency_response(self, omega, squeeze=None):
         """Evaluate the linear time-invariant system at an array of angular
@@ -338,23 +346,25 @@ def zero(sys):
 
 def damp(sys, doprint=True):
     """
-    Compute natural frequencies, damping ratios, and poles of a system
+    Compute natural frequency, damping ratio, and poles of a system
+
+    The function takes 1 or 2 parameters
 
     Parameters
     ----------
-    sys : LTI (StateSpace or TransferFunction)
+    sys: LTI (StateSpace or TransferFunction)
         A linear system object
-    doprint : bool (optional)
-        if True, print table with values
+    doprint:
+        if true, print table with values
 
     Returns
     -------
-    wn : array
-        Natural frequency for each system pole
-    zeta : array
-        Damping ratio for each system pole
-    poles : array
-        System pole locations
+    wn: array
+        Natural frequencies of the poles
+    damping: array
+        Damping values
+    poles: array
+        Pole locations
 
     See Also
     --------
@@ -364,37 +374,37 @@ def damp(sys, doprint=True):
     -----
     If the system is continuous,
         wn = abs(poles)
-        zeta  = -real(poles)/poles
+        Z  = -real(poles)/poles.
 
     If the system is discrete, the discrete poles are mapped to their
     equivalent location in the s-plane via
 
-        s = log(poles)/dt
+        s = log10(poles)/dt
 
     and
 
         wn = abs(s)
-        zeta = -real(s)/wn.
+        Z = -real(s)/wn.
 
     Examples
     --------
     >>> G = ct.tf([1], [1, 4])
-    >>> wn, zeta, poles = ct.damp(G)
-        Eigenvalue (pole)       Damping     Frequency
-                       -4             1             4
+    >>> wn, damping, poles = ct.damp(G)
+    _____Eigenvalue______ Damping___ Frequency_
+            -4                     1          4
 
     """
-    wn, zeta, poles = sys.damp()
+    wn, damping, poles = sys.damp()
     if doprint:
-        print('    Eigenvalue (pole)       Damping     Frequency')
-        for p, z, w in zip(poles, zeta, wn):
+        print('_____Eigenvalue______ Damping___ Frequency_')
+        for p, d, w in zip(poles, damping, wn):
             if abs(p.imag) < 1e-12:
-                print("           %10.4g    %10.4g    %10.4g" %
-                      (p.real, 1.0, w))
+                print("%10.4g            %10.4g %10.4g" %
+                      (p.real, 1.0, -p.real))
             else:
-                print("%10.4g%+10.4gj    %10.4g    %10.4g" %
-                      (p.real, p.imag, z, w))
-    return wn, zeta, poles
+                print("%10.4g%+10.4gj %10.4g %10.4g" %
+                      (p.real, p.imag, d, w))
+    return wn, damping, poles
 
 
 def evalfr(sys, x, squeeze=None):
diff --git a/control/namedio.py b/control/namedio.py
index c0d5f11d..8f61514f 100644
--- a/control/namedio.py
+++ b/control/namedio.py
@@ -12,7 +12,6 @@ from . import config
 
 __all__ = ['issiso', 'timebase', 'common_timebase', 'timebaseEqual',
            'isdtime', 'isctime']
-
 # Define module default parameter values
 _namedio_defaults = {
     'namedio.state_name_delim': '_',
@@ -21,9 +20,7 @@ _namedio_defaults = {
     'namedio.linearized_system_name_prefix': '',
     'namedio.linearized_system_name_suffix': '$linearized',
     'namedio.sampled_system_name_prefix': '',
-    'namedio.sampled_system_name_suffix': '$sampled',
-    'namedio.converted_system_name_prefix': '',
-    'namedio.converted_system_name_suffix': '$converted',
+    'namedio.sampled_system_name_suffix': '$sampled'
 }
 
 
@@ -52,15 +49,11 @@ class NamedIOSystem(object):
     _idCounter = 0              # Counter for creating generic system name
 
     # Return system name
-    def _name_or_default(self, name=None, prefix_suffix_name=None):
+    def _name_or_default(self, name=None):
         if name is None:
             name = "sys[{}]".format(NamedIOSystem._idCounter)
             NamedIOSystem._idCounter += 1
-        prefix = "" if prefix_suffix_name is None else config.defaults[
-            'namedio.' + prefix_suffix_name + '_system_name_prefix']
-        suffix = "" if prefix_suffix_name is None else config.defaults[
-            'namedio.' + prefix_suffix_name + '_system_name_suffix']
-        return prefix + name + suffix
+        return name
 
     # Check if system name is generic
     def _generic_name_check(self):
@@ -106,24 +99,16 @@ class NamedIOSystem(object):
     def _find_signal(self, name, sigdict):
         return sigdict.get(name, None)
 
-    def _copy_names(self, sys, prefix="", suffix="", prefix_suffix_name=None):
+    def _copy_names(self, sys):
         """copy the signal and system name of sys. Name is given as a keyword
         in case a specific name (e.g. append 'linearized') is desired. """
-        # Figure out the system name and assign it
-        if prefix == "" and prefix_suffix_name is not None:
-            prefix = config.defaults[
-                'namedio.' + prefix_suffix_name + '_system_name_prefix']
-        if suffix == "" and prefix_suffix_name is not None:
-            suffix = config.defaults[
-                'namedio.' + prefix_suffix_name + '_system_name_suffix']
-        self.name = prefix + sys.name + suffix
-
-        # Name the inputs, outputs, and states
-        self.input_index = sys.input_index.copy()
-        self.output_index = sys.output_index.copy()
-        if self.nstates and sys.nstates:
-            # only copy state names for state space systems
-            self.state_index = sys.state_index.copy()
+        self.name = sys.name
+        self.ninputs, self.input_index = \
+            sys.ninputs, sys.input_index.copy()
+        self.noutputs, self.output_index = \
+            sys.noutputs, sys.output_index.copy()
+        self.nstates, self.state_index = \
+            sys.nstates, sys.state_index.copy()
 
     def copy(self, name=None, use_prefix_suffix=True):
         """Make a copy of an input/output system
@@ -143,8 +128,10 @@ class NamedIOSystem(object):
         # Update the system name
         if name is None and use_prefix_suffix:
             # Get the default prefix and suffix to use
+            dup_prefix = config.defaults['namedio.duplicate_system_name_prefix']
+            dup_suffix = config.defaults['namedio.duplicate_system_name_suffix']
             newsys.name = self._name_or_default(
-                self.name, prefix_suffix_name='duplicate')
+                dup_prefix + self.name + dup_suffix)
         else:
             newsys.name = self._name_or_default(name)
 
diff --git a/control/optimal.py b/control/optimal.py
index 50145324..5dfbb34f 100644
--- a/control/optimal.py
+++ b/control/optimal.py
@@ -3,7 +3,7 @@
 # RMM, 11 Feb 2021
 #
 
-"""The :mod:`control.optimal` module provides support for optimization-based
+"""The :mod:`~control.optimal` module provides support for optimization-based
 controllers for nonlinear systems with state and input constraints.
 
 The docstring examples assume that the following import commands::
diff --git a/control/statesp.py b/control/statesp.py
index d3d1ab1d..41f92ae2 100644
--- a/control/statesp.py
+++ b/control/statesp.py
@@ -170,9 +170,9 @@ class StateSpace(LTI):
 
     The StateSpace class is used to represent state-space realizations of
     linear time-invariant (LTI) systems:
-
+    
     .. math::
-
+    
           dx/dt &= A x + B u \\
               y &= C x + D u
 
@@ -1368,13 +1368,10 @@ class StateSpace(LTI):
         """
         if not self.isctime():
             raise ValueError("System must be continuous time system")
-        if prewarp_frequency is not None:
-            if method in ('bilinear', 'tustin') or \
-                    (method == 'gbt' and alpha == 0.5):
-                Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency
-            else:
-                warn('prewarp_frequency ignored: incompatible conversion')
-                Twarp = Ts
+
+        if (method == 'bilinear' or (method == 'gbt' and alpha == 0.5)) and \
+                prewarp_frequency is not None:
+            Twarp = 2 * np.tan(prewarp_frequency * Ts/2)/prewarp_frequency
         else:
             Twarp = Ts
         sys = (self.A, self.B, self.C, self.D)
@@ -1382,8 +1379,13 @@ class StateSpace(LTI):
         sysd = StateSpace(Ad, Bd, C, D, Ts)
         # copy over the system name, inputs, outputs, and states
         if copy_names:
-            sysd._copy_names(self, prefix_suffix_name='sampled')
-            if name is not None:
+            sysd._copy_names(self)
+            if name is None:
+                sysd.name = \
+                    config.defaults['namedio.sampled_system_name_prefix'] +\
+                    sysd.name + \
+                    config.defaults['namedio.sampled_system_name_suffix']
+            else:
                 sysd.name = name
         # pass desired signal names if names were provided
         return StateSpace(sysd, **kwargs)
@@ -1519,7 +1521,7 @@ class StateSpace(LTI):
 
 
 # TODO: add discrete time check
-def _convert_to_statespace(sys, use_prefix_suffix=False):
+def _convert_to_statespace(sys):
     """Convert a system to state space form (if needed).
 
     If sys is already a state space, then it is returned.  If sys is a
@@ -1556,10 +1558,10 @@ def _convert_to_statespace(sys, use_prefix_suffix=False):
                            denorder, den, num, tol=0)
 
             states = ssout[0]
-            newsys = StateSpace(
+            return StateSpace(
                 ssout[1][:states, :states], ssout[2][:states, :sys.ninputs],
-                ssout[3][:sys.noutputs, :states], ssout[4], sys.dt)
-
+                ssout[3][:sys.noutputs, :states], ssout[4], sys.dt,
+                inputs=sys.input_labels, outputs=sys.output_labels)
         except ImportError:
             # No Slycot.  Scipy tf->ss can't handle MIMO, but static
             # MIMO is an easy special case we can check for here
@@ -1572,7 +1574,7 @@ def _convert_to_statespace(sys, use_prefix_suffix=False):
                 for i, j in itertools.product(range(sys.noutputs),
                                               range(sys.ninputs)):
                     D[i, j] = sys.num[i][j][0] / sys.den[i][j][0]
-                newsys = StateSpace([], [], [], D, sys.dt)
+                return StateSpace([], [], [], D, sys.dt)
             else:
                 if sys.ninputs != 1 or sys.noutputs != 1:
                     raise TypeError("No support for MIMO without slycot")
@@ -1582,13 +1584,9 @@ def _convert_to_statespace(sys, use_prefix_suffix=False):
                 # the squeeze
                 A, B, C, D = \
                     sp.signal.tf2ss(squeeze(sys.num), squeeze(sys.den))
-                newsys = StateSpace(A, B, C, D, sys.dt)
-
-        # Copy over the signal (and system) names
-        newsys._copy_names(
-            sys,
-            prefix_suffix_name='converted' if use_prefix_suffix else None)
-        return newsys
+                return StateSpace(
+                    A, B, C, D, sys.dt, inputs=sys.input_labels,
+                    outputs=sys.output_labels)
 
     elif isinstance(sys, FrequencyResponseData):
         raise TypeError("Can't convert FRD to StateSpace system.")
@@ -1601,6 +1599,7 @@ def _convert_to_statespace(sys, use_prefix_suffix=False):
     except Exception:
         raise TypeError("Can't convert given type to StateSpace system.")
 
+
 # TODO: add discrete time option
 def _rss_generate(
         states, inputs, outputs, cdtype, strictly_proper=False, name=None):
@@ -1778,9 +1777,7 @@ def _mimo2siso(sys, input, output, warn_conversion=False):
         new_B = sys.B[:, input]
         new_C = sys.C[output, :]
         new_D = sys.D[output, input]
-        sys = StateSpace(sys.A, new_B, new_C, new_D, sys.dt,
-                         name=sys.name,
-                         inputs=sys.input_labels[input], outputs=sys.output_labels[output])
+        sys = StateSpace(sys.A, new_B, new_C, new_D, sys.dt)
 
     return sys
 
@@ -1829,9 +1826,7 @@ def _mimo2simo(sys, input, warn_conversion=False):
         #  Y = C*X + D*U
         new_B = sys.B[:, input:input+1]
         new_D = sys.D[:, input:input+1]
-        sys = StateSpace(sys.A, new_B, sys.C, new_D, sys.dt,
-                         name=sys.name,
-                         inputs=sys.input_labels[input], outputs=sys.output_labels)
+        sys = StateSpace(sys.A, new_B, sys.C, new_D, sys.dt)
 
     return sys
 
@@ -1914,8 +1909,7 @@ def tf2ss(*args, **kwargs):
         if not isinstance(sys, TransferFunction):
             raise TypeError("tf2ss(sys): sys must be a TransferFunction "
                             "object.")
-        return StateSpace(_convert_to_statespace(sys, use_prefix_suffix=True),
-                          **kwargs)
+        return StateSpace(_convert_to_statespace(sys), **kwargs)
     else:
         raise ValueError("Needs 1 or 2 arguments; received %i." % len(args))
 
diff --git a/control/timeresp.py b/control/timeresp.py
index 2e25331d..638a0732 100644
--- a/control/timeresp.py
+++ b/control/timeresp.py
@@ -694,10 +694,7 @@ def _process_labels(labels, signal, length):
             raise ValueError("Name dictionary for %s is incomplete" % signal)
 
     # Convert labels to a list
-    if isinstance(labels, str):
-        labels = [labels]
-    else:
-        labels = list(labels)
+    labels = list(labels)
 
     # Make sure the signal list is the right length and type
     if len(labels) != length:
@@ -820,7 +817,7 @@ def _check_convert_array(in_obj, legal_shapes, err_msg_start, squeeze=False,
 # Forced response of a linear system
 def forced_response(sys, T=None, U=0., X0=0., transpose=False,
                     interpolate=False, return_x=None, squeeze=None):
-    """Compute the output of a linear system given the input.
+    """Simulate the output of a linear system.
 
     As a convenience for parameters `U`, `X0`:
     Numbers (scalars) are converted to constant arrays with the correct shape.
@@ -1114,8 +1111,6 @@ def forced_response(sys, T=None, U=0., X0=0., transpose=False,
 
     return TimeResponseData(
         tout, yout, xout, U, issiso=sys.issiso(),
-        output_labels=sys.output_labels, input_labels=sys.input_labels,
-        state_labels=sys.state_labels,
         transpose=transpose, return_x=return_x, squeeze=squeeze)
 
 
@@ -1379,16 +1374,8 @@ def step_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
     # Figure out if the system is SISO or not
     issiso = sys.issiso() or (input is not None and output is not None)
 
-    # Select only the given input and output, if any
-    input_labels = sys.input_labels if input is None \
-        else sys.input_labels[input]
-    output_labels = sys.output_labels if output is None \
-        else sys.output_labels[output]
-
     return TimeResponseData(
         response.time, yout, xout, uout, issiso=issiso,
-        output_labels=output_labels, input_labels=input_labels,
-        state_labels=sys.state_labels,
         transpose=transpose, return_x=return_x, squeeze=squeeze)
 
 
@@ -1616,7 +1603,7 @@ def step_info(sysdata, T=None, T_num=None, yfinal=None,
 def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
                      transpose=False, return_x=False, squeeze=None):
     # pylint: disable=W0622
-    """Compute the initial condition response for a linear system.
+    """Initial condition response of a linear system
 
     If the system has multiple outputs (MIMO), optionally, one output
     may be selected. If no selection is made for the output, all
@@ -1717,15 +1704,9 @@ def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
     # Figure out if the system is SISO or not
     issiso = sys.issiso() or (input is not None and output is not None)
 
-    # Select only the given output, if any
-    output_labels = sys.output_labels if output is None \
-        else sys.output_labels[0]
-
     # Store the response without an input
     return TimeResponseData(
         response.t, response.y, response.x, None, issiso=issiso,
-        output_labels=output_labels, input_labels=None,
-        state_labels=sys.state_labels,
         transpose=transpose, return_x=return_x, squeeze=squeeze)
 
 
@@ -1817,7 +1798,7 @@ def impulse_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
     -----
     This function uses the `forced_response` function to compute the time
     response. For continuous time systems, the initial condition is altered to
-    account for the initial impulse. For discrete-time aystems, the impulse is
+    account for the initial impulse. For discrete-time aystems, the impulse is 
     sized so that it has unit area.
 
     Examples
@@ -1888,16 +1869,8 @@ def impulse_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
     # Figure out if the system is SISO or not
     issiso = sys.issiso() or (input is not None and output is not None)
 
-    # Select only the given input and output, if any
-    input_labels = sys.input_labels if input is None \
-        else sys.input_labels[input]
-    output_labels = sys.output_labels if output is None \
-        else sys.output_labels[output]
-
     return TimeResponseData(
         response.time, yout, xout, uout, issiso=issiso,
-        output_labels=output_labels, input_labels=input_labels,
-        state_labels=sys.state_labels,
         transpose=transpose, return_x=return_x, squeeze=squeeze)
 
 
diff --git a/control/xferfcn.py b/control/xferfcn.py
index 60a40eca..a6a00c5d 100644
--- a/control/xferfcn.py
+++ b/control/xferfcn.py
@@ -1134,7 +1134,7 @@ class TransferFunction(LTI):
             Method to use for sampling:
 
             * gbt: generalized bilinear transformation
-            * bilinear or tustin: Tustin's approximation ("gbt" with alpha=0.5)
+            * bilinear: Tustin's approximation ("gbt" with alpha=0.5)
             * euler: Euler (or forward difference) method ("gbt" with alpha=0)
             * backward_diff: Backwards difference ("gbt" with alpha=1.0)
             * zoh: zero-order hold (default)
@@ -1192,13 +1192,9 @@ class TransferFunction(LTI):
         if method == "matched":
             return _c2d_matched(self, Ts)
         sys = (self.num[0][0], self.den[0][0])
-        if prewarp_frequency is not None:
-            if method in ('bilinear', 'tustin') or \
-                    (method == 'gbt' and alpha == 0.5):
-                Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency
-            else:
-                warn('prewarp_frequency ignored: incompatible conversion')
-                Twarp = Ts
+        if (method == 'bilinear' or (method == 'gbt' and alpha == 0.5)) and \
+                prewarp_frequency is not None:
+            Twarp = 2*np.tan(prewarp_frequency*Ts/2)/prewarp_frequency
         else:
             Twarp = Ts
         numd, dend, _ = cont2discrete(sys, Twarp, method, alpha)
@@ -1206,8 +1202,13 @@ class TransferFunction(LTI):
         sysd = TransferFunction(numd[0, :], dend, Ts)
         # copy over the system name, inputs, outputs, and states
         if copy_names:
-            sysd._copy_names(self, prefix_suffix_name='sampled')
-            if name is not None:
+            sysd._copy_names(self)
+            if name is None:
+                sysd.name = \
+                    config.defaults['namedio.sampled_system_name_prefix'] +\
+                    sysd.name + \
+                    config.defaults['namedio.sampled_system_name_suffix']
+            else:
                 sysd.name = name
         # pass desired signal names if names were provided
         return TransferFunction(sysd, name=name, **kwargs)
@@ -1433,8 +1434,7 @@ def _add_siso(num1, den1, num2, den2):
     return num, den
 
 
-def _convert_to_transfer_function(
-        sys, inputs=1, outputs=1, use_prefix_suffix=False):
+def _convert_to_transfer_function(sys, inputs=1, outputs=1):
     """Convert a system to transfer function form (if needed).
 
     If sys is already a transfer function, then it is returned.  If sys is a
@@ -1510,10 +1510,7 @@ def _convert_to_transfer_function(
                 num = squeeze(num)  # Convert to 1D array
                 den = squeeze(den)  # Probably not needed
 
-        newsys = TransferFunction(num, den, sys.dt)
-        if use_prefix_suffix:
-            newsys._copy_names(sys, prefix_suffix_name='converted')
-        return newsys
+        return TransferFunction(num, den, sys.dt)
 
     elif isinstance(sys, (int, float, complex, np.number)):
         num = [[[sys] for j in range(inputs)] for i in range(outputs)]
@@ -1813,8 +1810,7 @@ def ss2tf(*args, **kwargs):
             if not kwargs.get('outputs'):
                 kwargs['outputs'] = sys.output_labels
             return TransferFunction(
-                _convert_to_transfer_function(sys, use_prefix_suffix=True),
-                **kwargs)
+                _convert_to_transfer_function(sys), **kwargs)
         else:
             raise TypeError(
                 "ss2tf(sys): sys must be a StateSpace object.  It is %s."
diff --git a/doc/classes.rst b/doc/classes.rst
index 8564533b..87ce457d 100644
--- a/doc/classes.rst
+++ b/doc/classes.rst
@@ -58,8 +58,3 @@ Additional classes
    flatsys.SystemTrajectory
    optimal.OptimalControlProblem
    optimal.OptimalControlResult
-   optimal.OptimalEstimationProblem
-   optimal.OptimalEstimationResult
-
-The use of these classes is described in more detail in the
-:ref:`flatsys-module` module and the :ref:`optimal-module` module
diff --git a/doc/conventions.rst b/doc/conventions.rst
index 7c9c1ec6..5dc2e3d7 100644
--- a/doc/conventions.rst
+++ b/doc/conventions.rst
@@ -11,6 +11,7 @@ way that different types of standard information used by the library.
 Throughout this manual, we assume the `control` package has been
 imported as `ct`.
 
+
 LTI system representation
 =========================
 
@@ -131,40 +132,11 @@ constructor for the desired data type using the original system as the sole
 argument or using the explicit conversion functions :func:`ss2tf` and
 :func:`tf2ss`.
 
-Simulating LTI systems
-======================
-
-A number of functions are available for computing the output (and
-state) response of an LTI systems:
-
-.. autosummary::
-   :toctree: generated/
-   :template: custom-class-template.rst
-
-    initial_response
-    step_response
-    impulse_response
-    forced_response
-
-Each of these functions returns a :class:`TimeResponseData` object
-that contains the data for the time response (described in more detail
-in the next section).
-
-The :func:`forced_response` system is the most general and allows by
-the zero initial state response to be simulated as well as the
-response from a non-zero intial condition.
-
-In addition the :func:`input_output_response` function, which handles
-simulation of nonlinear systems and interconnected systems, can be
-used.  For an LTI system, results are generally more accurate using
-the LTI simulation functions above.  The :func:`input_output_response`
-function is described in more detail in the :ref:`iosys-module` section.
-
 .. currentmodule:: control
 .. _time-series-convention:
 
 Time series data
-----------------
+================
 A variety of functions in the library return time series data: sequences of
 values that change over time.  A common set of conventions is used for
 returning such data: columns represent different points in time, rows are
@@ -290,16 +262,16 @@ Selected variables that can be configured, along with their default values:
 
   * freqplot.dB (False): Bode plot magnitude plotted in dB (otherwise powers
     of 10)
-
+    
   * freqplot.deg (True): Bode plot phase plotted in degrees (otherwise radians)
-
+    
   * freqplot.Hz (False): Bode plot frequency plotted in Hertz (otherwise
     rad/sec)
-
+    
   * freqplot.grid (True): Include grids for magnitude and phase plots
-
+    
   * freqplot.number_of_samples (1000): Number of frequency points in Bode plots
-
+    
   * freqplot.feature_periphery_decade (1.0): How many decades to include in
     the frequency range on both sides of features (poles, zeros).
 
diff --git a/examples/scherer_etal_ex7_H2_h2syn.py b/examples/scherer_etal_ex7_H2_h2syn.py
index 0b47dd8a..c1f276ab 100644
--- a/examples/scherer_etal_ex7_H2_h2syn.py
+++ b/examples/scherer_etal_ex7_H2_h2syn.py
@@ -1,6 +1,6 @@
 """H2 design using h2syn.
 
-Demonstrate H2 desing for a SISO plant using h2syn. Based on [1], Ex. 7.
+Demonstrate H2 design for a SISO plant using h2syn. Based on [1], Ex. 7.
 
 [1] Scherer, Gahinet, & Chilali, "Multiobjective Output-Feedback Control via
 LMI Optimization", IEEE Trans. Automatic Control, Vol. 42, No. 7, July 1997.
diff --git a/examples/scherer_etal_ex7_Hinf_hinfsyn.py b/examples/scherer_etal_ex7_Hinf_hinfsyn.py
index 97318115..bdbdba01 100644
--- a/examples/scherer_etal_ex7_Hinf_hinfsyn.py
+++ b/examples/scherer_etal_ex7_Hinf_hinfsyn.py
@@ -1,6 +1,6 @@
 """Hinf design using hinfsyn.
 
-Demonstrate Hinf desing for a SISO plant using h2syn. Based on [1], Ex. 7.
+Demonstrate Hinf design for a SISO plant using h2syn. Based on [1], Ex. 7.
 
 [1] Scherer, Gahinet, & Chilali, "Multiobjective Output-Feedback Control via
 LMI Optimization", IEEE Trans. Automatic Control, Vol. 42, No. 7, July 1997.


Test patch:
diff --git a/control/tests/discrete_test.py b/control/tests/discrete_test.py
index 4415fac0..4cf28a21 100644
--- a/control/tests/discrete_test.py
+++ b/control/tests/discrete_test.py
@@ -376,51 +376,28 @@ class TestDiscrete:
     @pytest.mark.parametrize("plantname",
                              ["siso_ss1c",
                               "siso_tf1c"])
-    @pytest.mark.parametrize("wwarp",
-                             [.1, 1, 3])
-    @pytest.mark.parametrize("Ts",
-                             [.1, 1])
-    @pytest.mark.parametrize("discretization_type",
-                             ['bilinear', 'tustin', 'gbt'])
-    def test_sample_system_prewarp(self, tsys, plantname, discretization_type, wwarp, Ts):
+    def test_sample_system_prewarp(self, tsys, plantname):
         """bilinear approximation with prewarping test"""
+        wwarp = 50
+        Ts = 0.025
         # test state space version
         plant = getattr(tsys, plantname)
         plant_fr = plant(wwarp * 1j)
-        alpha = 0.5 if discretization_type == 'gbt' else None
 
-        plant_d_warped = plant.sample(Ts, discretization_type,
-            prewarp_frequency=wwarp, alpha=alpha)
+        plant_d_warped = plant.sample(Ts, 'bilinear', prewarp_frequency=wwarp)
         dt = plant_d_warped.dt
         plant_d_fr = plant_d_warped(np.exp(wwarp * 1.j * dt))
         np.testing.assert_array_almost_equal(plant_fr, plant_d_fr)
 
-        plant_d_warped = sample_system(plant, Ts, discretization_type,
-            prewarp_frequency=wwarp, alpha=alpha)
+        plant_d_warped = sample_system(plant, Ts, 'bilinear',
+                prewarp_frequency=wwarp)
         plant_d_fr = plant_d_warped(np.exp(wwarp * 1.j * dt))
         np.testing.assert_array_almost_equal(plant_fr, plant_d_fr)
 
-        plant_d_warped = c2d(plant, Ts, discretization_type,
-            prewarp_frequency=wwarp, alpha=alpha)
+        plant_d_warped = c2d(plant, Ts, 'bilinear', prewarp_frequency=wwarp)
         plant_d_fr = plant_d_warped(np.exp(wwarp * 1.j * dt))
         np.testing.assert_array_almost_equal(plant_fr, plant_d_fr)
 
-    @pytest.mark.parametrize("plantname",
-                             ["siso_ss1c",
-                              "siso_tf1c"])
-    @pytest.mark.parametrize("discretization_type",
-                             ['euler', 'backward_diff', 'zoh'])
-    def test_sample_system_prewarp_warning(self, tsys, plantname, discretization_type):
-        plant = getattr(tsys, plantname)
-        wwarp = 1
-        Ts = 0.1
-        with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"):
-            plant_d_warped = plant.sample(Ts, discretization_type, prewarp_frequency=wwarp)
-        with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"):
-            plant_d_warped = sample_system(plant, Ts, discretization_type, prewarp_frequency=wwarp)
-        with pytest.warns(UserWarning, match="prewarp_frequency ignored: incompatible conversion"):
-            plant_d_warped = c2d(plant, Ts, discretization_type, prewarp_frequency=wwarp)
-
     def test_sample_system_errors(self, tsys):
         # Check errors
         with pytest.raises(ValueError):
@@ -469,11 +446,11 @@ class TestDiscrete:
         np.testing.assert_array_almost_equal(omega, omega_out)
         np.testing.assert_array_almost_equal(mag_out, np.absolute(H_z))
         np.testing.assert_array_almost_equal(phase_out, np.angle(H_z))
-
+    
     def test_signal_names(self, tsys):
         "test that signal names are preserved in conversion to discrete-time"
-        ssc = StateSpace(tsys.siso_ss1c,
-            inputs='u', outputs='y', states=['a', 'b', 'c'])
+        ssc = StateSpace(tsys.siso_ss1c, 
+            inputs='u', outputs='y', states=['a', 'b', 'c']) 
         ssd = ssc.sample(0.1)
         tfc = TransferFunction(tsys.siso_tf1c, inputs='u', outputs='y')
         tfd = tfc.sample(0.1)
@@ -490,7 +467,7 @@ class TestDiscrete:
         assert ssd.output_labels == ['y']
         assert tfd.input_labels == ['u']
         assert tfd.output_labels == ['y']
-
+    
         # system names and signal name override
         sysc = StateSpace(1.1, 1, 1, 1, inputs='u', outputs='y', states='a')
 
@@ -511,14 +488,14 @@ class TestDiscrete:
         assert sysd_nocopy.find_state('a') is None
 
         # if signal names are provided, they should override those of sysc
-        sysd_newnames = sample_system(sysc, 0.1,
+        sysd_newnames = sample_system(sysc, 0.1, 
             inputs='v', outputs='x', states='b')
         assert sysd_newnames.find_input('v') == 0
         assert sysd_newnames.find_input('u') is None
         assert sysd_newnames.find_output('x') == 0
         assert sysd_newnames.find_output('y') is None
         assert sysd_newnames.find_state('b') == 0
-        assert sysd_newnames.find_state('a') is None
+        assert sysd_newnames.find_state('a') is None        
         # test just one name
         sysd_newnames = sample_system(sysc, 0.1, inputs='v')
         assert sysd_newnames.find_input('v') == 0
diff --git a/control/tests/interconnect_test.py b/control/tests/interconnect_test.py
index cf59c8c1..2c29aeac 100644
--- a/control/tests/interconnect_test.py
+++ b/control/tests/interconnect_test.py
@@ -68,13 +68,8 @@ def test_interconnect_implicit():
     ki = ct.tf(random.uniform(1, 10), [1, 0])
     C = ct.tf2io(kp + ki, inputs='e', outputs='u', name='C')
 
-    # same but static C2
-    C2 = ct.tf(random.uniform(1, 10), 1,
-        inputs='e', outputs='u', name='C2')
-
     # Block diagram computation
     Tss = ct.feedback(P * C, 1)
-    Tss2 = ct.feedback(P * C2, 1)
 
     # Construct the interconnection explicitly
     Tio_exp = ct.interconnect(
@@ -98,15 +93,6 @@ def test_interconnect_implicit():
     np.testing.assert_almost_equal(Tio_sum.C, Tss.C)
     np.testing.assert_almost_equal(Tio_sum.D, Tss.D)
 
-    # test whether signal names work for static system C2
-    Tio_sum2 = ct.interconnect(
-        [C2, P, sumblk], inputs='r', outputs='y')
-
-    np.testing.assert_almost_equal(Tio_sum2.A, Tss2.A)
-    np.testing.assert_almost_equal(Tio_sum2.B, Tss2.B)
-    np.testing.assert_almost_equal(Tio_sum2.C, Tss2.C)
-    np.testing.assert_almost_equal(Tio_sum2.D, Tss2.D)
-
     # Setting connections to False should lead to an empty connection map
     empty = ct.interconnect(
         (C, P, sumblk), connections=False, inplist=['r'], outlist=['y'])
@@ -246,54 +232,23 @@ def test_string_inputoutput():
     assert P_s2.output_index == {'y2' : 0}
 
 def test_linear_interconnect():
-    tf_ctrl = ct.tf(1, (10.1, 1), inputs='e', outputs='u', name='ctrl')
-    tf_plant = ct.tf(1, (10.1, 1), inputs='u', outputs='y', name='plant')
-    ss_ctrl = ct.ss(1, 2, 1, 0, inputs='e', outputs='u', name='ctrl')
-    ss_plant = ct.ss(1, 2, 1, 0, inputs='u', outputs='y', name='plant')
+    tf_ctrl = ct.tf(1, (10.1, 1), inputs='e', outputs='u')
+    tf_plant = ct.tf(1, (10.1, 1), inputs='u', outputs='y')
+    ss_ctrl = ct.ss(1, 2, 1, 2, inputs='e', outputs='u')
+    ss_plant = ct.ss(1, 2, 1, 2, inputs='u', outputs='y')
     nl_ctrl = ct.NonlinearIOSystem(
-        lambda t, x, u, params: x*x, lambda t, x, u, params: u*x,
-        states=1, inputs='e', outputs='u', name='ctrl')
+        lambda t, x, u, params: x*x, 
+        lambda t, x, u, params: u*x, states=1, inputs='e', outputs='u')
     nl_plant = ct.NonlinearIOSystem(
-        lambda t, x, u, params: x*x, lambda t, x, u, params: u*x,
-        states=1, inputs='u', outputs='y', name='plant')
-    sumblk = ct.summing_junction(inputs=['r', '-y'], outputs=['e'], name='sum')
-
-    # Interconnections of linear I/O systems should be linear I/O system
-    assert isinstance(
-        ct.interconnect([tf_ctrl, tf_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert isinstance(
-        ct.interconnect([ss_ctrl, ss_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert isinstance(
-        ct.interconnect([tf_ctrl, ss_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert isinstance(
-        ct.interconnect([ss_ctrl, tf_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-
-    # Interconnections with nonliner I/O systems should not be linear
-    assert ~isinstance(
-        ct.interconnect([nl_ctrl, ss_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert ~isinstance(
-        ct.interconnect([nl_ctrl, tf_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert ~isinstance(
-        ct.interconnect([ss_ctrl, nl_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-    assert ~isinstance(
-        ct.interconnect([tf_ctrl, nl_plant, sumblk], inputs='r', outputs='y'),
-        ct.LinearIOSystem)
-
-    # Implicit converstion of transfer function should retain name
-    clsys = ct.interconnect(
-        [tf_ctrl, ss_plant, sumblk],
-        connections=[
-            ['plant.u', 'ctrl.u'],
-            ['ctrl.e', 'sum.e'],
-            ['sum.y', 'plant.y']
-        ],
-        inplist=['sum.r'], inputs='r',
-        outlist=['plant.y'], outputs='y')
-    assert clsys.syslist[0].name == 'ctrl'
+        lambda t, x, u, params: x*x, 
+        lambda t, x, u, params: u*x, states=1, inputs='u', outputs='y')
+
+    assert isinstance(ct.interconnect((tf_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert isinstance(ct.interconnect((ss_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert isinstance(ct.interconnect((tf_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert isinstance(ct.interconnect((ss_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    
+    assert ~isinstance(ct.interconnect((nl_ctrl, ss_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert ~isinstance(ct.interconnect((nl_ctrl, tf_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert ~isinstance(ct.interconnect((ss_ctrl, nl_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
+    assert ~isinstance(ct.interconnect((tf_ctrl, nl_plant), inputs='e', outputs='y'), ct.LinearIOSystem)
\ No newline at end of file
diff --git a/control/tests/kwargs_test.py b/control/tests/kwargs_test.py
index 83026391..f9400954 100644
--- a/control/tests/kwargs_test.py
+++ b/control/tests/kwargs_test.py
@@ -190,7 +190,6 @@ kwarg_unittest = {
     'tf2io' : test_unrecognized_kwargs,
     'tf2ss' : test_unrecognized_kwargs,
     'sample_system' : test_unrecognized_kwargs,
-    'c2d' : test_unrecognized_kwargs,
     'zpk': test_unrecognized_kwargs,
     'flatsys.point_to_point':
         flatsys_test.TestFlatSys.test_point_to_point_errors,
@@ -211,7 +210,7 @@ kwarg_unittest = {
     'NonlinearIOSystem.__init__':
         interconnect_test.test_interconnect_exceptions,
     'StateSpace.__init__': test_unrecognized_kwargs,
-    'StateSpace.sample': test_unrecognized_kwargs,
+    'StateSpace.sample': test_unrecognized_kwargs, 
     'TimeResponseData.__call__': trdata_test.test_response_copy,
     'TransferFunction.__init__': test_unrecognized_kwargs,
     'TransferFunction.sample': test_unrecognized_kwargs,
diff --git a/control/tests/namedio_test.py b/control/tests/namedio_test.py
index cf30b94a..4925e979 100644
--- a/control/tests/namedio_test.py
+++ b/control/tests/namedio_test.py
@@ -90,10 +90,8 @@ fun_notinstance = {
      (lambda t, x, u, params: -x, None),
      {'inputs': 2, 'outputs':2, 'states':2}],
     [ct.ss, ([[1, 2], [3, 4]], [[0], [1]], [[1, 0]], 0), {}],
-    [ct.ss, ([], [], [], 3), {}], # static system
     [ct.StateSpace, ([[1, 2], [3, 4]], [[0], [1]], [[1, 0]], 0), {}],
     [ct.tf, ([1, 2], [3, 4, 5]), {}],
-    [ct.tf, (2, 3), {}], # static system
     [ct.TransferFunction, ([1, 2], [3, 4, 5]), {}],
 ])
 def test_io_naming(fun, args, kwargs):
@@ -114,7 +112,7 @@ def test_io_naming(fun, args, kwargs):
     assert sys_g.name == 'sys[0]'
     assert sys_g.input_labels == [f'u[{i}]' for i in range(sys_g.ninputs)]
     assert sys_g.output_labels == [f'y[{i}]' for i in range(sys_g.noutputs)]
-    if sys_g.nstates is not None:
+    if sys_g.nstates:
         assert sys_g.state_labels == [f'x[{i}]' for i in range(sys_g.nstates)]
 
     #
@@ -130,7 +128,7 @@ def test_io_naming(fun, args, kwargs):
     sys_r.set_outputs(output_labels)
     assert sys_r.output_labels == output_labels
 
-    if sys_g.nstates is not None:
+    if sys_g.nstates:
         state_labels = [f'x{i}' for i in range(sys_g.nstates)]
         sys_r.set_states(state_labels)
         assert sys_r.state_labels == state_labels
@@ -145,7 +143,7 @@ def test_io_naming(fun, args, kwargs):
         sys_k = fun(state_labels, output_labels, input_labels, name='mysys')
 
     elif sys_g.nstates is None:
-        # Don't pass state labels if TransferFunction
+        # Don't pass state labels
         sys_k = fun(
             *args, inputs=input_labels, outputs=output_labels, name='mysys')
 
@@ -157,7 +155,7 @@ def test_io_naming(fun, args, kwargs):
     assert sys_k.name == 'mysys'
     assert sys_k.input_labels == input_labels
     assert sys_k.output_labels == output_labels
-    if sys_g.nstates is not None:
+    if sys_g.nstates:
         assert sys_k.state_labels == state_labels
 
     #
@@ -169,9 +167,6 @@ def test_io_naming(fun, args, kwargs):
         assert sys_ss != sys_r
         assert sys_ss.input_labels == input_labels
         assert sys_ss.output_labels == output_labels
-        if not isinstance(sys_r, ct.StateSpace):
-            # System should get unique name
-            assert sys_ss.name != sys_r.name
 
         # Reassign system and signal names
         sys_ss = ct.ss(
@@ -198,24 +193,6 @@ def test_io_naming(fun, args, kwargs):
         assert sys_tf.input_labels == input_labels
         assert sys_tf.output_labels == output_labels
 
-    #
-    # Convert the system to a LinearIOSystem and make sure labels transfer
-    #
-    if not isinstance(
-            sys_r, (ct.FrequencyResponseData, ct.NonlinearIOSystem)) and \
-                    ct.slycot_check():
-        sys_lio = ct.LinearIOSystem(sys_r)
-        assert sys_lio != sys_r
-        assert sys_lio.input_labels == input_labels
-        assert sys_lio.output_labels == output_labels
-
-        # Reassign system and signal names
-        sys_lio = ct.LinearIOSystem(
-            sys_g, inputs=input_labels, outputs=output_labels, name='new')
-        assert sys_lio.name == 'new'
-        assert sys_lio.input_labels == input_labels
-        assert sys_lio.output_labels == output_labels
-
 
 # Internal testing of StateSpace initialization
 def test_init_namedif():
@@ -244,29 +221,14 @@ def test_init_namedif():
 
 # Test state space conversion
 def test_convert_to_statespace():
-    # Set up the initial systems
-    sys = ct.tf(ct.rss(2, 1, 1), inputs='u', outputs='y', name='sys')
-    sys_static = ct.tf(1, 2, inputs='u', outputs='y', name='sys_static')
-
-    # check that name, inputs, and outputs passed through
-    sys_new = ct.ss(sys)
-    assert sys_new.name == 'sys$converted'
-    assert sys_new.input_labels == ['u']
-    assert sys_new.output_labels == ['y']
-    sys_new = ct.ss(sys_static)
-    assert sys_new.name == 'sys_static$converted'
-    assert sys_new.input_labels == ['u']
-    assert sys_new.output_labels == ['y']
+    # Set up the initial system
+    sys = ct.tf(ct.rss(2, 1, 1))
 
     # Make sure we can rename system name, inputs, outputs
     sys_new = ct.ss(sys, inputs='u', outputs='y', name='new')
     assert sys_new.name == 'new'
     assert sys_new.input_labels == ['u']
     assert sys_new.output_labels == ['y']
-    sys_new = ct.ss(sys_static, inputs='u', outputs='y', name='new')
-    assert sys_new.name == 'new'
-    assert sys_new.input_labels == ['u']
-    assert sys_new.output_labels == ['y']
 
     # Try specifying the state names (via low level test)
     with pytest.warns(UserWarning, match="non-unique state space realization"):
diff --git a/control/tests/nyquist_test.py b/control/tests/nyquist_test.py
index ca3c813a..ddc69e7b 100644
--- a/control/tests/nyquist_test.py
+++ b/control/tests/nyquist_test.py
@@ -370,23 +370,8 @@ def test_nyquist_legacy():
 def test_discrete_nyquist():
     # Make sure we can handle discrete time systems with negative poles
     sys = ct.tf(1, [1, -0.1], dt=1) * ct.tf(1, [1, 0.1], dt=1)
-    ct.nyquist_plot(sys, plot=False)
-
-    # system with a pole at the origin
-    sys = ct.zpk([1,], [.3, 0], 1, dt=True)
-    ct.nyquist_plot(sys, plot=False)
-    sys = ct.zpk([1,], [0], 1, dt=True)
-    ct.nyquist_plot(sys, plot=False)
-
-    # only a pole at the origin
-    sys = ct.zpk([], [0], 2, dt=True)
-    ct.nyquist_plot(sys, plot=False)
-
-    # pole at zero (pure delay)
-    sys = ct.zpk([], [1], 1, dt=True)
-    ct.nyquist_plot(sys, plot=False)
-
-
+    ct.nyquist_plot(sys)
+    
 if __name__ == "__main__":
     #
     # Interactive mode: generate plots for manual viewing
@@ -442,5 +427,5 @@ if __name__ == "__main__":
               np.array2string(sys.poles(), precision=2, separator=','))
     count = ct.nyquist_plot(sys)
 
-
+    
 
diff --git a/control/tests/trdata_test.py b/control/tests/trdata_test.py
index 028e5358..734d3559 100644
--- a/control/tests/trdata_test.py
+++ b/control/tests/trdata_test.py
@@ -196,20 +196,15 @@ def test_response_copy():
     with pytest.raises(ValueError, match="not enough"):
         t, y, x = response_mimo
 
-    # Make sure labels are transferred to the response
-    assert response_siso.output_labels == sys_siso.output_labels
-    assert response_siso.state_labels == sys_siso.state_labels
-    assert response_siso.input_labels == sys_siso.input_labels
-    assert response_mimo.output_labels == sys_mimo.output_labels
-    assert response_mimo.state_labels == sys_mimo.state_labels
-    assert response_mimo.input_labels == sys_mimo.input_labels
-
-    # Check relabelling
+    # Labels
+    assert response_mimo.output_labels is None
+    assert response_mimo.state_labels is None
+    assert response_mimo.input_labels is None
     response = response_mimo(
         output_labels=['y1', 'y2'], input_labels='u',
-        state_labels=["x%d" % i for i in range(4)])
+        state_labels=["x[%d]" % i for i in range(4)])
     assert response.output_labels == ['y1', 'y2']
-    assert response.state_labels == ['x0', 'x1', 'x2', 'x3']
+    assert response.state_labels == ['x[0]', 'x[1]', 'x[2]', 'x[3]']
     assert response.input_labels == ['u']
 
     # Unknown keyword
@@ -236,17 +231,6 @@ def test_trdata_labels():
     np.testing.assert_equal(
         response.input_labels, ["u[%d]" % i for i in range(sys.ninputs)])
 
-    # Make sure the selected input and output are both correctly transferred to the response
-    for nu in range(sys.ninputs):
-        for ny in range(sys.noutputs):
-            step_response = ct.step_response(sys, T, input=nu, output=ny)
-            assert step_response.input_labels == [sys.input_labels[nu]]
-            assert step_response.output_labels == [sys.output_labels[ny]]
-
-            init_response = ct.initial_response(sys, T, input=nu, output=ny)
-            assert init_response.input_labels == None
-            assert init_response.output_labels == [sys.output_labels[ny]]
-
 
 def test_trdata_multitrace():
     #
diff --git a/control/tests/xferfcn_test.py b/control/tests/xferfcn_test.py
index 7d561e77..ebb781b8 100644
--- a/control/tests/xferfcn_test.py
+++ b/control/tests/xferfcn_test.py
@@ -1262,12 +1262,6 @@ def test_copy_names(create, args, kwargs, convert):
     if cpy.nstates is not None and sys.nstates is not None:
         assert cpy.state_labels == sys.state_labels
 
-    # Make sure that names aren't the same if system changed type
-    if not isinstance(cpy, create):
-        assert cpy.name == sys.name + '$converted'
-    else:
-        assert cpy.name == sys.name
-
     # Relabel inputs and outputs
     cpy = convert(sys, inputs='myin', outputs='myout')
     assert cpy.input_labels == ['myin']