MARGIN

Stability margins measure how much uncertainty a feedback system can tolerate before it becomes unstable.

The two standard quantities are gain margin and phase margin:

G_m = \text{allowable gain factor at the } -180^\circ \text{ phase crossing}

P_m = 180^\circ + \angle G(j\omega_{cp})

where \omega_{cp} is the gain crossover frequency at which |G(j\omega)| = 1. This wrapper returns a single spreadsheet row containing gain margin, phase margin, phase crossover frequency, and gain crossover frequency.

Excel Usage

=MARGIN(sysdata)
  • sysdata (list[list], required): The open-loop transfer function, provided as numerator and denominator coefficients in consecutive rows.

Returns (list[list]): A 1x4 horizontal array containing [Gain Margin, Phase Margin, Wcg, Wcp]. If a margin is infinite, it returns ‘inf’.

Example 1: Check margins of a stable system

Inputs:

sysdata
1
1

Excel formula:

=MARGIN({1;1,2,1,0})

Expected output:

Result
2 21.3864 1 0.682328
Example 2: System with infinite gain margin

Inputs:

sysdata
10
1

Excel formula:

=MARGIN({10;1,5,6})

Expected output:

Result
inf 102.663 1.95135

Python Code

Show Code
import control as ct
import numpy as np

def margin(sysdata):
    """
    Calculate the gain and phase margins and crossover frequencies of a system.

    See: https://python-control.readthedocs.io/en/latest/generated/control.margin.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        sysdata (list[list]): The open-loop transfer function, provided as numerator and denominator coefficients in consecutive rows.

    Returns:
        list[list]: A 1x4 horizontal array containing [Gain Margin, Phase Margin, Wcg, Wcp]. If a margin is infinite, it returns 'inf'.
    """
    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

        if not sysdata or len(sysdata) < 2:
            return "Error: sysdata must contain numerator and denominator rows"

        num = flatten_to_1d(sysdata[0])
        den = flatten_to_1d(sysdata[1])

        if not num or not den:
            return "Error: Invalid numerator or denominator arrays"

        sys = ct.tf(num, den)

        gm, pm, wcg, wcp = ct.margin(sys)

        # Handle infs and NaNs for excel
        def clean_val(v):
            if v is None or np.isnan(v):
                return ""
            if np.isinf(v):
                return "inf" if v > 0 else "-inf"
            return float(v)

        # Return as a 1x4 2D array
        return [[clean_val(gm), clean_val(pm), clean_val(wcg), clean_val(wcp)]]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

The open-loop transfer function, provided as numerator and denominator coefficients in consecutive rows.