{ "cells": [ { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ".. _media::\n", "\n", "|\n", "|\n", "\n", "Download This Notebook: :download:`Media.ipynb`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Media\n", "\n", "## Introduction\n", "\n", "\n", "**Scikit-rf** supports S-Parameters simulations of transmission line models such as hollow waveguide, coaxial, microstripline and freespace among others.\n", "\n", "The [Media](../api/media/index.rst) object stores the properties of a media, such as the *propagation constant* and the *characteristic impedance*. The network objects - like a line of a given length - are generated from the media.\n", "\n", "This tutorial illustrates how to create networks using several kinds of media. It also explains the most common pitfalls regarding the network *port impedance* and *characteristic impedance*.\n", "\n", "The singular of the Latin word *media* is *medium*, which means middle or center. For clarity's sake, *media* is used for both singular and plural in scikit-rf." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## RG58C/U coaxial cable\n", "\n", "Two arguments are common to all media constructors:\n", "\n", "* `frequency` (required)\n", "* `z0_port` (optional)\n", "\n", "The frequency axes `frequency` is a `Frequency` object defining the media band.\n", "\n", "The *port impedance* `z0_port` is optional and is used to renormalize from the media *characteristic impedance* `z0` to the *port impedance* of a simulated measurement.\n", "\n", "If no *port impedance* `z0_port` is specified, the lines have the raw *characteristic impedance* `z0` of the media.\n", "\n", "An example with an RG58C/U flexible coaxial cable media follows." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simulating a VNA measurement\n", "![coaxial measurement](figures/media_coax_measurement.svg)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# various initialization\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "import skrf as rf\n", "\n", "# import the desired media and the frequency axis\n", "from skrf import Frequency\n", "from skrf.media import Coaxial\n", "\n", "rf.stylely()\n", "\n", "\n", "# frequency\n", "f_rg58 = Frequency(1, 5, 101, 'GHz')\n", "\n", "# media with z0_port the port impedance of the VNA\n", "rg58 = Coaxial(f_rg58, Dint = 0.91e-3, Dout = 2.95e-3, epsilon_r = 2.3, z0_port = 50)\n", "print(rg58)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Media has a *characteristic impedance* `z0` of approximately 47 Ohm. The *port impedance* `z0_port` is 50 Ohm. The propagation constant `gamma` is also computed from the Media parameters. These properties defines the transmission line model." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f'z0 = {rg58.z0[0]}')\n", "print(f'z0_port = {rg58.z0_port[0]}')\n", "print(f'gamma = {rg58.gamma[0]}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a line network corresponding to a 100 millimeter length of coax." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rg58_line = rg58.line(100, unit = 'mm', name = '100 mm, z0 Ohm')\n", "print(rg58_line)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The *characteristic impedance* `z0 `of the media has been renormalized to *port impedance* `z0_port`. The network has a port impedance `z0` equal to `z0_port`.\n", "\n", "In some cases, lines of arbitrary impedance are required without creating multiples media. The impedance of the line can be overridden at construction. The resulting network is renormalized to `z0_port`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rg58_25ohm_line = rg58.line(100, unit = 'mm', z0 = 25, name = '100 mm, 25 Ohm')\n", "print(rg58_25ohm_line)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot the two lines S-Parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(1, 2, figsize = (8, 3.5))\n", "\n", "# return loss\n", "rg58_line.plot_s_db(0, 0, ax = axes[0])\n", "rg58_25ohm_line.plot_s_db(0, 0, ax = axes[0])\n", "axes[0].set_title('Return Loss')\n", "rg58_line.plot_s_db(1, 0, ax = axes[1])\n", "rg58_25ohm_line.plot_s_db(1, 0, ax = axes[1])\n", "axes[1].set_title('Insertion Loss')\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The return loss and insertion loss show the effect of the mismatch between the *port impedance* `z0_port` and the *characteristic impedance* `z0` of the line, that is either defined by the geometry or forced to an arbitrary value. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic examples\n", "\n", "Let's create two networks, a length of 50-Ohm planar microstripline and a length of WR-10 rectangular waveguide.\n", "\n", "| Microstripline | Rectangular Waveguide |\n", "| :-: | :-: |\n", "| ![microstripline](figures/media_mline.svg) | ![rectangular waveguide](figures/media_rectangularwaveguide.svg) |\n", "\n", "First of all, create two [Media](../api/media/index.rst) objects with the model parameters.\n", "\n", "### Media objects\n", "\n", "| Microstripline | | | WR-10 Rectangular Waveguide | | |\n", "| :- | :- | :- | :- | :- | :- |\n", "| Track width | `w` | 3 mm | Aperture width | `a` | 100 mil |\n", "| Track thickness | `t` | 35 um| Aperture height | `b` | 50 mil |\n", "| Substrate height | `h` | 1.6 mm | - | - | - |\n", "| Relative permittivity (FR-4) | `ep_r` | 4.5 | Relative permittivity (air) | `ep_r`| 1.0 |\n", "| Resistivity (copper) | `rho` | 1.68e-08 Ohm * m | Resistivity (copper) | `rho` | 1.68e-08 Ohm * m |" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# various initialization\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "import skrf as rf\n", "\n", "# import the desired media and the frequency axis\n", "from skrf import Frequency\n", "from skrf.constants import to_meters\n", "from skrf.media import MLine, RectangularWaveguide\n", "\n", "rf.stylely()\n", "\n", "# create frequency axes\n", "f_mlin = Frequency(0.1, 10,1001, 'GHz')\n", "f_wr10 = Frequency(75, 110, 1001,'GHz')\n", "\n", "# create media from parameters\n", "mlin = MLine(f_mlin, w = 3e-3, h = 1.6e-3, t = 35e-6, ep_r = 4.5, rho = 1.68e-08)\n", "print(mlin)\n", "wr10 = RectangularWaveguide(f_wr10, a = to_meters(100, 'mil'), b = to_meters(50, 'mil'), ep_r = 1.0, rho = 1.68e-08)\n", "print(wr10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Line creation\n", "\n", "Secondly, use the [Media](../api/media/index.rst) objects to generate networks corresponding to a 100 millimeter length of both media." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create the transmission line networks\n", "mlin_100 = mlin.line(100e-3, unit = 'm', name = 'mlin 100mm')\n", "print(mlin_100)\n", "wr10_100 = wr10.line(100e-3, unit = 'm', name = 'wr10 100mm')\n", "print(wr10_100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plotting the results\n", "\n", "The S-Parameters of the lines are plotted in the figure below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# prepare figure\n", "fig1, axes = plt.subplots(2, 2, figsize = (8, 6))\n", "\n", "# plot miscrostipline\n", "mlin_100.plot_s_mag(0, 0, ax = axes[0,0])\n", "mlin_100.plot_s_db(1, 0, ax = axes[0,1])\n", "\n", "# plot rectangular waveguide\n", "wr10_100.plot_s_mag(0, 0, ax = axes[1,0])\n", "wr10_100.plot_s_db(1, 0, ax = axes[1,1])\n", "\n", "# resize plot nicely\n", "axes[0, 0].set_ylim((-1, 1))\n", "axes[1, 0].set_ylim((-1, 1))\n", "fig1.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The insertion loss S21 of the transmission line is frequency dependent, but the S11 magnitude is constant and zero. The absolute magnitude of S11 was plotted instead of dB because log(0) is undefined. \n", "\n", "*Why does return loss S11 not show the shape observed on vector network analyzer measurements?*\n", "\n", "This is because no *port impedance* `z0_port` was specified at media construction. In this case, the *characteristic impedance* `z0` is used as *port impedance* and the network is perfectly matched with itself on the whole frequency band.\n", "\n", "The *characteristic impedance* is usually a frequency-dependent parameter. Having a frequency-dependent *port impedance* is often encountered in electromagnetic simulations. Real-world measurements use a constant *port impedance* instead.\n", "\n", "![simulation](figures/media_simulation.svg)\n", "\n", "The *port impedance* `z0` of the line network and the *characteristic impedance* `z0` of the media are plotted below. These parameters are frequency-dependent. In both cases, the *port impedance* of the line equals the *characteristic impedance* of the media.\n", "\n", "The *port impedance* of `Network` object is also `z0` but it is not a *characteristic impedance*. The `Network` S-Parameters are normalized to its *port impedance*." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# prepare figure\n", "fig2, axes = plt.subplots(1, 2, figsize = (8, 3.5))\n", "\n", "# plot miscrostipline\n", "axes[0].plot(mlin_100.frequency.f_scaled, mlin_100.z0[:, 0].real, marker = '.',\n", " label = f'line {mlin_100.name} port z0')\n", "axes[0].plot(mlin.frequency.f_scaled, mlin.z0.real,\n", " label = 'media mlin characteristic z0')\n", "axes[0].set_ylabel('Impedance (Ohm)')\n", "axes[0].set_xlabel(f'Frequency ({mlin.frequency.unit})')\n", "axes[0].set_title('Microstripline')\n", "axes[0].legend()\n", "\n", "# plot rectangular waveguide\n", "axes[1].plot(wr10_100.frequency.f_scaled, wr10_100.z0[:, 0].real, marker = '.',\n", " label = f'line {wr10_100.name} port z0')\n", "axes[1].plot(wr10.frequency.f_scaled, wr10.z0.real,\n", " label = 'media wr10 characteristic z0')\n", "axes[1].set_ylabel('Impedance (Ohm)')\n", "axes[1].set_xlabel(f'Frequency ({wr10.frequency.unit})')\n", "axes[1].set_title('WR-10')\n", "axes[1].legend()\n", "\n", "# resize plot nicely\n", "fig2.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Measured-like microstripline\n", "\n", "S-Parameters measurement of a microstriplines is done with a VNA (Vector Network Analyzer). The VNA has a known *port impedance* - usually 50 Ohm - and coaxial connectivity.\n", "\n", "A coaxial to microstripline transition is used and the VNA is calibrated at the end of the coaxial cable. In this example let's assume an ideal transition (no length, perfect match on both sides).\n", "\n", "Thus, the transition is only a mismatch between VNA *port impedance* `z0_port` and transmission line *characteristic impedance* `z0`.\n", "\n", "![mline measurement](figures/media_mline_measurement.svg)\n", "\n", "To get S-Parameter network with 50-Ohm *port impedance*, either the *port impedance* `z0_port` can be specified at media object construction or the line can be renormalized from *characteristic* to *port impedance*." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# renormalization method\n", "mlin_100_measured1 = mlin_100.copy()\n", "mlin_100_measured1.renormalize([50, 50])\n", "mlin_100_measured1.name = f'{mlin_100.name} renormalize'\n", "print(mlin_100_measured1)\n", "\n", "# port impedance specified at media construction method\n", "mlin_meas = MLine(f_mlin, w=3e-3, h=1.6e-3, t=35e-6, ep_r=4.5, rho=1.68e-08, z0_port=50)\n", "mlin_100_measured2 = mlin_meas.line(100e-3, unit = 'm', name = 'mlin 100mm z0_port')\n", "print(mlin_100_measured2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The plot below shows that the *characteristic impedance* of the microstripline is now embedded within a network with 50-Ohm *port impedance*. This is equivalent to a VNA measurement with an ideal coaxial to microstripline transition." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# prepare figure\n", "fig3, axes = plt.subplots(2, 2, figsize = (8, 6))\n", "gs = axes[1, 0].get_gridspec()\n", "for ax in axes[1, :]:\n", " ax.remove()\n", "axbig = fig3.add_subplot(gs[1, :])\n", "\n", "# plot return loss\n", "mlin_100_measured1.plot_s_db(0, 0, ax=axes[0,0])\n", "mlin_100_measured2.plot_s_db(0, 0, ax=axes[0,0])\n", "\n", "# plot insertion loss\n", "mlin_100_measured1.plot_s_db(1, 0, ax=axes[0,1])\n", "mlin_100_measured2.plot_s_db(1, 0, ax=axes[0,1])\n", "\n", "# plot port and characteristic impedances\n", "axbig.plot(mlin_100_measured1.frequency.f_scaled, mlin_100_measured1.z0[:, 0].real,\n", " marker = 'd', markevery = 30, label = f'line {mlin_100_measured1.name} z0')\n", "axbig.plot(mlin_100_measured2.frequency.f_scaled, mlin_100_measured2.z0[:, 0].real,\n", " marker = 'x', markevery = 30, label = f'line {mlin_100_measured2.name} z0')\n", "axbig.plot(mlin.frequency.f_scaled, mlin.z0.real, label = 'media mlin z0')\n", "axbig.set_ylabel('Impedance (Ohm)')\n", "axbig.set_xlabel(f'Frequency ({mlin.frequency.unit})')\n", "axbig.legend()\n", "\n", "# resize plot nicely\n", "fig3.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Measured-like WR-10 waveguide\n", "\n", "S-Parameters measurement of a hollow waveguide is done with a VNA (Vector Network Analyzer). The VNA has a known port impedance - usually 50 Ohm - and coaxial connectivity.\n", "\n", "A coaxial to waveguide transition is used. The transition is calibrated at the waveguide interface. Thus, VNA *port impedance* override the *characteristic impedance* of the waveguide.\n", "\n", "The measurement will store the *port impedance* instead of the *characteristic impedance*, which is lost. This is not normalization. The actual *characteristic impedance* of the line is not measured. This method is specific to hollow waveguides.\n", "\n", "![waveguide measurement](figures/media_waveguide_measurement.svg)\n", "\n", "To get an S-Parameters network with 50-Ohm port impedance, either the port impedance `z0_override` can be specified at media object construction or the impedance can be overridden manually." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# override method\n", "wr10_100_measured1 = wr10_100.copy()\n", "wr10_100_measured1.z0[:,:] = 50\n", "wr10_100_measured1.name = f'{wr10_100.name} override'\n", "print(wr10_100_measured1)\n", "\n", "# port impedance at media construction method\n", "wr10_meas = RectangularWaveguide(f_wr10, a=to_meters(100, 'mil'), b=to_meters(50, 'mil'), ep_r=1.0, rho=1.68e-08,\n", " z0_override = 50)\n", "wr10_100_measured2 = wr10_meas.line(100e-3, unit = 'm', name = 'wr10 100mm z0_port')\n", "print(wr10_100_measured2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The plot below shows that the port impedance of the rectangular waveguide measurement is now 50 Ohm. The insertion loss S21 is equal for both methods. However, there is a slight S11 difference between manual override and port impedance specification at construction.\n", "\n", "This is because the default s-parameter definition for network is `s_def = 'power'` and that [the characteristic impedance has an imaginary part](https://scikit-rf.readthedocs.io/en/latest/examples/networktheory/Working%20with%20Complex%20Characteristic%20Impedances.html). For this reason, perfect match is not zero but complex conjugate. The manual override method does not gives the expected results." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# prepare figure\n", "fig4, axes = plt.subplots(2, 2, figsize = (8, 6))\n", "gs = axes[1, 0].get_gridspec()\n", "for ax in axes[1, :]:\n", " ax.remove()\n", "axbig = fig4.add_subplot(gs[1, :])\n", "\n", "# plot return loss\n", "wr10_100_measured1.plot_s_mag(0, 0, ax=axes[0,0])\n", "wr10_100_measured2.plot_s_mag(0, 0, ax=axes[0,0])\n", "\n", "# plot insertion loss\n", "wr10_100_measured1.plot_s_db(1, 0, ax=axes[0,1])\n", "wr10_100_measured2.plot_s_db(1, 0, ax=axes[0,1])\n", "\n", "# plot port and characteristic impedances\n", "axbig.plot(wr10_100_measured1.frequency.f_scaled, wr10_100_measured1.z0[:, 0].real,\n", " marker = 'd', markevery = 30, label = f'line {wr10_100_measured1.name} z0')\n", "axbig.plot(wr10_100_measured2.frequency.f_scaled, wr10_100_measured2.z0[:, 0].real,\n", " marker = 'x', markevery = 30, label = f'line {wr10_100_measured2.name} z0')\n", "axbig.plot(wr10.frequency.f_scaled, wr10.z0.real, label = 'media wr10 z0')\n", "axbig.set_ylabel('Impedance (Ohm)')\n", "axbig.set_xlabel(f'Frequency ({mlin.frequency.unit})')\n", "axbig.legend()\n", "\n", "# resize plot nicely\n", "fig4.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Parameter variation\n", "\n", "The construction parameters of the media can be varied. If, for example, we want to know how much the *characteristic impedance* and *propagation constant* of a microstripline would be affected by a change in relative permittivity of the substrate:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#prepare\n", "fig5, axes = plt.subplots(1, 2, figsize = (8, 3.5))\n", "\n", "# plot\n", "for ep_r in [4.0, 4.5, 5.0]:\n", " ml = MLine(f_mlin, w=3e-3, h=1.6e-3, t=35e-6, ep_r=ep_r, rho=1.68e-08, z0_port=50)\n", " axes[0].plot(f_mlin.f_scaled, ml.z0.real, label=f'ep_r={ep_r:.1f}')\n", " axes[1].plot(f_mlin.f_scaled, ml.beta, label=f'ep_r={ep_r:.1f}')\n", "\n", "axes[0].set_xlabel(f'Frequency ({f_mlin.unit})')\n", "axes[0].set_ylabel('Characteristic Impedance (Ohm)')\n", "axes[0].legend()\n", "axes[1].set_xlabel(f'Frequency ({f_mlin.unit})')\n", "axes[1].set_ylabel('Propagation Constant (rad/m)')\n", "\n", "axes[1].legend()\n", "\n", "# resize plot nicely\n", "fig5.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More detailed examples illustrating how to create various kinds of [Media](../api/media/index.rst) \n", "objects follow. The list of supported media is in the [Media](../api/media/index.rst) API page. The network creation and connection syntax of **skrf** are cumbersome if you need to doing complex circuit design. **skrf**'s synthesis capabilities lend themselves more to scripted applications such as calibration, optimization or batch processing." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Slab of Si in Freespace\n", "\n", "A plane-wave in freespace from 10 to 20 GHz." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from skrf.media import Freespace\n", "\n", "freq = Frequency(10, 20, 101, 'GHz')\n", "air = Freespace(freq)\n", "air" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "air.z0[:2] # 377 Ohm baby!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# plane wave in Si\n", "si = Freespace(freq, ep_r = 11.2)\n", "si.z0[:3] # ~110 Ohm\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simulate a 1cm slab of Si in half-space," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "slab = air.thru() ** si.line(1, unit = 'cm') ** air.thru()\n", "slab.plot_s_db(n = 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Network Synthesis\n", "\n", "Networks are created through methods of a Media object. To create a 1-port short for a microstripline, " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mlin_meas.short(name = 'short')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or to create a $90^{\\circ}$ section of microstripline, " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mlin_meas.line(d = 90, unit = 'deg', name = 'line')" ] }, { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ".. note::\n", "\n", "\tSimple circuits like ``Media.short`` \n", "\tand ``Media.open`` are ideal short and open with\n", "\t:math:`\\Gamma = -1` and :math:`\\Gamma = 1`. They dont account the sophisticated effects of the discontinuities.\n", "\tEventually, these more complex networks could be implemented with methods specific to a given Media, e.g. ``MLine.mlin_short``, should the need arise.\n", "\t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building Circuits\n", "\n", "\n", "Complex circuits can be made by connecting a series of networks. Let's build a $90^{\\circ}$ microstripline delay short." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "delay_short = mlin_meas.line(d = 90, unit = 'deg') ** mlin_meas.short()\n", "delay_short.name = 'delay short'\n", "delay_short" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When `Networks` with more than 2 ports need to be connected together, use \n", "`rf.connect()`. To create a two-port network for a shunted delayed open, you can create an ideal 3-way splitter (a 'tee') and connect the delayed open to one of its ports,\n", "\t" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tee = mlin_meas.tee()\n", "delay_open = mlin_meas.delay_open(40, unit = 'deg')\n", "shunt_open = rf.network.connect(tee, 1, delay_open, 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adding networks in shunt is so common, there is a `Media.shunt()` function to do this, " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mlin_meas.shunt(delay_open)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a specific circuit is created frequently, it may make sense to \n", "use a function to create the circuit. This can be done most quickly using `lambda`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "delay_short = lambda d: mlin_meas.line(d, unit = 'deg') ** mlin_meas.short() # noqa: E731\n", "delay_short(90)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A more useful example may be to create a function for a shunt-stub tuner,\n", "that will work for any media object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def shunt_stub(med, d0, d1):\n", " return med.line(d0, unit = 'deg')**med.shunt_delay_open(d1, unit = 'deg')\n", "\n", "shunt_stub(mlin_meas, 10, 90)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This approach lends itself to design optimization." ] }, { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ".. note::\n", "\n", "\tTo connect complex circuits, you might be interested to use the ``skrf.circuit`` module. \n", "\t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Design Optimization\n", "\n", "\n", "The abilities of `scipy`'s optimizers can be used to automate network design. In this example, scikit-rf is used to automate the single stub impedance matching network design. First, we create a 'cost' function that returns something we want to minimize, such as the reflection coefficient magnitude at the band center. Then, one of scipy's minimization algorithms is used to determine the optimal parameters of the stub lengths to minimize this cost." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from scipy.optimize import fmin\n", "\n", "from skrf.media import CPW\n", "\n", "freq = Frequency(75, 110, 101, 'GHz')\n", "cpw = CPW(freq, w = 10e-6, s = 5e-6, ep_r = 10.6, z0_port = 50)\n", "\n", "# the load we are trying to match\n", "load = cpw.load(.2+.2j)\n", "\n", "# single stub circuit generator function\n", "def shunt_stub(med, d0, d1):\n", " return med.line(d0, unit = 'deg') ** med.shunt_delay_open(d1, unit = 'deg')\n", "\n", "\n", "# define the cost function we want to minimize (this uses sloppy namespace)\n", "def cost(d):\n", " # prevent negative length lines, returning high cost\n", " if d[0] < 0 or d[1] < 0:\n", " return 1e3\n", " return (shunt_stub(cpw, d[0], d[1]) ** load)[100].s_mag.squeeze()\n", "\n", "# initial guess of optimal delay lengths in degrees\n", "d0 = 120,40 # initial guess\n", "\n", "#determine the optimal delays\n", "d_opt = fmin(cost, (120, 40))\n", "\n", "d_opt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "celltoolbar": "Raw Cell Format", "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.8.10" } }, "nbformat": 4, "nbformat_minor": 1 }