lynx.Diagram.get_ss

Diagram.get_ss(from_signal: str, to_signal: str) Any[source]

Extract state-space model from one signal to another.

Converts a Lynx diagram to a python-control StateSpace object representing the system dynamics from from_signal to to_signal. Automatically handles subsystem extraction, sign negation for sum blocks, and connection routing.

Signal Reference Patterns (4-tier priority system):
  1. IOMarker labels (highest priority): Use the ‘label’ parameter from InputMarker or OutputMarker blocks (e.g., ‘r’, ‘y’, ‘u’)

  2. Connection labels: Reference labeled connections between blocks (e.g., ‘error’, ‘control’)

  3. Block.port format: Explicit block ID and port using dot notation (e.g., ‘controller.out’, ‘plant.in’)

  4. Block labels (lowest priority, SISO only): For single-input/single-output blocks, reference by block label (e.g., ‘controller’, ‘plant’)

Parameters:
  • from_signal – Source signal name using any of the 4 reference patterns

  • to_signal – Destination signal name using any of the 4 reference patterns

Returns:

State-space system from from_signal → to_signal

Return type:

control.StateSpace

Raises:
  • SignalNotFoundError – If either signal doesn’t exist in the diagram

  • ValidationError – If diagram has missing I/O markers or unconnected ports

  • DiagramExportError – If python-control conversion fails

Examples

>>> # Complete feedback loop example
>>> diagram = Diagram()
>>> diagram.add_block('io_marker', 'ref', marker_type='input', label='r')
>>> diagram.add_block('sum', 'error_sum', signs=['+', '-', '|'])
>>> diagram.add_block('gain', 'controller', K=5.0, label='C')
>>> diagram.add_block('transfer_function', 'plant',
...                  numerator=[2.0], denominator=[1.0, 3.0], label='P')
>>> diagram.add_block(
...     'io_marker', 'output', marker_type='output', label='y'
... )
>>>
>>> diagram.add_connection('c1', 'ref', 'out', 'error_sum', 'in1')
>>> diagram.add_connection(
...     'c2', 'error_sum', 'out', 'controller', 'in', label='e'
... )
>>> diagram.add_connection(
...     'c3', 'controller', 'out', 'plant', 'in', label='u'
... )
>>> diagram.add_connection('c4', 'plant', 'out', 'output', 'in')
>>> diagram.add_connection('c5', 'plant', 'out', 'error_sum', 'in2')
>>>
>>> # Extract closed-loop transfer function (IOMarker labels)
>>> sys_ry = diagram.get_ss('r', 'y')
>>> print(f"DC gain: {sys_ry.dcgain()}")  # Should be ~0.769
>>>
>>> # Extract sensitivity function (IOMarker + connection label)
>>> sys_re = diagram.get_ss('r', 'e')
>>>
>>> # Extract controller transfer function (connection labels)
>>> sys_eu = diagram.get_ss('e', 'u')
>>>
>>> # Extract using block.port format (explicit)
>>> sys_cp = diagram.get_ss('controller.out', 'plant.out')
>>>
>>> # Extract using block labels (SISO blocks only)
>>> sys_CP = diagram.get_ss('C', 'P')  # Same as controller → plant