Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional Mixed Mode Tutorial #396

Merged
merged 13 commits into from
Oct 1, 2020
Merged
290 changes: 290 additions & 0 deletions doc/source/examples/mixedmodeanalysis/Mixed Mode Basics.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"# Mixed Mode S-Parameter Conversion\n",
"\n",
"When analyzing differential devices, mixed-mode S-Parameters are typically used to look at differential and common mode\n",
"characteristics. Scikit-rf provides functions to ease conversion between single ended and mixed mode S-parameters.\n",
"Nevertheless, the user must be careful to set up ports correctly and take note of the form of the mixed-mode matrix to\n",
"prevent confusion. This notebook will introduce you to the process of converting from single ended to mixed mode S\n",
"parameters using a [50 ohm calibration load](https://www.formfactor.com/download/iss-map-129-246/) as an example.\n",
"The experimental port setup consists of two differential probes as shown:\n",
"\n",
"![](mixedmodebasics_files/setup.PNG)\n",
"\n",
"Experimental data for the 50 ohm load taken in normal \"single ended\" mode will be compared against data saved in\n",
"a mixed mode format and taken using Keysight's integrated True Mode Stimulus, which applies true differential\n",
"and common mode signals during the measurement process."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"import re\n",
"import skrf as rf\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"sedatafile = r'mixedmodebasics_files/load_se.s4p'\n",
"mmdatafile = r'mixedmodebasics_files/load_truemode_balbal.s4p'\n",
"\n",
"for file in [sedatafile, mmdatafile]:\n",
" with open(file, encoding='cp1252') as f:\n",
" for line in f:\n",
" print(line.rstrip())\n",
" if re.search('#', line):\n",
" break\n",
" else:\n",
" pass\n",
" print('\\n')"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"The header data for the mixed mode data indicates that it is saved in the following format:\n",
"\n",
"$$\n",
"\\begin{bmatrix}\n",
"\\begin{bmatrix}\n",
" S_{dd} & S_{dc} \\\\\n",
" S_{cd} & S_{cc}\n",
"\\end{bmatrix}_{11} &\n",
"\\begin{bmatrix}\n",
" S_{dd} & S_{dc} \\\\\n",
" S_{cd} & S_{cc}\n",
"\\end{bmatrix}_{12} \\\\\n",
"\\begin{bmatrix}\n",
" S_{dd} & S_{dc} \\\\\n",
" S_{cd} & S_{cc}\n",
"\\end{bmatrix}_{21} &\n",
"\\begin{bmatrix}\n",
" S_{dd} & S_{dc} \\\\\n",
" S_{cd} & S_{cc}\n",
"\\end{bmatrix}_{22}\n",
"\\end{bmatrix}\n",
"$$\n",
"\n",
"It is important to keep this in mind, as this format may vary between different software and hardware.\n",
"For instance, skrf will transform singled ended data to the following form when two balanced ports are present:\n",
"\n",
"$$\n",
"\\begin{bmatrix}\n",
"\\begin{bmatrix}\n",
" S_{11} & S_{12} \\\\\n",
" S_{21} & S_{22}\n",
"\\end{bmatrix}_{dd} &\n",
"\\begin{bmatrix}\n",
" S_{11} & S_{12} \\\\\n",
" S_{21} & S_{22}\n",
"\\end{bmatrix}_{dc} \\\\\n",
"\\begin{bmatrix}\n",
" S_{11} & S_{12} \\\\\n",
" S_{21} & S_{22}\n",
"\\end{bmatrix}_{cd} &\n",
"\\begin{bmatrix}\n",
" S_{11} & S_{12} \\\\\n",
" S_{21} & S_{22}\n",
"\\end{bmatrix}_{cc}\n",
"\\end{bmatrix}\n",
"$$\n",
"\n",
"To transform our single ended data, we must first pair the ports as they existed during the experimental setup with\n",
"ports 1 and 3 making up one balanced port, and 2 and 4 on the other probe. We can then use the `se2gmm()` method of the\n",
"skrf.Network class to transform to a mixed mode s-parameter matrix, with the `p` parameter used to specify the number of\n",
"mixed mode ports. Skrf will transform the ports in pairs starting at the lowest number ports (1 and 3 after our\n",
"renumbering) and continue until the matrix contains `p` mixed mode ports, leaving the remaining ports as single ended."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"sedata = rf.Network(sedatafile)\n",
"sedata.renumber([0, 1, 2, 3], [0, 2, 1, 3]) # pair ports as 1,3 and 2,4 to match experimental setup\n",
"sedata.se2gmm(p=2) # two balanced ports\n",
"# sedata now in form Sdd Sdc with each submatrix as S11 S12\n",
"# Scd Scc S21 S22"
]
},
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"The following function converts a two-port balanced-balanced network in the skrf format into the format used by\n",
"Keysight to make data comparisons easier:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"def gmm_reorder(m):\n",
" \"\"\"\n",
" Reorders data from form 11 12 with each submatrix as dd dc\n",
" 21 22 cd cc\n",
" to form dd dc with each submatrix as 11 12\n",
" cd cc 21 22\n",
" \"\"\"\n",
" b = np.array([1, 0, 0, 0,\n",
" 0, 0, 1, 0,\n",
" 0, 1, 0, 0,\n",
" 0, 0, 0, 1]).reshape(4, 4)\n",
" m = b.dot(m.dot(b))\n",
" return m\n",
"\n",
"mmdata = rf.Network(mmdatafile)\n",
"# raw data is mmdata in form S11 S12 with each submatrix as dd dc\n",
"# S21 S22 cd cc\n",
"for i, freq in enumerate(mmdata.f):\n",
" mmdata.s[i, :, :] = gmm_reorder(mmdata.s[i, :, :])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that the two networks are in the same format, we can see that skrf does not consider them equal.\n",
"This is because the networks are not identical, they are merely two measurements of the same device, both with\n",
"experimental noise and variation. \n",
"If the tolerances of the comparison are relaxed, the comparison of the networks returns True:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"print(sedata == mmdata) # uses np.allclose with tight tolerances\n",
"print(np.allclose(abs(sedata.s), abs(mmdata.s), rtol=1, atol=1e-3)) # relaxed tolerances"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plotted, the error looks like this:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"for m in range(4):\n",
" for n in range(4):\n",
" plt.plot(sedata.f, abs(mmdata.s)[:,m,n]-abs(sedata.s)[:,m,n], label=f'S{m+1}{n+1}')\n",
" plt.title('Magnitude Error between Measurements')\n",
" plt.legend(bbox_to_anchor=(1.1, 1.05))\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig, axes = plt.subplots(2,2, sharex=True, sharey='row', figsize=(14,6))\n",
"sedata.plot_s_db(ax=axes[0][0])\n",
"mmdata.plot_s_db(ax=axes[0][1])\n",
"sedata.plot_s_deg(ax=axes[1][0])\n",
"mmdata.plot_s_deg(ax=axes[1][1])\n",
"axes[0][0].get_legend().remove()\n",
"axes[1][0].get_legend().remove()\n",
"axes[1][1].get_legend().remove()\n",
"axes[0][1].get_legend().remove()\n",
"\n",
"fig.legend(*axes[0, 1].get_legend_handles_labels(), loc=\"center right\")\n",
"fig.tight_layout()\n",
"plt.subplots_adjust(top=0.9, right=0.8)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": [
"fig, axes = plt.subplots(4,4, sharex=True, figsize=(14,8))\n",
"for m in range(4):\n",
" for n in range(4):\n",
" sedata.plot_s_deg_unwrap(m=m, n=n, ax=axes[m][n])\n",
" mmdata.plot_s_deg_unwrap(m=m, n=n, ax=axes[m][n], ls='--')\n",
" axes[m][n].get_legend().remove()\n",
"fig.tight_layout()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"\n",
"Lastly, since it is desired to use this network in a cascade analysis as a 2-port block in a 50 Ω environment, the differential mode will be terminated in 100 Ω and a 50 Ω port transformed to 25 Ω will be connected to the common mode port:\n",
"\n",
"![](mixed_mode.png)"
"![](mixedmodeSandZtransform_files/mixed_mode.png)"
]
},
{
Expand All @@ -23,7 +23,7 @@
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"filename = r'EP2C+_Plus25DegC_Unit1.S3P'\n",
"filename = r'mixedmodeSandZtransform_files/EP2C+_Plus25DegC_Unit1.S3P'\n",
"se_ntwk = skrf.Network(filename)\n",
"se_ntwk.frequency.unit = 'GHz'\n",
"\n",
Expand Down Expand Up @@ -145,4 +145,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}