pychopmarg.com

Classes

class pychopmarg.com.COM(debug: bool = False)[source]

Encoding of the IEEE 802.3-22 Annex 93A/178A ‘Channel Operating Margin’ (COM) specification.

Todo

  1. Clean up unused attributes.

status_str = 'Ready'
FB = 25781250000.0
fb = 25781250000.0
nspui = 32
tmax = 1e-08
fstep = 10000000.0
fmax = 40000000000.0
Av = 0.4
Afe = 0.4
Ane = 0.6
L = 2
RLM = 1.0
tr = 1e-11
nTxTaps = 6
tx_n_post = 3
tx_taps_pos = array([-3, -2, -1,  1,  2,  3])
tx_taps_max = array([1., 1., 1., 1., 1., 1.])
tx_taps_step = array([0.25, 0.25, 0.25, 0.25, 0.25, 0.25])
tx_taps = array([0., 0., 0., 0., 0., 0.])
fr = 0.75
gDC_vals = [0.0, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -10.0, -11.0, -12.0]
gDC = 0.0
gDC2_vals = [0.0]
gDC2 = 0.0
fz = 6445312500.0
fp1 = 6445312500.0
fp2 = 25781250000.0
fLF = 1000000.0
opt_mode = 2
norm_mode = 1
unit_amp = True
N_DFE = 1
nDFE = 1
bmax = array([1.])
bmin = array([-1.])
dfe_taps = array([0.])
nRxTaps = 16
nRxPreTaps = 5
rx_taps_min = array([-1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1.,        -1., -1., -1.])
rx_taps_max = array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
rx_taps = array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
R0 = 50.0
Rd = 55.0
Cd = [4e-14, 9e-14, 1.1e-13]
Cb = 0.0
Cp = 1.8e-13
Ls = [1.3e-10, 1.5e-10, 1.4e-10]
zp_vals = [12.0, 33.0]
zp = 12.0
zp_B = 1.8
zc = [87.5, 92.5]
gamma0 = 0.0005
a1 = 0.00089
a2 = 0.0002
tau = 0.006141
sigma_Rj = 0.01
Add = 0.05
eta0 = 5.2e-08
TxSNR = 27.0
DER0 = 1e-05
chnl_s32p = PosixPath('.')
chnl_s4p_thru = PosixPath('.')
chnl_s4p_fext1 = PosixPath('.')
chnl_s4p_fext2 = PosixPath('.')
chnl_s4p_fext3 = PosixPath('.')
chnl_s4p_fext4 = PosixPath('.')
chnl_s4p_fext5 = PosixPath('.')
chnl_s4p_fext6 = PosixPath('.')
chnl_s4p_next1 = PosixPath('.')
chnl_s4p_next2 = PosixPath('.')
chnl_s4p_next3 = PosixPath('.')
chnl_s4p_next4 = PosixPath('.')
chnl_s4p_next5 = PosixPath('.')
chnl_s4p_next6 = PosixPath('.')
vic_chnl_ix = 1
chnls: list[tuple[Network, str]] = []
pulse_resps_nopkg: list[ndarray[Any, dtype[Real]]] = []
pulse_resps_noeq: list[ndarray[Any, dtype[Real]]] = []
cursor_ix: int = 0
com = 0.0
com_As = 0.0
com_cursor_ix = 0
com_sigma_Tx = 0.0
com_sigma_G = 0.0
com_sigma_N = 0.0
fom = 0.0
fom_As = 0.0
Ani = 0.0
fom_cursor_ix = 0
sigma_ISI = 0.0
sigma_J = 0.0
sigma_XT = 0.0
sigma_Tx = 0.0
sigma_N = 0.0
about_str = '\n      <H2><em>PyChOpMarg</em> - A Python implementation of COM, as per IEEE 802.3-22 Annex 93A/178A.</H2>\n\n      <strong>By:</strong> David Banas <capn.freako@gmail.com><p>\n\n      <strong>On:</strong> November 1, 2024<p>\n\n      <strong>At:</strong> v2.0.1\n\n      <H3>Useful Links</H3>\n\n      (You\'ll probably need to: right click, select <em>Copy link address</em>, and paste into your browser.)\n        <UL>\n\n          <LI><a href="https://github.com/capn-freako/PyChOpMarg"><em>GitHub</em> Home</a>\n          <LI><a href="https://pypi.org/project/PyChOpMarg/"><em>PyPi</em> Home</a>\n          <LI><a href="https://readthedocs.org/projects/pychopmarg/"><em>Read the Docs</em> Home</a>\n        </UL>\n    '
property ui: float

Unit interval (s).

property times: ndarray[Any, dtype[Real]]

System time vector (s); decoupled from system frequency vector!

property freqs: ndarray[Any, dtype[Real]]

System frequency vector (Hz); decoupled from system time vector!

property t_irfft: ndarray[Any, dtype[Real]]

irfft() result time index (s) (i.e. - time vector coupled to frequency vector).

property Xsinc: ndarray[Any, dtype[Real]]

Frequency domain sinc(f) corresponding to Rect(ui) in time domain.

property Ht: ndarray[Any, dtype[Comp]]

Return the voltage transfer function, H(f), associated w/ the Tx risetime, according to (93A-46).

property Hr: ndarray[Any, dtype[Comp]]

Return the voltage transfer function, H(f), of the Rx AFE, according to (93A-20).

property Hctf: ndarray[Any, dtype[Comp]]

Return the voltage transfer function, H(f), of the Rx CTLE, according to (93A-22).

calc_Hctf(gDC: float | None = None, gDC2: float | None = None) ndarray[Any, dtype[Comp]][source]

Return the voltage transfer function, H(f), of the Rx CTLE, according to (93A-22).

Keyword Arguments:
  • gDC – CTLE first stage d.c. gain (dB). Default: None

  • gDC2 – CTLE second stage d.c. gain (dB). Default: None

Notes

1. The instance’s current value(s) for gDC and gDC2 are used if not provided. (Necessary, to accommodate sweeping when optimizing EQ.)

property tx_combs: list[list[float]]

All possible Tx tap weight combinations.

property gamma1: float

Reflection coefficient looking out of the left end of the channel.

property gamma2: float

Reflection coefficient looking out of the right end of the channel.

property sDieLadder: Network

On-die parasitic capacitance/inductance ladder network.

property sPkgRx: Network

Rx package response.

property sPkgTx: Network

Tx package response.

property sPkgNEXT: Network

NEXT package response.

property sZp: Network

THRU/FEXT package transmission line.

property sZpNEXT: Network

NEXT package transmission line.

calc_sZp(NEXT: bool = False) Network[source]

Return the 2-port network corresponding to a package transmission line, according to (93A-9:14).

Keyword Arguments:

NEXT – Use first package T-line length option when True. Default: False

Returns:

2-port network equivalent to package transmission line.

get_chnls() list[tuple[Network, str]][source]

Import all channels from Touchstone file(s).

get_chnls_s32p_wPkg() list[tuple[Network, str]][source]

Augment imported s32p channels, w/ package response.

get_chnls_s4p_wPkg() list[tuple[Network, str]][source]

Augment imported s4p channels, w/ package response.

get_chnls_s32p_noPkg() list[tuple[Network, str]][source]

Import s32p file, w/o package.

get_chnls_s4p_noPkg() list[tuple[Network, str]][source]

Import s4p files, w/o package.

add_pkgs(ntwks: list[tuple[Network, str]]) list[tuple[Network, str]][source]

Add package response to raw channels and generate pulse responses.

add_pkg(ntwk: tuple[Network, str]) tuple[Network, str][source]

Add package response to raw channel.

set_status(status: str) None[source]

Set the GUI status string and print it if we’re debugging.

debug = False
c0_min = 0.0
tx_taps_min = array([-1., -1., -1., -1., -1., -1.])
classmethod init(params: dict, chnl_fnames: list[str], vic_id: int, zp_sel: int = 1)[source]

Legacy initializer supports my VITA notebook, which was created before PyChOpMarg was altered to support a GUI.

set_params(params: dict[str, Any]) None[source]

Set the COM instance parameters, according to the given dictionary.

Parameters:

params – Dictionary of COM parameter values.

Raises:
  • KeyError – If an expected key is not found in the provided dictionary.

  • ValueError – If certain invariants aren’t met.

sC(c: float) Network[source]

Return the 2-port network corresponding to a shunt capacitance, according to (93A-8).

Parameters:

c – Value of shunt capacitance (F).

Returns:

2-port network equivalent to shunt capacitance, calculated at given frequencies.

Raises:

None

H21(s2p: Network) ndarray[Any, dtype[Comp]][source]

Return the voltage transfer function, H21(f), of a terminated two port network, according to (93A-18).

Parameters:

s2p – Two port network of interest.

Returns:

Complex voltage transfer function at given frequencies.

Raises:

ValueError – If given network is not two port.

Notes

1. It is at this point in the analysis that the “raw” Touchstone data gets interpolated to our system frequency vector.

  1. After this step, the package and R0/Rd mismatch have been accounted for, but not the EQ.

H(s2p: Network, tx_taps: ndarray[Any, dtype[Real]] | None = None, gDC: float | None = None, gDC2: float | None = None, rx_taps: ndarray[Any, dtype[Real]] | None = None, dfe_taps: ndarray[Any, dtype[Real]] | None = None, passive_RxFFE: bool = False) ndarray[Any, dtype[Comp]][source]

Return the voltage transfer function, H(f), of a complete COM signal path, according to (93A-19).

Parameters:

s2p – Two port network of interest.

Keyword Arguments:
  • tx_taps – Tx FFE tap weights. Default: None (i.e. - Use self.tx_taps.)

  • gDC – CTLE first stage d.c. gain (dB). Default: None (i.e. - Use self.gDC.)

  • gDC2 – CTLE second stage d.c. gain (dB). Default: None (i.e. - Use self.gDC2.)

  • rx_taps – Rx FFE tap weights. Default: None (i.e. - Use self.rx_taps.)

  • dfe_taps – Rx DFE tap weights. Default: None (i.e. - Use self.dfe_taps.)

  • passive_RxFFE – Enforce passivity of Rx FFE when True. Default: True

Returns:

Complex voltage transfer function of complete path.

Raises:

ValueError – If given network is not two port.

Notes

  1. It is in this processing step that linear EQ is first applied.

2. Any unprovided EQ values are taken from the COM instance. If you really want to omit a particular EQ component then call with:

  • tx_taps: []

  • rx_taps: [1.0]

  • gDC/gDC2: 0

pulse_resp(H: ndarray[Any, dtype[Comp]]) ndarray[Any, dtype[Real]][source]

Return the unit pulse response, p(t), corresponding to the given voltage transfer function, H(f), according to (93A-24).

Parameters:

H – The voltage transfer function, H(f). Note: Possitive frequency components only, including fN.

Returns:

The pulse response corresponding to the given voltage transfer function.

Raises:

ValueError – If the length of the given voltage transfer function differs from that of the system frequency vector.

Notes

1. It is at this point in the signal processing chain that we change time domains.

gen_pulse_resps(ntwks: list[tuple[Network, str]] | None = None, gDC: float | None = None, gDC2: float | None = None, tx_taps: ndarray[Any, dtype[Real]] | None = None, rx_taps: ndarray[Any, dtype[Real]] | None = None, dfe_taps: ndarray[Any, dtype[Real]] | None = None, apply_eq: bool = True) list[ndarray[Any, dtype[Real]]][source]

Generate pulse responses for all networks.

Keyword Arguments:
  • ntwks – The list of networks to generate pulse responses for. Default: None (i.e. - Use self.chnls.)

  • gDC – Rx CTLE first stage d.c. gain. Default: None (i.e. - Use self.gDC.)

  • gDC2 – Rx CTLE second stage d.c. gain. Default: None (i.e. - Use self.gDC2.)

  • tx_taps – Desired Tx tap weights. Default: None (i.e. - Use self.tx_taps.)

  • rx_taps – Desired Rx FFE tap weights. Default: None (i.e. - Use self.rx_taps.)

  • dfe_taps – Desired Rx DFE tap weights. Default: None (i.e. - Use self.dfe_taps.)

  • apply_eq – Include linear EQ when True; otherwise, exclude it. (Allows for pulse response generation of terminated, but unequalized, channel.) Default: True

Returns:

list of pulse responses.

Notes

1. Assumes self.gDC, self.gDC2, self.tx_taps, self.rx_taps, and self.dfe_taps have been set correctly, if the equivalent function parameters have not been provided.

2. To generate pulse responses that include all linear EQ except the Rx FFE/DFE (i.e. - pulse responses suitable for Rx FFE/DFE tap weight optimization, via either optimize.przf() or optimize.mmse()), set rx_taps equal to: [1.0] and dfe_taps equal to: [].

filt_pr_samps(pr_samps: ndarray[Any, dtype[Real]], As: float, rel_thresh: float = 0.001) ndarray[Any, dtype[Real]][source]

Filter a list of pulse response samples for minimum magnitude.

Parameters:
  • pr_samps – The pulse response samples to filter.

  • As – Signal amplitude, as per 93A.1.6.c.

Keyword Arguments:

rel_thresh – Filtration threshold (As). Default: 0.001 (i.e. - 0.1%, as per Note 2 of 93A.1.7.1)

Returns:

The subset of pr_samps passing filtration.

calc_hJ(pulse_resp: ndarray[Any, dtype[Real]], As: float, cursor_ix: int, rel_thresh: float = 0.001) ndarray[Any, dtype[Real]][source]

Calculate the set of slopes for valid pulse response samples.

Parameters:
  • pulse_resp – The pulse response of interest.

  • As – Signal amplitude, as per 93A.1.6.c.

  • cursor_ix – Cursor index.

Keyword Arguments:

rel_thresh – Filtration threshold (As). Default: 0.001 (i.e. - 0.1%, as per Note 2 of 93A.1.7.1)

Returns:

The calculated slopes around the valid samples.

loc_curs(pulse_resp: ndarray[Any, dtype[Real]], max_range: int = 1, eps: float = 0.001) int[source]

Locate the cursor position for the given pulse response, according to (93A-25) and (93A-26) (i.e. - Muller-Mueller criterion).

Parameters:

pulse_resp – The pulse response of interest.

Keyword Arguments:
  • max_range – The search radius, from the peak (UI). Default: 1

  • eps – Threshold for declaring floating point value to be zero. Default: 0.001

Returns:

The index in the given pulse response vector of the cursor.

Notes

1. As per v3.70 of the COM MATLAB code, we only minimize the residual of (93A-25); we don’t require solving it exactly. (We do, however, give priority to exact solutions.)

calc_fom(tx_taps: ndarray[Any, dtype[Real]], gDC: float | None = None, gDC2: float | None = None, rx_taps: ndarray[Any, dtype[Real]] | None = None, opt_mode: OptMode | None = None, norm_mode: NormMode | None = None, unit_amp: bool | None = None) float[source]

Calculate the figure of merit (FOM), given the existing linear EQ settings. Optimize Rx FFE taps if they aren’t specified by caller.

Parameters:

tx_taps – The Tx FFE tap weights, excepting the cursor. (The cursor takes whatever is left.)

Keyword Arguments:
  • gDC – CTLE first stage d.c. gain. Default: None (i.e. - Use self.gDC.)

  • gDC2 – CTLE second stage d.c. gain. Default: None (i.e. - Use self.gDC2.)

  • rx_taps – Rx FFE tap weight overrides. Default: None (i.e. - Optimize Rx FFE tap weights.)

  • opt_mode – Optimization mode. Default: None (i.e. - Use self.opt_mode.)

  • norm_mode – The tap weight normalization mode to use. Default: None (i.e. - Use self.norm_mode.)

  • unit_amp – Enforce unit pulse response amplitude when True. (For comparing optimize.przf() results to optimize.mmse() results.) Default: None (i.e. - Use self.unit_amp.)

Returns:

The resultant figure of merit.

Notes

  1. See: IEEE 802.3-2022 93A.1.6.

  2. When not provided, the values for gDC and gDC2 are taken from the COM instance.

  3. Unlike other member functions of the COM class, this function optimizes the Rx FFE tap weights when they are not provided.

opt_eq(do_opt_eq: bool = True, tx_taps: ndarray[Any, dtype[Real]] | None = None, opt_mode: OptMode | None = None, norm_mode: NormMode | None = None, unit_amp: bool | None = None) bool[source]

Find and set the optimum values for the linear equalization parameters: c[n], gDC, gDC2, and w[n] as per IEEE 802.3-22 93A.1.6 (or, [1] slide 11 if MMSE has been chosen).

Keyword Arguments:
  • do_opt_eq – Perform optimization of linear EQ when True. Default: True

  • tx_taps – Used when do_opt_eq = False. Default: None

  • opt_mode – Optimization mode. Default: None (i.e. - Use self.opt_mode.)

  • norm_mode – The tap weight normalization mode to use. Default: None (i.e. - Use self.norm_mode.)

  • unit_amp – Enforce unit pulse response amplitude when True. (For comparing przf() results to mmse() results.) Default: None (i.e. - Use self.unit_amp.)

Returns:

True if no errors encountered; False otherwise.

Notes

  1. The found optimum equalization values are set for the instance.

calc_noise(cursor_ix: int | None = None, opt_mode: OptMode | None = None, norm_mode: NormMode | None = None, unit_amp: bool | None = None, dbg_dict: Dict[str, Any] | None = None) tuple[float, float, int][source]

Calculate the interference and noise for COM.

Keyword Arguments:
  • cursor_ix – An optional predetermined cursor index, to be used instead of our own estimate. (In support of MMSE.) Default: None

  • opt_mode – Optimization mode. Default: None (i.e. - Use self.opt_mode.)

  • norm_mode – The tap weight normalization mode to use. Default: None (i.e. - Use self.norm_mode.)

  • unit_amp – Enforce unit pulse response amplitude when True. (For comparing przf() results to mmse() results.) Default: None (i.e. - Use self.unit_amp.)

  • dbg_dict – Optional dictionary into which debugging values may be stashed, for later analysis. Default: None

Returns:

A triplet containing

  • signal amplitude (V)

  • noise + interference amplitude (V)

  • cursor location within victim pulse response vector

Notes

  1. Assumes the following instance variables have been set optimally:

    • gDC

    • gDC2

    • tx_taps

    • rx_taps

    (This assumption is embedded into the gen_pulse_resps() function.)

  2. Fills in the com_results dictionary w/ various useful values for debugging.

Todo

  1. DER0 / 2 in Ani calculation?