graph TD
A[Start: What data do you already trust?] --> B{Measured frequency response points?}
B -- Yes --> C[Use FRD]
B -- No --> D{Have A, B, C, D matrices?}
D -- Yes --> E[Use STATE_SPACE]
E --> F{Need polynomial form for reporting/classical tools?}
F -- Yes --> G[Use SS2TF]
F -- No --> H{Need digital implementation?}
H -- Yes --> I[Use C2D]
H -- No --> J[Stay in STATE_SPACE]
D -- No --> K{Have numerator/denominator coefficients?}
K -- Yes --> L[Use TRANSFER_FUNCTION]
L --> M{Need state realization?}
M -- Yes --> N[Use TF2SS]
M -- No --> O{Need explicit poles and zeros?}
O -- Yes --> P[Use ZPK]
O -- No --> Q{Need digital implementation?}
Q -- Yes --> I
Q -- No --> R[Keep TRANSFER_FUNCTION]
K -- No --> S{Need to include pure delay in rational model?}
S -- Yes --> T[Use PADE_DELAY then combine with model]
S -- No --> U[Collect/derive model structure first]
Modeling
Overview
Introduction Control systems modeling is the discipline of representing dynamic systems in forms that are analyzable, simulatable, and implementable. In practice, engineers model how inputs, disturbances, and controller actions produce outputs over time, then use those models to design stable and performant control laws. Mathematically, most classical workflows assume linear time-invariant (LTI) behavior around an operating point, which enables equivalent representations in transfer-function form, state-space form, zero-pole-gain form, and frequency-response form. A concise introduction to the broader field is available in Control theory (Wikipedia).
For technical teams, this matters because modeling quality directly affects decision quality. If a process engineer is tuning a distillation column controller, a robotics team is stabilizing an inverted pendulum, or a power-electronics team is shaping converter dynamics, the model determines whether simulation insights transfer to production. Poor representation choices can hide nonminimum-phase behavior, misstate dead-time effects, or create numerical conditioning problems that slow model-based design. Good representation choices reduce risk by making pole locations, gain margins, and implementation constraints visible early.
Boardflare’s control-system modeling category is built around the python-control ecosystem and focuses on practical transformations among canonical forms. The tools include TRANSFER_FUNCTION and ZPK for compact SISO polynomial/root-factor models, STATE_SPACE for matrix-based dynamic models that scale to MIMO systems, TF2SS and SS2TF for moving between polynomial and state-variable realizations, C2D for digital implementation via sampling methods, PADE_DELAY for rational approximations of transport delay, and FRD for working directly from measured frequency response points. Together, these functions support the full “identify → represent → convert → discretize → validate” workflow.
From a mathematical perspective, these are equivalent views of the same underlying dynamics when assumptions hold. If the model is linear and time invariant, transfer function, state-space, and zero-pole-gain encodings can often be converted back and forth without changing external input-output behavior. The main differences are interpretability, numerical behavior, and convenience for downstream tasks. For example, transfer functions are compact for single-input single-output (SISO) loop shaping, while state-space is generally preferred for multivariable design, observer synthesis, and simulation with internal states. FRD complements both by preserving measured behavior even when no low-order rational model is trustworthy.
When to Use It Teams typically choose this category when they need a reliable bridge between physical process intuition and implementation-ready control code. The “job to be done” is not merely to write an equation; it is to create a model representation that matches the question being asked.
A first common scenario is digital controller deployment from a continuous design. An engineer may derive a plant as G(s) from first principles or lab fitting, perform compensator design in continuous time, and then need a robust sampled model for firmware. Here, TRANSFER_FUNCTION or ZPK can define the original plant, PADE_DELAY can introduce approximated dead time if transport lag is present, and C2D converts to a discrete equivalent at the target sample period. This workflow is especially useful in embedded motion control, motor drives, and process loops where sampling effects must be visible before commissioning.
A second scenario is multivariable process modeling and architecture decisions. In HVAC, chemical plants, and aerospace subsystems, engineers often work with coupled states and multiple actuators/sensors. The practical task is to assess controllability, observability, interaction, and transient behavior with matrix tools. STATE_SPACE is typically the natural starting point for these systems. If legacy documentation or tuning artifacts are transfer-function based, TF2SS brings those models into a state-space form for modern methods; SS2TF then allows selective projection back into transfer functions for communication, verification, or controller handoff to teams using classical frequency-domain methods.
A third scenario is test-data-driven model handling and acceptance checks. In many industrial settings, the most trusted information is frequency sweep data from commissioning tests rather than a symbolic model. The immediate need is to compare measured response against specifications, not force an uncertain parametric fit. FRD addresses this directly by encoding complex response versus frequency, preserving measured phase and gain behavior. Teams can use this to verify resonance suppression, bandwidth targets, and robustness trends before deciding whether to fit or refine a rational transfer-function model.
Other routine jobs include: converting between model forms for vendor interoperability; creating explainable documentation of poles/zeros for design review; and reducing handoff errors between controls, simulation, and software teams by standardizing representations across tooling. In all cases, the value is operational: fewer late-stage surprises, faster tuning cycles, and clearer traceability from requirement to deployed controller logic.
How It Works Under the LTI assumption, system dynamics can be written in multiple mathematically connected forms.
In state-space form, \dot{x}(t) = A x(t) + B u(t), \qquad y(t) = C x(t) + D u(t) where x is the state vector, u the input vector, and y the output vector. This form is the foundation for STATE_SPACE, and it scales naturally to MIMO systems.
The corresponding transfer matrix is G(s) = C(sI - A)^{-1}B + D, which is exactly what SS2TF computes for the represented input-output channels. Conversely, TF2SS constructs one valid state realization from polynomial coefficients. Because realizations are not unique, two different A,B,C,D sets can represent the same transfer function while yielding identical external input-output behavior.
In polynomial transfer-function form, G(s) = \frac{b_m s^m + \cdots + b_1 s + b_0}{a_n s^n + \cdots + a_1 s + a_0}, which is encoded by TRANSFER_FUNCTION. This representation is concise and useful for root-locus, Bode, and loop-shaping discussions. Its equivalent factorized root form is G(s) = k \frac{\prod_{i=1}^{m}(s-z_i)}{\prod_{j=1}^{n}(s-p_j)}, implemented by ZPK. In many design reviews, ZPK is preferred when pole/zero locations convey design intent more directly than long polynomial coefficients.
For dead time, ideal transport delay appears as G_{delay}(s) = e^{-sT}, which is not rational and therefore does not map directly into finite-dimensional polynomial forms. PADE_DELAY uses Padé approximation to replace the delay with e^{-sT} \approx \frac{N_n(s)}{D_n(s)} for chosen order n (and optional numerator degree). Higher order usually improves approximation near the expansion region but can introduce additional dynamics that affect robustness interpretation. Practically, engineers choose the lowest order that preserves decision-relevant behavior across the frequency range of interest.
Digital implementation requires discretization. Given a continuous model G(s) and sample period T_s, C2D computes a discrete model G(z) using methods such as zero-order hold (ZOH), bilinear/Tustin, Euler, backward difference, or first-order hold. These methods embed different assumptions about intersample input behavior and frequency warping. ZOH is often a default for sampled-data plants driven by held actuator commands; bilinear is frequently used in filter/controller mappings due to stable pole mapping and frequency-domain convenience. Method choice should reflect the physical signal path and fidelity requirements around bandwidth and crossover.
Not all workflows start from parametric equations. FRD stores frequency response directly as complex points G(j\omega_k) at specified frequencies \omega_k. This is especially useful when empirical data is richer or more trustworthy than a low-order fitted model. FRD keeps measured phase/magnitude intact at sample frequencies and avoids overconfidence from overconstrained fitting. Teams can then decide whether to remain in data-driven analysis or proceed to rational approximation for implementation tasks.
The underlying library for these tools is the Python Control Systems Library, documented at python-control docs. Function-level references used in this category include control.c2d, control.frd, control.pade, control.ss2tf, control.ss, control.tf2ss, control.tf, and control.zpk. In Boardflare, these capabilities are exposed in spreadsheet-friendly function wrappers so analysts can operate with matrix ranges and coefficient vectors while still leveraging robust numerical backends.
Modeling assumptions remain critical. The category generally assumes linearization is valid over the operating region being analyzed, parameters are sufficiently known, and signal timing semantics (continuous vs sampled, held vs interpolated) are explicit. Violating these assumptions can create misleading results regardless of representation quality. Typical safeguards include checking pole locations and damping, validating discrete models against continuous references in the intended frequency band, and cross-checking FRD data against simulated responses where possible.
Practical Example Consider a process-control team deploying a digital temperature loop for a thermal chamber with mild transport delay. The team has identified an approximate continuous plant from step-test data and needs a model that is implementation-ready for a PLC running at fixed sample time.
Step 1 is baseline continuous representation. The team encodes the fitted plant with TRANSFER_FUNCTION, for example a low-order stable denominator and physically plausible gain. This provides a compact equation for sanity checks and documentation.
Step 2 is delay modeling. Chamber transport delay appears in tests due to sensor placement and thermal lag. Because exact delay is non-rational, the team generates a Padé approximation using PADE_DELAY, selecting order based on the bandwidth region that matters for control. They multiply or conceptually combine this approximation with the baseline plant to include dead-time effect in classical analysis.
Step 3 is alternate pole/zero interpretation for design review. To communicate resonance and dominant time constants to non-specialists, the team presents the same dynamics in ZPK form. Pole locations make it easier to discuss expected rise time, overshoot risk, and robustness margins than raw coefficient vectors.
Step 4 is conversion for modern state-based analysis. The same model is converted via TF2SS, giving a state-space realization suitable for simulation scenarios that include actuator saturation logic and disturbance channels. The team uses this representation to test startup transients and load disturbances over realistic input sequences.
Step 5 is round-trip verification. To ensure no transcription errors occurred in setup, the team maps the realization back using SS2TF and confirms equivalence of key coefficients or dynamic response. This is a practical quality gate that catches shape mismatches in matrix ranges early.
Step 6 is discretization for implementation. With sample period fixed by PLC cycle time, C2D produces the discrete model for algorithm prototyping. The team compares ZOH and bilinear variants around loop crossover frequencies, then selects the method that best preserves phase and gain behavior relevant to stability targets.
Step 7 is test-data alignment. During commissioning, measured sweep data is loaded into FRD. Engineers compare measured points against predictions from the discretized/continuous models, identifying where unmodeled dynamics appear (for example sensor filtering or actuator lag). This closes the loop between model assumptions and plant reality.
Why this workflow is effective in Boardflare compared with ad hoc spreadsheets: each step keeps representation intent explicit, avoids hidden manual algebra, and relies on tested numerical routines underneath familiar tabular inputs. The team can version assumptions, reuse templates, and keep conversion logic transparent across controls and operations stakeholders.
How to Choose Choosing the right calculator depends on what information is known and what decision must be made next. A useful heuristic is: choose the representation that makes the next engineering decision easiest and least error-prone.
| Function | Best used when | Inputs you need | Strengths | Tradeoffs |
|---|---|---|---|---|
| TRANSFER_FUNCTION | You have polynomial coefficients and need a canonical SISO model | Numerator and denominator coefficients | Compact, familiar, ideal for classical analysis | Less intuitive for high-order MIMO structures |
| ZPK | You reason in poles/zeros or need root-level interpretability | Zero list, pole list, scalar gain | Clear stability and mode interpretation | Requires accurate roots; can be sensitive if roots are poorly known |
| STATE_SPACE | You have or need matrix dynamics, especially MIMO | A,B,C,D matrices | Scales well, supports modern control workflows | Harder to interpret quickly for non-specialists |
| TF2SS | You need a state realization from transfer coefficients | Numerator and denominator coefficients | Bridge from classical to modern workflows | Realization is not unique; state meaning may be abstract |
| SS2TF | You need transfer-function output from matrix models | A,B,C,D matrices | Easy handoff to classical tools and reports | Polynomial form can become unwieldy at high order |
| C2D | You are deploying on digital hardware | Continuous model + sample period + method | Essential for implementation realism | Method choice affects phase/magnitude fidelity |
| PADE_DELAY | You must include dead time in rational LTI analysis | Delay value and approximation order | Enables classical methods with delayed systems | Approximation introduces extra dynamics |
| FRD | You trust measured frequency points more than a fitted equation | Complex response vector + frequency vector | Preserves empirical behavior directly | Not inherently a compact parametric model |
Decision tree:
A practical selection sequence for most teams is: start from the representation closest to trusted data, convert only when a specific next task requires it, and validate equivalence after each conversion. For example, if test data is primary, start with FRD, then fit/convert only when controller synthesis requires a compact parametric form. If first-principles equations are available, TRANSFER_FUNCTION or STATE_SPACE can be initial anchors depending on whether the system is SISO or MIMO and whether internal states are decision-relevant.
For implementation-centric projects, C2D should be treated as a design-stage requirement rather than an afterthought. Sample period and discretization method are part of the model, not merely deployment details. For delay-dominant plants, PADE_DELAY is valuable for classical analysis and quick design cycles, but teams should remain aware that it is an approximation and should be validated against measured behavior, often via FRD.
Finally, conversion tools TF2SS and SS2TF are best viewed as interoperability bridges. They are most effective when used intentionally: convert, verify key dynamics, and proceed with the representation best aligned to the next decision. This representation-aware workflow keeps modeling transparent, reduces handoff friction, and improves confidence that simulated behavior reflects what the deployed control system will actually do.
C2D
Continuous-to-discrete conversion (“discretization”) is essential for implementing control algorithms on digital hardware. This function converts a continuous-time LTI system into a mathematically equivalent discrete-time system, matching the original dynamics as closely as possible at the given sample rate (T_s).
The default method is Zero-Order Hold (ZOH), which assumes the control inputs are held constant between samples. Other available transformation methods include Bilinear (Tustin’s approximation).
Excel Usage
=C2D(sysc, Ts, disc_method)
sysc(list[list], required): The continuous-time transfer function to convert (provided as numerator and denominator coefficients).Ts(float, required): The sampling period (must be > 0) in seconds.disc_method(str, optional, default: “zoh”): Method to use for conversion (‘zoh’, ‘bilinear’, ‘tustin’, ‘euler’, ‘backward_diff’, ‘foh’).
Returns (str): A string representation of the discrete-time system model.
Example 1: ZOH Discretization of 1/s
Inputs:
| sysc | Ts | disc_method |
|---|---|---|
| 1 | 0.1 | zoh |
| 1 |
Excel formula:
=C2D({1;1,0}, 0.1, "zoh")
Expected output:
"<TransferFunction>: sys[0]$sampled\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\ndt = 0.1\n\n 0.1\n -----\n z - 1"
Example 2: Bilinear Discretization of 1/(s+1)
Inputs:
| sysc | Ts | disc_method |
|---|---|---|
| 1 | 0.05 | bilinear |
| 1 |
Excel formula:
=C2D({1;1,1}, 0.05, "bilinear")
Expected output:
"<TransferFunction>: sys[2]$sampled\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\ndt = 0.05\n\n 0.02439 z + 0.02439\n -------------------\n z - 0.9512"
Example 3: Second order system with 1 sec sample
Inputs:
| sysc | Ts | disc_method | |
|---|---|---|---|
| 1 | 2 | 1 | zoh |
| 1 | 3 |
Excel formula:
=C2D({1,2;1,3,2}, 1, "zoh")
Expected output:
"<TransferFunction>: sys[4]$sampled\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\ndt = 1.0\n\n 0.6321 z - 0.08555\n ------------------------\n z^2 - 0.5032 z + 0.04979"
Example 4: First order hold discretization
Inputs:
| sysc | Ts | disc_method |
|---|---|---|
| 2 | 0.01 | foh |
| 1 |
Excel formula:
=C2D({2;1,5}, 0.01, "foh")
Expected output:
"<TransferFunction>: sys[6]$sampled\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\ndt = 0.01\n\n 0.009835 z + 0.009673\n ---------------------\n z - 0.9512"
Python Code
Show Code
import control as ct
import numpy as np
def c2d(sysc, Ts, disc_method='zoh'):
"""
Convert a continuous-time system to discrete-time by sampling.
See: https://python-control.readthedocs.io/en/latest/generated/control.c2d.html
This example function is provided as-is without any representation of accuracy.
Args:
sysc (list[list]): The continuous-time transfer function to convert (provided as numerator and denominator coefficients).
Ts (float): The sampling period (must be > 0) in seconds.
disc_method (str, optional): Method to use for conversion ('zoh', 'bilinear', 'tustin', 'euler', 'backward_diff', 'foh'). Valid options: Zero-Order Hold, Bilinear (Tustin), Tustin Alias, Euler, Backward Difference, First-Order Hold. Default is 'zoh'.
Returns:
str: A string representation of the discrete-time system model.
"""
try:
def flatten_to_1d(x):
if not isinstance(x, list):
return [float(x)] if str(x) != "" else []
flat = []
for row in x:
if isinstance(row, list):
for val in row:
if str(val) != "":
flat.append(float(val))
elif str(row) != "":
flat.append(float(row))
return flat
# For simplicity in Excel, we will assume sysc is provided as a Transfer Function
# with num and den in consecutive rows
if len(sysc) < 2:
return "Error: sysc must contain at least two rows (numerator and denominator)"
num = flatten_to_1d(sysc[0])
den = flatten_to_1d(sysc[1])
if not num or not den:
return "Error: Invalid numerator or denominator arrays"
sys_continuous = ct.tf(num, den)
sample_time = float(Ts)
if sample_time <= 0:
return "Error: Sampling time Ts must be greater than zero"
valid_methods = ['zoh', 'bilinear', 'tustin', 'euler', 'backward_diff', 'foh']
conv_method = disc_method.lower() if isinstance(disc_method, str) else 'zoh'
if conv_method not in valid_methods:
return f"Error: Invalid method '{conv_method}'. Valid options are {', '.join(valid_methods)}"
sys_discrete = ct.c2d(sys_continuous, sample_time, method=conv_method)
return str(sys_discrete)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
FRD
A Frequency Response Data (FRD) model stores the measured or simulated frequency response of a system directly as complex numbers at specific frequencies.
Rather than representing an idealized mathematical model (like state-space or transfer functions), FRD models capture the actual empirical magnitude and phase behavior of a system across a defined frequency vector (\omega).
Excel Usage
=FRD(frdata, omega)
frdata(list[list], required): Complex vector with the system response.omega(list[list], required): Vector of frequencies (in rad/s) at which the response was evaluated or measured.
Returns (str): String representation of the FRD model.
Example 1: Simple magnitude response
Inputs:
| frdata | omega | ||||
|---|---|---|---|---|---|
| 1 | 1 | 0.5 | 1 | 10 | 100 |
Excel formula:
=FRD({1,1,0.5}, {1,10,100})
Expected output:
"<FrequencyResponseData>: sys[0]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\nFreq [rad/s] Response\n------------ ---------------------\n 1.000 1 +0j\n 10.000 1 +0j\n 100.000 0.5 +0j"
Example 2: Synthetic complex frequency response
Inputs:
| frdata | omega | ||||
|---|---|---|---|---|---|
| 1 | 0.5 | 0.1 | 0.1 | 1 | 10 |
Excel formula:
=FRD({1,0.5,0.1}, {0.1,1,10})
Expected output:
"<FrequencyResponseData>: sys[1]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\nFreq [rad/s] Response\n------------ ---------------------\n 0.100 1 +0j\n 1.000 0.5 +0j\n 10.000 0.1 +0j"
Example 3: Single frequency point
Inputs:
| frdata | omega |
|---|---|
| 0.866 | 50 |
Excel formula:
=FRD({0.866}, {50})
Expected output:
"<FrequencyResponseData>: sys[2]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\nFreq [rad/s] Response\n------------ ---------------------\n 50.000 0.866 +0j"
Example 4: Simulating a low pass filter roll off
Inputs:
| frdata | omega | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 1 | 0.99 | 0.707 | 0.1 | 0.01 | 0.1 | 1 | 10 | 100 | 1000 |
Excel formula:
=FRD({1,0.99,0.707,0.1,0.01}, {0.1,1,10,100,1000})
Expected output:
"<FrequencyResponseData>: sys[3]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\nFreq [rad/s] Response\n------------ ---------------------\n 0.100 1 +0j\n 1.000 0.99 +0j\n 10.000 0.707 +0j\n 100.000 0.1 +0j\n 1000.000 0.01 +0j"
Python Code
Show Code
import control as ct
import numpy as np
def frd(frdata, omega):
"""
Create a frequency response data (FRD) model from measured response data.
See: https://python-control.readthedocs.io/en/latest/generated/control.frd.html
This example function is provided as-is without any representation of accuracy.
Args:
frdata (list[list]): Complex vector with the system response.
omega (list[list]): Vector of frequencies (in rad/s) at which the response was evaluated or measured.
Returns:
str: String representation of the FRD model.
"""
try:
def flatten_to_1d(x):
if not isinstance(x, list):
return [x] if str(x) != "" else []
flat = []
for row in x:
if isinstance(row, list):
for val in row:
if str(val) != "":
flat.append(complex(val) if isinstance(val, (complex, str)) else float(val))
elif str(row) != "":
flat.append(complex(row) if isinstance(row, (complex, str)) else float(row))
return flat
response = flatten_to_1d(frdata)
freqs = flatten_to_1d(omega)
if len(response) != len(freqs):
return "Error: Response data and frequency data must have the same length"
if len(freqs) == 0:
return "Error: Data vectors cannot be empty"
sys = ct.frd(response, freqs)
return str(sys)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
PADE_DELAY
Time delays (dead time) are common in process control, representing the transport delay before an output responds to an input. In the Laplace domain, a strict time delay T is represented by the exponential term e^{-sT}.
However, e^{-sT} is not a rational polynomial, making it difficult to use with standard linear analysis tools (like Root Locus or state-space matrices). The Padé approximation is a technique to approximate this delay using a standard continuous-time transfer function (a ratio of polynomials).
The function computes the numerator and denominator coefficients for an n-th order Padé approximation: e^{-sT} \approx \frac{num(s)}{den(s)}
Excel Usage
=PADE_DELAY(T, n, numdeg)
T(float, required): The time delay to approximate (in seconds).n(int, required): The order (degree) of the denominator polynomial approximation.numdeg(int, optional, default: null): Optional order of the numerator. If left blank, it equals the denominator degree.
Returns (list[list]): A 2D array where the first row contains the numerator coefficients and the second row contains the denominator coefficients.
Example 1: First order approximation of 1s delay
Inputs:
| T | n | numdeg |
|---|---|---|
| 1 | 1 |
Excel formula:
=PADE_DELAY(1, 1, )
Expected output:
| Result | |
|---|---|
| -1 | 2 |
| 1 | 2 |
Example 2: Third order approximation of 0.5s delay
Inputs:
| T | n | numdeg |
|---|---|---|
| 0.5 | 3 |
Excel formula:
=PADE_DELAY(0.5, 3, )
Expected output:
| Result | |||
|---|---|---|---|
| -1 | 24 | -240 | 960 |
| 1 | 24 | 240 | 960 |
Example 3: Denominator order 3, Numerator order 2
Inputs:
| T | n | numdeg |
|---|---|---|
| 1 | 3 | 2 |
Excel formula:
=PADE_DELAY(1, 3, 2)
Expected output:
| Result | |||
|---|---|---|---|
| 3 | -24 | 60 | |
| 1 | 9 | 36 | 60 |
Example 4: Zero delay (returns 1/1)
Inputs:
| T | n | numdeg |
|---|---|---|
| 0 | 2 |
Excel formula:
=PADE_DELAY(0, 2, )
Expected output:
| Result |
|---|
| 1 |
| 1 |
Python Code
Show Code
import control as ct
import numpy as np
def pade_delay(T, n, numdeg=None):
"""
Calculate the Pade approximation of a continuous time delay.
See: https://python-control.readthedocs.io/en/latest/generated/control.pade.html
This example function is provided as-is without any representation of accuracy.
Args:
T (float): The time delay to approximate (in seconds).
n (int): The order (degree) of the denominator polynomial approximation.
numdeg (int, optional): Optional order of the numerator. If left blank, it equals the denominator degree. Default is None.
Returns:
list[list]: A 2D array where the first row contains the numerator coefficients and the second row contains the denominator coefficients.
"""
try:
delay = float(T)
if delay < 0:
return "Error: Time delay T must be non-negative"
order = int(n)
if order < 1:
return "Error: Order n must be a positive integer"
if numdeg is not None and str(numdeg) != "":
num_degree = int(numdeg)
num, den = ct.pade(delay, order, num_degree)
else:
num, den = ct.pade(delay, order)
# Ensure rectangular output by padding the shorter array with empty strings
num_list = list(num)
den_list = list(den)
max_len = max(len(num_list), len(den_list))
padded_num = num_list + [""] * (max_len - len(num_list))
padded_den = den_list + [""] * (max_len - len(den_list))
return [padded_num, padded_den]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
SS2TF
Converts a linear state-space representation back into a continuous-time polynomial transfer function.
Given the state-space matrices \mathbf{A}, \mathbf{B}, \mathbf{C}, and \mathbf{D}, the equivalent transfer function matrix \mathbf{H}(s) is calculated using the formula:
\mathbf{H}(s) = \mathbf{C}(s\mathbf{I} - \mathbf{A})^{-1}\mathbf{B} + \mathbf{D}
Excel Usage
=SS2TF(A, B, C, D)
A(list[list], required): System matrix (nxn)B(list[list], required): Control matrix (nxm)C(list[list], required): Output matrix (pxn)D(list[list], required): Feedforward matrix (pxm)
Returns (list[list]): A 2D array where the first row contains the numerator coefficients and the second row contains the denominator coefficients.
Example 1: Convert a 2-state SISO system
Inputs:
| A | B | C | D | ||
|---|---|---|---|---|---|
| -1 | -2 | 5 | 7 | 8 | 9 |
| 3 | -4 | 6 |
Excel formula:
=SS2TF({-1,-2;3,-4}, {5;6}, {7,8}, {9})
Expected output:
| Result | ||
|---|---|---|
| 9 | 128 | 314 |
| 1 | 5 | 10 |
Example 2: Integrator state space to TF
Inputs:
| A | B | C | D |
|---|---|---|---|
| 0 | 1 | 1 | 0 |
Excel formula:
=SS2TF({0}, {1}, {1}, {0})
Expected output:
| Result | |
|---|---|
| 1 | |
| 1 | 0 |
Example 3: State space with complex poles
Inputs:
| A | B | C | D | ||
|---|---|---|---|---|---|
| 0 | 1 | 0 | 1 | 0 | 0 |
| -2 | -2 | 1 |
Excel formula:
=SS2TF({0,1;-2,-2}, {0;1}, {1,0}, {0})
Expected output:
| Result | ||
|---|---|---|
| 0 | 1 | |
| 1 | 2 | 2 |
Example 4: Simple pass-through gain
Inputs:
| A | B | C | D |
|---|---|---|---|
| -1 | 1 | 1 | 5 |
Excel formula:
=SS2TF({-1}, {1}, {1}, {5})
Expected output:
| Result | |
|---|---|
| 5 | 6 |
| 1 | 1 |
Python Code
Show Code
import control as ct
import numpy as np
def ss2tf(A, B, C, D):
"""
Transform a state-space system into a transfer function.
See: https://python-control.readthedocs.io/en/latest/generated/control.ss2tf.html
This example function is provided as-is without any representation of accuracy.
Args:
A (list[list]): System matrix (nxn)
B (list[list]): Control matrix (nxm)
C (list[list]): Output matrix (pxn)
D (list[list]): Feedforward matrix (pxm)
Returns:
list[list]: A 2D array where the first row contains the numerator coefficients and the second row contains the denominator coefficients.
"""
try:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
A = to2d(A)
B = to2d(B)
C = to2d(C)
D = to2d(D)
# Convert input lists to numpy arrays
A_arr = np.array(A, dtype=float)
B_arr = np.array(B, dtype=float)
C_arr = np.array(C, dtype=float)
D_arr = np.array(D, dtype=float)
# Basic dimension checks
if A_arr.ndim != 2 or A_arr.shape[0] != A_arr.shape[1]:
return "Error: A must be a square 2D matrix"
n = A_arr.shape[0]
if B_arr.shape[0] != n:
return "Error: B must have the same number of rows as A"
if C_arr.shape[1] != n:
return "Error: C must have the same number of columns as A"
m = B_arr.shape[1] if B_arr.ndim == 2 else 1
p = C_arr.shape[0] if C_arr.ndim == 2 else 1
expected_d_shape = (p, m)
if D_arr.shape != expected_d_shape:
return f"Error: D must have shape {expected_d_shape}"
sys_tf = ct.ss2tf(A_arr, B_arr, C_arr, D_arr)
# Extract num and den (for SISO systems)
if sys_tf.ninputs > 1 or sys_tf.noutputs > 1:
return "Error: Function currently only formats outputs for SISO systems"
num = sys_tf.num[0][0]
den = sys_tf.den[0][0]
# Clean up small numerical artifacts
num[np.abs(num) < 1e-12] = 0.0
den[np.abs(den) < 1e-12] = 0.0
# Ensure rectangular output
num_list = list(num)
den_list = list(den)
max_len = max(len(num_list), len(den_list))
padded_num = num_list + [""] * (max_len - len(num_list))
padded_den = den_list + [""] * (max_len - len(den_list))
return [padded_num, padded_den]
except Exception as e:
return f"Error: {str(e)}"Online Calculator
STATE_SPACE
Creates a linear input/output system in state-space form. A continuous-time state-space model is defined by the following differential equations:
d\mathbf{x}/dt = \mathbf{A}\mathbf{x} + \mathbf{B}\mathbf{u} \mathbf{y} = \mathbf{C}\mathbf{x} + \mathbf{D}\mathbf{u}
This function accepts matrices \mathbf{A}, \mathbf{B}, \mathbf{C}, and \mathbf{D}. It returns a representation of the system dynamics that can be used for simulation and control design within the python-control library package.
Excel Usage
=STATE_SPACE(A, B, C, D)
A(list[list], required): System matrix (nxn) representing state dynamics.B(list[list], required): Control matrix (nxm) representing input effects on states.C(list[list], required): Output matrix (pxn) representing state effects on outputs.D(list[list], required): Feedforward/feedthrough matrix (pxm). For strictly proper systems, this is a zero matrix.
Returns (str): String representation of the StateSpace object (since we cannot return an object to excel directly).
Example 1: Simple SISO system
Inputs:
| A | B | C | D | ||
|---|---|---|---|---|---|
| -1 | -2 | 5 | 6 | 8 | 9 |
| 3 | -4 | 7 |
Excel formula:
=STATE_SPACE({-1,-2;3,-4}, {5;7}, {6,8}, {9})
Expected output:
"<StateSpace>: sys[0]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (2): ['x[0]', 'x[1]']\n\nA = [[-1. -2.]\n [ 3. -4.]]\n\nB = [[5.]\n [7.]]\n\nC = [[6. 8.]]\n\nD = [[9.]]"
Example 2: Simple Integrator (1/s)
Inputs:
| A | B | C | D |
|---|---|---|---|
| 0 | 1 | 1 | 0 |
Excel formula:
=STATE_SPACE({0}, {1}, {1}, {0})
Expected output:
"<StateSpace>: sys[1]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (1): ['x[0]']\n\nA = [[0.]]\n\nB = [[1.]]\n\nC = [[1.]]\n\nD = [[0.]]"
Example 3: 2x2 MIMO System
Inputs:
| A | B | C | D | ||||
|---|---|---|---|---|---|---|---|
| -1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
| 0 | -2 | 0 | 1 | 1 | -1 | 0 | 0 |
Excel formula:
=STATE_SPACE({-1,0;0,-2}, {1,0;0,1}, {1,1;1,-1}, {0,0;0,0})
Expected output:
"<StateSpace>: sys[2]\nInputs (2): ['u[0]', 'u[1]']\nOutputs (2): ['y[0]', 'y[1]']\nStates (2): ['x[0]', 'x[1]']\n\nA = [[-1. 0.]\n [ 0. -2.]]\n\nB = [[1. 0.]\n [0. 1.]]\n\nC = [[ 1. 1.]\n [ 1. -1.]]\n\nD = [[0. 0.]\n [0. 0.]]"
Example 4: Scalar continuous-time drift
Inputs:
| A | B | C | D |
|---|---|---|---|
| -5 | 2 | 1 | 0 |
Excel formula:
=STATE_SPACE({-5}, {2}, {1}, {0})
Expected output:
"<StateSpace>: sys[3]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (1): ['x[0]']\n\nA = [[-5.]]\n\nB = [[2.]]\n\nC = [[1.]]\n\nD = [[0.]]"
Python Code
Show Code
import control as ct
import numpy as np
def state_space(A, B, C, D):
"""
Create a state-space system model from system, control, output, and feedforward matrices.
See: https://python-control.readthedocs.io/en/latest/generated/control.ss.html
This example function is provided as-is without any representation of accuracy.
Args:
A (list[list]): System matrix (nxn) representing state dynamics.
B (list[list]): Control matrix (nxm) representing input effects on states.
C (list[list]): Output matrix (pxn) representing state effects on outputs.
D (list[list]): Feedforward/feedthrough matrix (pxm). For strictly proper systems, this is a zero matrix.
Returns:
str: String representation of the StateSpace object (since we cannot return an object to excel directly).
"""
try:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
A = to2d(A)
B = to2d(B)
C = to2d(C)
D = to2d(D)
# Convert input lists to numpy arrays
A_arr = np.array(A, dtype=float)
B_arr = np.array(B, dtype=float)
C_arr = np.array(C, dtype=float)
D_arr = np.array(D, dtype=float)
# Basic dimension checks
if A_arr.ndim != 2 or A_arr.shape[0] != A_arr.shape[1]:
return "Error: A must be a square 2D matrix"
n = A_arr.shape[0]
if B_arr.shape[0] != n:
return "Error: B must have the same number of rows as A"
if C_arr.shape[1] != n:
return "Error: C must have the same number of columns as A"
m = B_arr.shape[1] if B_arr.ndim == 2 else 1
p = C_arr.shape[0] if C_arr.ndim == 2 else 1
expected_d_shape = (p, m)
if D_arr.shape != expected_d_shape:
return f"Error: D must have shape {expected_d_shape}"
sys = ct.ss(A_arr, B_arr, C_arr, D_arr)
return str(sys)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
TF2SS
Converts a continuous-time or discrete-time polynomial transfer function into an equivalent state-space representation.
A state-space model provides a time-domain representation of the system dynamics using a set of first-order differential (or difference) equations, defined by the matrices \mathbf{A}, \mathbf{B}, \mathbf{C}, and \mathbf{D}.
Because state-space realizations are not unique (the same transfer function can be represented by infinitely many state-space models depending on the choice of state variables), this function returns a generic realization created by the slycot or scipy backend methods.
Excel Usage
=TF2SS(num, den)
num(list[list], required): Polynomial coefficients of the numerator, in descending powers of s.den(list[list], required): Polynomial coefficients of the denominator, in descending powers of s.
Returns (str): String representation of the resulting state-space model.
Example 1: 1/(s+1) -> State-Space
Inputs:
| num | den | |
|---|---|---|
| 1 | 1 | 1 |
Excel formula:
=TF2SS({1}, {1,1})
Expected output:
"<StateSpace>: sys[2]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (1): ['x[0]']\n\nA = [[-1.]]\n\nB = [[1.]]\n\nC = [[1.]]\n\nD = [[0.]]"
Example 2: (s+2)/(s^2+3s+2) -> State-Space
Inputs:
| num | den | |||
|---|---|---|---|---|
| 1 | 2 | 1 | 3 | 2 |
Excel formula:
=TF2SS({1,2}, {1,3,2})
Expected output:
"<StateSpace>: sys[6]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (2): ['x[0]', 'x[1]']\n\nA = [[-3. -2.]\n [ 1. 0.]]\n\nB = [[1.]\n [0.]]\n\nC = [[1. 2.]]\n\nD = [[0.]]"
Example 3: 1/s -> State space
Inputs:
| num | den | |
|---|---|---|
| 1 | 1 | 0 |
Excel formula:
=TF2SS({1}, {1,0})
Expected output:
"<StateSpace>: sys[10]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (1): ['x[0]']\n\nA = [[-0.]]\n\nB = [[1.]]\n\nC = [[1.]]\n\nD = [[0.]]"
Example 4: Third order system
Inputs:
| num | den | ||||
|---|---|---|---|---|---|
| 2 | 1 | 1 | 4 | 5 | 2 |
Excel formula:
=TF2SS({2,1}, {1,4,5,2})
Expected output:
"<StateSpace>: sys[14]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\nStates (3): ['x[0]', 'x[1]', 'x[2]']\n\nA = [[-4. -5. -2.]\n [ 1. 0. 0.]\n [ 0. 1. 0.]]\n\nB = [[1.]\n [0.]\n [0.]]\n\nC = [[0. 2. 1.]]\n\nD = [[0.]]"
Python Code
Show Code
import control as ct
import numpy as np
def tf2ss(num, den):
"""
Convert a transfer function object back into a state-space system object.
See: https://python-control.readthedocs.io/en/latest/generated/control.tf2ss.html
This example function is provided as-is without any representation of accuracy.
Args:
num (list[list]): Polynomial coefficients of the numerator, in descending powers of s.
den (list[list]): Polynomial coefficients of the denominator, in descending powers of s.
Returns:
str: String representation of the resulting state-space model.
"""
try:
def flatten_to_1d(x):
if not isinstance(x, list):
return [float(x)] if str(x) != "" else []
flat = []
for row in x:
if isinstance(row, list):
for val in row:
if str(val) != "":
flat.append(float(val))
elif str(row) != "":
flat.append(float(row))
return flat
num_arr = flatten_to_1d(num)
den_arr = flatten_to_1d(den)
if not num_arr or not den_arr:
return "Error: Numerator and denominator coefficients must be provided"
if all(d == 0 for d in den_arr):
return "Error: Denominator cannot be zero"
try:
sys_ss = ct.tf2ss(num_arr, den_arr, method='scipy')
except TypeError:
sys_ss = ct.tf2ss(num_arr, den_arr)
# For Excel output, we must return a rectangular array. Let's return the string representations.
return str(sys_ss)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
TRANSFER_FUNCTION
A transfer function is a mathematical representation, in terms of spatial or temporal frequency, of the relation between the input and output of a linear time-invariant system. It is defined as the Laplace transform of the output divided by the Laplace transform of the input, with zero initial conditions.
For a continuous-time polynomial representation, the transfer function is expressed as:
H(s) = \frac{num(s)}{den(s)} = \frac{b_m s^m + b_{m-1} s^{m-1} + \dots + b_1 s + b_0}{a_n s^n + a_{n-1} s^{n-1} + \dots + a_1 s + a_0}
The function accepts the coefficients of the numerator and denominator polynomials in descending order of powers of s (or z for discrete systems).
Excel Usage
=TRANSFER_FUNCTION(num, den)
num(list[list], required): Polynomial coefficients of the numerator, in descending powers of s.den(list[list], required): Polynomial coefficients of the denominator, in descending powers of s.
Returns (str): String representation of the TransferFunction object.
Example 1: First order system 1/(s+1)
Inputs:
| num | den | |
|---|---|---|
| 1 | 1 | 1 |
Excel formula:
=TRANSFER_FUNCTION({1}, {1,1})
Expected output:
"<TransferFunction>: sys[0]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 1\n -----\n s + 1"
Example 2: Second order system (s+2)/(s^2+3s+2)
Inputs:
| num | den | |||
|---|---|---|---|---|
| 1 | 2 | 1 | 3 | 2 |
Excel formula:
=TRANSFER_FUNCTION({1,2}, {1,3,2})
Expected output:
"<TransferFunction>: sys[1]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n s + 2\n -------------\n s^2 + 3 s + 2"
Example 3: Simple Integrator 1/s
Inputs:
| num | den | |
|---|---|---|
| 1 | 1 | 0 |
Excel formula:
=TRANSFER_FUNCTION({1}, {1,0})
Expected output:
"<TransferFunction>: sys[2]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 1\n -\n s"
Example 4: Third order denominator
Inputs:
| num | den | ||||
|---|---|---|---|---|---|
| 2 | 1 | 1 | 4 | 5 | 2 |
Excel formula:
=TRANSFER_FUNCTION({2,1}, {1,4,5,2})
Expected output:
"<TransferFunction>: sys[3]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 2 s + 1\n ---------------------\n s^3 + 4 s^2 + 5 s + 2"
Python Code
Show Code
import control as ct
import numpy as np
def transfer_function(num, den):
"""
Create a transfer function system from its numerator and denominator polynomial coefficients.
See: https://python-control.readthedocs.io/en/latest/generated/control.tf.html
This example function is provided as-is without any representation of accuracy.
Args:
num (list[list]): Polynomial coefficients of the numerator, in descending powers of s.
den (list[list]): Polynomial coefficients of the denominator, in descending powers of s.
Returns:
str: String representation of the TransferFunction object.
"""
try:
def flatten_to_1d(x):
# Extract first row or column
if not isinstance(x, list):
return [x]
flat = []
for row in x:
if isinstance(row, list):
for val in row:
if str(val) != "":
flat.append(float(val))
elif str(row) != "":
flat.append(float(row))
return flat
num_arr = flatten_to_1d(num)
den_arr = flatten_to_1d(den)
if not num_arr or not den_arr:
return "Error: Numerator and denominator coefficients must be provided"
if all(d == 0 for d in den_arr):
return "Error: Denominator cannot be zero"
sys = ct.tf(num_arr, den_arr)
return str(sys)
except Exception as e:
return f"Error: {str(e)}"Online Calculator
ZPK
The zero-pole-gain (ZPK) format is an alternative way to represent a transfer function. Instead of specifying the coefficients of the numerator and denominator polynomials, the ZPK format defines the locations of the roots of those equations in the complex plane.
The transfer function is defined as:
H(s) = k \frac{(s - z_1)(s - z_2)\dots(s - z_m)}{(s - p_1)(s - p_2)\dots(s - p_n)}
Where z_i are the system zeros, p_j are the system poles, and k is the system gain. Zeros are values of s where the transfer function goes to zero, while poles are values where it goes to infinity.
Excel Usage
=ZPK(zeros, poles, gain)
zeros(list[list], required): Array containing the locations of the system zeros.poles(list[list], required): Array containing the locations of the system poles.gain(float, required): The system gain (k).
Returns (str): String representation of the TransferFunction object with ZPK format.
Example 1: Single zero and pole
Inputs:
| zeros | poles | gain | |
|---|---|---|---|
| 1 | 2 | 3 | 1 |
Excel formula:
=ZPK({1}, {2,3}, 1)
Expected output:
"<TransferFunction>: sys[0]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n s - 1\n -------------\n s^2 - 5 s + 6"
Example 2: No zeros (all-pole system)
Inputs:
| zeros | poles | gain | |
|---|---|---|---|
| -1 | -2 | 5 |
Excel formula:
=ZPK({""}, {-1,-2}, 5)
Expected output:
"<TransferFunction>: sys[1]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 5\n -------------\n s^2 + 3 s + 2"
Example 3: Complex conjugate poles (approximated conceptually as we pass floats in test cases usually)
Inputs:
| zeros | poles | gain | |
|---|---|---|---|
| -5 | -1 | 1 | 2 |
Excel formula:
=ZPK({-5}, {-1,1}, 2)
Expected output:
"<TransferFunction>: sys[2]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 2 s + 10\n --------\n s^2 - 1"
Example 4: Integrator pole at origin
Inputs:
| zeros | poles | gain |
|---|---|---|
| 0 | 1 |
Excel formula:
=ZPK({""}, {0}, 1)
Expected output:
"<TransferFunction>: sys[3]\nInputs (1): ['u[0]']\nOutputs (1): ['y[0]']\n\n 1\n -\n s"
Python Code
Show Code
import control as ct
import numpy as np
def zpk(zeros, poles, gain):
"""
Create a transfer function model from zeros, poles, and gain.
See: https://python-control.readthedocs.io/en/latest/generated/control.zpk.html
This example function is provided as-is without any representation of accuracy.
Args:
zeros (list[list]): Array containing the locations of the system zeros.
poles (list[list]): Array containing the locations of the system poles.
gain (float): The system gain (k).
Returns:
str: String representation of the TransferFunction object with ZPK format.
"""
try:
def parse_numeric_value(value):
if isinstance(value, complex):
return value
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, str):
text = value.strip()
if text == "":
return None
try:
return float(text)
except ValueError:
return complex(text)
return complex(value)
def flatten_to_1d(x):
if not isinstance(x, list):
parsed = parse_numeric_value(x)
return [] if parsed is None else [parsed]
flat = []
for row in x:
if isinstance(row, list):
for val in row:
parsed = parse_numeric_value(val)
if parsed is not None:
flat.append(parsed)
else:
parsed = parse_numeric_value(row)
if parsed is not None:
flat.append(parsed)
return flat
z_arr = flatten_to_1d(zeros)
p_arr = flatten_to_1d(poles)
k = float(gain)
sys = ct.zpk(z_arr, p_arr, k)
return str(sys)
except Exception as e:
return f"Error: {str(e)}"Online Calculator