├── .bump.cfg.nt ├── figures ├── leaky-cap-schematic.py ├── leaky-cap-chart2.py ├── tfm201610alm.py ├── MCFE1412TR47_JB.py ├── run_tests ├── leaky-cap-chart.py ├── C0603C102K3GACTU.py ├── leaky-cap-schematic.svg ├── Golden │ └── leaky-cap-schematic.svg ├── C0603C102K3GACTU_imp_esr.csv └── tfm201610alm.svg ├── .gitignore ├── setup.py ├── rlc_chart.py ├── README.rst └── LICENSE /.bump.cfg.nt: -------------------------------------------------------------------------------- 1 | major: 1 2 | minor: 0 3 | patch: 0 4 | revision: 0 5 | type: release 6 | files: 7 | setup.py: 8 | version: version 9 | rlc_chart.py: 10 | version: __version__ 11 | date: __released__ 12 | README.rst: 13 | version: Version 14 | date: Released 15 | -------------------------------------------------------------------------------- /figures/leaky-cap-schematic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from svg_schematic import ( 4 | Schematic, Capacitor, Ground, Inductor, Resistor, Pin, Source, Wire 5 | ) 6 | from inform import Error, error, os_error 7 | 8 | try: 9 | with Schematic( 10 | filename = 'leaky-cap.svg', 11 | background = 'none', 12 | line_width = 1.5, 13 | ): 14 | p1 = Pin(kind='in') 15 | c = Capacitor(p=p1.C, xoff=50, value='C', orient='v') 16 | l = Inductor(p=c.n, yoff=-12.5, value='L', orient='v') 17 | r = Resistor(p=l.n, yoff=-12.5, value='Rs', orient='v') 18 | p2 = Pin(C=r.n, xoff=-50, kind='in') 19 | Wire([p1.C, c.p], '-|') 20 | Wire([p2.C, r.n], '-|') 21 | rp = Resistor(C=l.C, xoff=100, value='Rp', orient='v') 22 | Wire([p1.C, rp.p], '-|') 23 | Wire([p2.C, rp.n], '-|') 24 | except Error as e: 25 | e.report() 26 | except OSError as e: 27 | error(os_error(e)) 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | figures/MCFE1412TR47_JB.ac.cache 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | -------------------------------------------------------------------------------- /figures/leaky-cap-chart2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Create the RCL chart for a leaky capacitor with the following parameters: 4 | 5 | Rs = 2 Ω 6 | Rp = 500 kΩ 7 | C = 1 nF 8 | L = 10 μH 9 | fmin = 1 Hz 10 | fmax = 100 MHz 11 | zmin = 1 Ω 12 | zmax = 1 MΩ 13 | filename = "leaky-cap-chart2.svg" 14 | """ 15 | 16 | from rlc_chart import RLC_Chart 17 | from inform import fatal, os_error 18 | from numpy import logspace, log10 as log, pi as π 19 | from quantiphy import Quantity 20 | 21 | # Add parameters as local variables 22 | params = Quantity.extract(__doc__) 23 | globals().update(params) 24 | 25 | f = logspace(log(fmin), log(fmax), 2000, endpoint=True) 26 | jω = 2j*π*f 27 | z1 = Rs + 1/(jω*C) + jω*L 28 | z2 = Rp 29 | z = z1 * z2 / (z1 + z2) 30 | 31 | try: 32 | with RLC_Chart(filename, fmin, fmax, zmin, zmax) as chart: 33 | chart.add_trace(f, abs(z.real), stroke='blue') 34 | chart.add_trace(f, abs(z.imag), stroke='red') 35 | chart.add_trace(f, abs(z)) 36 | except OSError as e: 37 | fatal(os_error(e)) 38 | -------------------------------------------------------------------------------- /figures/tfm201610alm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Convert S-parameters of inductor, measured as a two port, to impedance 3 | 4 | from inform import fatal, os_error 5 | from rlc_chart import RLC_Chart 6 | from cmath import rect, pi as π 7 | from pathlib import Path 8 | 9 | y11 = [] 10 | y12 = [] 11 | y21 = [] 12 | y22 = [] 13 | Zind = [] 14 | freq = [] 15 | z0 = 50 16 | 17 | try: 18 | data = Path('tfm201610alm_r47mtaa.s2p').read_text() 19 | lines = data.splitlines() 20 | for line in lines: 21 | line = line.strip() 22 | if line[0] in '!#': 23 | continue 24 | f, s11m, s11p, s12m, s12p, s21m, s21p, s22m, s22p = line.split() 25 | s11 = rect(float(s11m), π*float(s11p)/180) 26 | s12 = rect(float(s12m), π*float(s12p)/180) 27 | s21 = rect(float(s21m), π*float(s21p)/180) 28 | s22 = rect(float(s22m), π*float(s22p)/180) 29 | Δ = (1 + s11)*(1 + s22) - s12*s21 30 | y11 = ((1 - s11)*(1 + s22) + s12*s21) / Δ / z0 31 | y12 = -2*s12 / Δ / z0 32 | y21 = -2*s21 / Δ / z0 33 | y22 = ((1 + s11)*(1 - s22) + s12*s21) / Δ / z0 34 | f = float(f) 35 | if f: 36 | freq.append(f) 37 | Zind.append(abs(1/y12)) 38 | 39 | with RLC_Chart('tfm201610alm.svg', 100e3, 1e9, 0.1, 1000) as chart: 40 | chart.add_trace(freq, Zind) 41 | 42 | except OSError as e: 43 | fatal(os_error(e)) 44 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import sys 3 | 4 | with open('README.rst', encoding="UTF-8") as f: 5 | readme = f.read() 6 | 7 | setup( 8 | name = 'rlc_chart', 9 | version = '1.0.0', 10 | description = 'A library that renders impedance charts that include capacitance and inductance grids.', 11 | long_description = readme, 12 | long_description_content_type = 'text/x-rst', 13 | author = "Ken Kundert", 14 | author_email = 'rlc_chart@nurdletech.com', 15 | url = "https://nurdletech.com/linux-utilities/rlc_chart", 16 | download_url = "https://github.com/kenkundert/rlc_chart/tarball/master", 17 | license = 'GPLv3+', 18 | zip_safe = True, 19 | py_modules = 'rlc_chart'.split(), 20 | install_requires = 'quantiphy svgwrite'.split(), 21 | python_requires = '>=3.6', 22 | classifiers = [ 23 | 'Development Status :: 5 - Production/Stable', 24 | 'Intended Audience :: Science/Research', 25 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 26 | 'Natural Language :: English', 27 | 'Operating System :: POSIX :: Linux', 28 | 'Programming Language :: Python :: 3.6', 29 | 'Programming Language :: Python :: 3.7', 30 | 'Programming Language :: Python :: 3.8', 31 | 'Programming Language :: Python :: 3.9', 32 | 'Programming Language :: Python :: 3.10', 33 | 'Topic :: Utilities', 34 | 'Topic :: Scientific/Engineering', 35 | ], 36 | ) 37 | -------------------------------------------------------------------------------- /figures/MCFE1412TR47_JB.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from psf_utils import PSF 4 | from inform import Error, os_error, fatal 5 | from rlc_chart import RLC_Chart 6 | 7 | try: 8 | psf = PSF('MCFE1412TR47_JB.ac') 9 | sweep = psf.get_sweep() 10 | z_ckt = psf.get_signal('1') 11 | z_mod = psf.get_signal('2') 12 | 13 | with RLC_Chart('MCFE1412TR47_JB.svg', 100, 1e9, 0.01, 1000) as chart: 14 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate), stroke='red') 15 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate), stroke='blue') 16 | 17 | with RLC_Chart('MCFE1412TR47_JB.rxz.svg', 100, 1e9, 0.01, 1000) as chart: 18 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate.real), stroke='green') 19 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate.imag), stroke='orange') 20 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate.real), stroke='blue') 21 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate.imag), stroke='red') 22 | chart.add(chart.text( 23 | " ← poor loss modeling", 24 | insert = ( 25 | chart.to_x(sweep.abscissa[-1]), 26 | chart.to_y(abs(z_mod.ordinate.real[-1])) 27 | ), 28 | **chart.text_props 29 | )) 30 | chart.add(chart.text( 31 | "MCFE1412TR47_JB", 32 | insert = (chart.WIDTH/2, 24), 33 | font_size = 24, 34 | fill = 'black', 35 | text_anchor = 'middle', 36 | )) 37 | 38 | except Error as e: 39 | e.terminate() 40 | except OSError as e: 41 | fatal(os_error(e)) 42 | 43 | -------------------------------------------------------------------------------- /figures/run_tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from shlib import Run, to_path, set_prefs 4 | from inform import Error, Info, display, error, os_error, InformantFactory 5 | 6 | match = InformantFactory(clone=display, message_color="green") 7 | no_match = InformantFactory(clone=display, message_color="red") 8 | 9 | set_prefs(use_inform=True) 10 | 11 | test_cases = [ 12 | Info( 13 | cmd = "leaky-cap-chart.py", 14 | results = ("leaky-cap-chart.svg",) 15 | ), 16 | Info( 17 | cmd = "leaky-cap-chart2.py", 18 | results = ("leaky-cap-chart2.svg",) 19 | ), 20 | Info( 21 | cmd = "MCFE1412TR47_JB.py", 22 | results = ( 23 | "MCFE1412TR47_JB.svg", 24 | "MCFE1412TR47_JB.rxz.svg" 25 | ) 26 | ), 27 | Info( 28 | cmd = "C0603C102K3GACTU.py", 29 | results = ("C0603C102K3GACTU.svg",) 30 | ), 31 | Info( 32 | cmd = "tfm201610alm.py", 33 | results = ("tfm201610alm.svg",) 34 | ), 35 | ] 36 | 37 | for test_case in test_cases: 38 | try: 39 | cmd = to_path(test_case.cmd) 40 | display(f"{cmd.stem}:") 41 | Run(["python3", cmd], modes="soeW") 42 | 43 | for result in test_case.results: 44 | result = to_path(result) 45 | diff = Run(['diff', result, "Golden" / result], modes="sOEW1") 46 | if diff.status: 47 | no_match(f" {result.stem}: no match!") 48 | else: 49 | match(f" {result.stem}: matches.") 50 | except OSError as e: 51 | error(os_error(e)) 52 | except Error as e: 53 | e.report() 54 | 55 | -------------------------------------------------------------------------------- /figures/leaky-cap-chart.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Create the RCL chart for a leaky capacitor with the following parameters: 4 | 5 | Rs = 2 Ω 6 | Rp = 500 kΩ 7 | C = 1 nF 8 | L = 10 μH 9 | fmin = 1 Hz 10 | fmax = 100 MHz 11 | zmin = 1 Ω 12 | zmax = 1 MΩ 13 | filename = "leaky-cap-chart.svg" 14 | """ 15 | 16 | from rlc_chart import RLC_Chart 17 | from inform import display, fatal, os_error 18 | from math import log10 as log, pi as π 19 | from quantiphy import Quantity 20 | 21 | # Add parameters as local variables 22 | params = Quantity.extract(__doc__) 23 | globals().update(params) 24 | #display(f"Parameters:") 25 | #for k, v in params.items(): 26 | # display(f' {k} = {v}') 27 | 28 | try: 29 | with RLC_Chart(filename, fmin, fmax, zmin, zmax) as chart: 30 | mult = 10**((log(fmax) - log(fmin))/400) 31 | f = fmin 32 | freq = [] 33 | impedance = [] 34 | resistance = [] 35 | reactance = [] 36 | 37 | # Compute impedance of component 38 | # z = (Rs + 1/(jωC + jωL) ‖ Rp 39 | j2π = 2j*π 40 | while(f <= 1.01*fmax): 41 | jω = j2π*f 42 | z1 = Rs + 1/(jω*C) + jω*L 43 | z2 = Rp 44 | z = z1 * z2 / (z1 + z2) 45 | freq += [f] 46 | impedance += [abs(z)] 47 | resistance += [abs(z.real)] 48 | reactance += [abs(z.imag)] 49 | f *= mult 50 | chart.add_trace(freq, impedance) 51 | #chart.add_trace(freq, resistance, stroke='blue') 52 | #chart.add_trace(freq, reactance, stroke='red') 53 | except OSError as e: 54 | fatal(os_error(e)) 55 | -------------------------------------------------------------------------------- /figures/C0603C102K3GACTU.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from rlc_chart import RLC_Chart 4 | from inform import fatal, os_error 5 | from pathlib import Path 6 | from math import pi as π 7 | import csv 8 | 9 | fmin = 100 10 | fmax = 10e9 11 | zmin = 0.01 12 | zmax = 1e6 13 | cmod = 1e-9 14 | lmod = 700e-12 15 | rmod = 20e-3 16 | 17 | def model(f): 18 | return 1/(2j*π*f*cmod) + rmod + 2j*π*f*lmod 19 | 20 | frequency = [] 21 | z_data = [] 22 | r_data = [] 23 | z_model = [] 24 | r_model = [] 25 | try: 26 | contents = Path('C0603C102K3GACTU_imp_esr.csv').read_text() 27 | data = csv.DictReader(contents.splitlines(), delimiter=',') 28 | for row in data: 29 | f = float(row['Frequency']) 30 | z = model(f) 31 | frequency.append(f) 32 | z_data.append(float(row['Impedance'])) 33 | r_data.append(float(row['ESR'])) 34 | z_model.append(abs(z)) 35 | r_model.append(z.real) 36 | 37 | with RLC_Chart('C0603C102K3GACTU.svg', fmin, fmax, zmin, zmax) as chart: 38 | 39 | # add annotations 40 | svg_text_args = dict(font_size=22, fill='black') 41 | 42 | # capacitance annotations 43 | chart.add(chart.text( 44 | "C = 1 nF", 45 | insert = (chart.to_x(150e3), chart.to_y(1.5e3)), 46 | **svg_text_args 47 | )) 48 | chart.add_line(1e3, 190.23e6, c=1e-9) 49 | 50 | # inductance annotations 51 | chart.add(chart.text( 52 | "L = 700 pH", 53 | insert = (chart.to_x(6e9), chart.to_y(30)), 54 | text_anchor = 'end', 55 | **svg_text_args 56 | )) 57 | chart.add_line(190.232e6, 10e9, l=700e-12) 58 | 59 | # resistance annotations 60 | chart.add(chart.text( 61 | "ESR = 20 mΩ", 62 | insert = (chart.to_x(100e3), chart.to_y(25e-3)), 63 | text_anchor = 'start', 64 | **svg_text_args 65 | )) 66 | chart.add_line(100e3, 1e9, r=20e-3) 67 | 68 | # resonant frequency annotations 69 | chart.add(chart.text( 70 | "f₀ = 190 MHz", 71 | insert = (chart.to_x(190.23e6), chart.to_y(400)), 72 | text_anchor = 'middle', 73 | **svg_text_args 74 | )) 75 | chart.add_line(1e-2, 300, f=190.23e6) 76 | 77 | # Q annotations 78 | chart.add(chart.text( 79 | "Q = 42", 80 | insert = (chart.to_x(10e6), chart.to_y(100e-3)), 81 | text_anchor = 'start', 82 | **svg_text_args 83 | )) 84 | chart.add_line(10e6, 190.23e6, r=836.66e-3) 85 | 86 | # title 87 | chart.add(chart.text( 88 | "C0603C102K3GACTU 1nF Ceramic Capacitor", 89 | insert = (chart.WIDTH/2, 36), 90 | font_size = 24, 91 | fill = 'black', 92 | text_anchor = 'middle', 93 | )) 94 | 95 | # add traces last, so they are on top 96 | chart.add_trace(frequency, z_data, stroke='red') 97 | chart.add_trace(frequency, r_data, stroke='blue') 98 | chart.add_trace(frequency, z_model, stroke='red', stroke_dasharray=(10,5)) 99 | chart.add_trace(frequency, r_model, stroke='blue', stroke_dasharray=(10,5)) 100 | 101 | except OSError as e: 102 | fatal(os_error(e)) 103 | -------------------------------------------------------------------------------- /figures/leaky-cap-schematic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | C 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | L 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Rs 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Rp 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /figures/Golden/leaky-cap-schematic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | C 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | L 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Rs 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Rp 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /figures/C0603C102K3GACTU_imp_esr.csv: -------------------------------------------------------------------------------- 1 | Frequency,Impedance,ESR 2 | 100,1591313,2886.394 3 | 104.7129,1519692,2755.397 4 | 109.6478,1451295,2630.52 5 | 114.8153,1385976,2511.135 6 | 120.2264,1323597,2397.327 7 | 125.8925,1264025,2288.676 8 | 131.8256,1207135,2184.949 9 | 138.0385,1152804,2085.922 10 | 144.544,1100920,1991.516 11 | 151.3562,1051370,1901.381 12 | 158.4893,1004051,1815.326 13 | 165.9587,958861.1,1733.166 14 | 173.7801,915705.2,1654.833 15 | 181.9701,874491.7,1579.937 16 | 190.5461,835133.1,1508.529 17 | 199.5262,797546,1440.349 18 | 208.9296,761650.6,1375.25 19 | 218.7761,727370.7,1313.094 20 | 229.0867,694633.6,1253.746 21 | 239.8834,663369.5,1197.08 22 | 251.1887,633513.1,1143.052 23 | 263.0269,605000.3,1091.462 24 | 275.4229,577770.8,1042.132 25 | 288.4032,551766.9,995.0966 26 | 301.9952,526933.3,950.1842 27 | 316.2278,503217.4,907.299 28 | 331.1311,480569,866.3492 29 | 346.7368,458939.7,827.2476 30 | 363.078,438284.2,789.9633 31 | 380.1893,418558.1,754.3093 32 | 398.1071,399719.9,720.2645 33 | 416.8693,381729.6,687.8019 34 | 436.5159,364548.8,656.7585 35 | 457.0883,348141.3,627.1578 36 | 478.6302,332472.4,598.8914 37 | 501.1873,317508.7,571.8612 38 | 524.8075,303218.4,546.0872 39 | 549.5409,289571.4,521.4747 40 | 575.4399,276538.6,497.9715 41 | 602.5596,264092.3,475.5276 42 | 630.9573,252206.2,454.0953 43 | 660.6934,240855.1,433.6577 44 | 691.8308,230014.8,414.1125 45 | 724.4358,219662.4,395.4482 46 | 758.5778,209775.9,377.6249 47 | 794.3284,200334.4,360.629 48 | 831.7639,191317.9,344.3753 49 | 870.9637,182707.2,328.8541 50 | 912.0109,174484,314.0323 51 | 954.9926,166631,299.8986 52 | 1000,159131.3,286.382 53 | 1047.129,151969.2,273.4926 54 | 1096.478,145129.5,261.1662 55 | 1148.153,138597.6,249.3952 56 | 1202.264,132359.7,238.1706 57 | 1258.925,126402.5,227.4361 58 | 1318.256,120713.5,217.1998 59 | 1380.385,115280.4,207.4104 60 | 1445.44,110092,198.0754 61 | 1513.562,105137,189.1606 62 | 1584.893,100405.1,180.6349 63 | 1659.587,95886.1,172.505 64 | 1737.801,91570.52,164.741 65 | 1819.701,87449.17,157.3265 66 | 1905.461,83513.31,150.2456 67 | 1995.262,79754.59,143.4739 68 | 2089.296,76165.06,137.0166 69 | 2187.761,72737.06,130.8498 70 | 2290.867,69463.36,124.9606 71 | 2398.833,66336.96,119.3285 72 | 2511.887,63351.32,113.9578 73 | 2630.269,60500.03,108.8289 74 | 2754.229,57777.08,103.9377 75 | 2884.032,55176.7,99.25314 76 | 3019.952,52693.33,94.786 77 | 3162.278,50321.75,90.51994 78 | 3311.311,48056.9,86.44588 79 | 3467.368,45893.98,82.55516 80 | 3630.78,43828.41,78.83435 81 | 3801.893,41855.81,75.29122 82 | 3981.071,39971.98,71.90255 83 | 4168.693,38172.95,68.6664 84 | 4365.159,36454.88,65.57588 85 | 4570.883,34814.14,62.62449 86 | 4786.302,33247.24,59.80988 87 | 5011.873,31750.87,57.11422 88 | 5248.075,30321.85,54.54366 89 | 5495.409,28957.14,52.08878 90 | 5754.399,27653.86,49.7477 91 | 6025.596,26409.23,47.50869 92 | 6309.573,25220.62,45.37045 93 | 6606.934,24085.5,43.32844 94 | 6918.308,23001.48,41.38109 95 | 7244.358,21966.24,39.51863 96 | 7585.778,20977.59,37.73998 97 | 7943.284,20033.44,36.0414 98 | 8317.64,19131.79,34.41928 99 | 8709.637,18270.72,32.87016 100 | 9120.109,17448.4,31.39283 101 | 9549.927,16663.09,29.97992 102 | 10000,15913.13,28.63251 103 | 10471.29,15196.93,27.34384 104 | 10964.78,14512.95,26.11489 105 | 11481.54,13859.76,24.93952 106 | 12022.64,13235.97,23.81864 107 | 12589.25,12640.25,22.74663 108 | 13182.56,12071.35,21.7243 109 | 13803.84,11528.05,20.74655 110 | 14454.39,11009.2,19.81411 111 | 15135.61,10513.71,18.92359 112 | 15848.92,10040.51,18.07308 113 | 16595.86,9588.615,17.25966 114 | 17378,9157.057,16.48394 115 | 18197.02,8744.912,15.74306 116 | 19054.62,8351.327,15.03451 117 | 19952.63,7975.456,14.3588 118 | 20892.97,7616.503,13.71255 119 | 21877.62,7273.704,13.09625 120 | 22908.68,6946.332,12.50765 121 | 23988.34,6633.696,11.94629 122 | 25118.87,6335.132,11.40938 123 | 26302.68,6050.003,10.89659 124 | 27542.29,5777.708,10.40685 125 | 28840.32,5517.669,9.939125 126 | 30199.52,5269.333,9.49305 127 | 31622.78,5032.175,9.066391 128 | 33113.11,4805.689,8.659482 129 | 34673.68,4589.398,8.270288 130 | 36307.8,4382.841,7.89911 131 | 38018.93,4185.582,7.544091 132 | 39810.71,3997.199,7.205504 133 | 41686.93,3817.296,6.882114 134 | 43651.57,3645.489,6.573236 135 | 45708.8,3481.415,6.278222 136 | 47862.99,3324.726,5.996448 137 | 50118.7,3175.089,5.727321 138 | 52480.72,3032.187,5.470634 139 | 54954.06,2895.716,5.225105 140 | 57544.02,2765.385,4.990921 141 | 60255.99,2640.921,4.766922 142 | 63095.76,2522.06,4.553277 143 | 66069.37,2408.549,4.349207 144 | 69183.13,2300.147,4.154284 145 | 72443.62,2196.623,3.968096 146 | 75857.78,2097.758,3.790502 147 | 79432.84,2003.344,3.620618 148 | 83176.39,1913.179,3.458576 149 | 87096.37,1827.072,3.303786 150 | 91201.09,1744.84,3.155923 151 | 95499.27,1666.309,3.014677 152 | 100000,1591.313,2.879943 153 | 104712.9,1519.692,2.751049 154 | 109647.8,1451.294,2.628096 155 | 114815.3,1385.975,2.510639 156 | 120226.4,1323.596,2.398588 157 | 125892.5,1264.025,2.291387 158 | 131825.6,1207.134,2.189121 159 | 138038.4,1152.804,2.091419 160 | 144543.9,1100.919,1.998208 161 | 151356.1,1051.37,1.909026 162 | 158489.3,1004.05,1.823943 163 | 165958.6,958.8605,1.742767 164 | 173780,915.7046,1.665093 165 | 181970.2,874.4902,1.591088 166 | 190546.2,835.1315,1.520274 167 | 199526.3,797.5444,1.452706 168 | 208929.7,761.6489,1.38814 169 | 218776.2,727.369,1.326531 170 | 229086.8,694.6319,1.267655 171 | 239883.4,663.3682,1.211471 172 | 251188.7,633.5116,1.157776 173 | 263026.8,604.9987,1.106533 174 | 275422.9,577.7692,1.057557 175 | 288403.2,551.7651,1.010814 176 | 301995.2,526.9314,0.9661361 177 | 316227.8,503.2155,0.9234924 178 | 331131.1,480.567,0.8827875 179 | 346736.8,458.9377,0.8438758 180 | 363078,438.2819,0.8067307 181 | 380189.3,418.5557,0.7712697 182 | 398107.1,399.7175,0.7374144 183 | 416869.3,381.7271,0.7050894 184 | 436515.7,364.5463,0.6741804 185 | 457088,348.1387,0.6446669 186 | 478629.9,332.4697,0.6165237 187 | 501187,317.5058,0.5896078 188 | 524807.2,303.2155,0.563902 189 | 549540.6,289.5683,0.5393849 190 | 575440.3,276.5349,0.5159315 191 | 602559.9,264.0885,0.4935602 192 | 630957.6,252.2022,0.4721875 193 | 660693.7,240.8509,0.4517964 194 | 691831.2,230.0104,0.4322841 195 | 724436.2,219.6579,0.4136915 196 | 758577.8,209.7713,0.3958969 197 | 794328.4,200.3296,0.3789375 198 | 831763.9,191.3128,0.3627256 199 | 870963.7,182.7019,0.347227 200 | 912010.9,174.4784,0.3324511 201 | 954992.6,166.6251,0.3183217 202 | 1000000,159.1252,0.3048475 203 | 1047129,151.9781,0.2919705 204 | 1096478,145.1518,0.2796698 205 | 1148154,138.6322,0.2679344 206 | 1202264,132.4053,0.2567048 207 | 1258925,126.4581,0.2460036 208 | 1318256,120.7779,0.2357745 209 | 1380384,115.3528,0.2260091 210 | 1445439,110.1713,0.2166714 211 | 1513561,105.2225,0.2077667 212 | 1584893,100.4959,0.1992605 213 | 1659586,95.98157,0.1911335 214 | 1737800,91.66991,0.1833781 215 | 1819702,87.55179,0.1759648 216 | 1905462,83.61864,0.1688873 217 | 1995263,79.86211,0.1621189 218 | 2089297,76.27423,0.1556631 219 | 2187763,72.84744,0.1495034 220 | 2290868,69.57452,0.143616 221 | 2398834,66.44849,0.1379951 222 | 2511887,63.46283,0.132627 223 | 2630269,60.61119,0.1275059 224 | 2754229,57.88754,0.1226045 225 | 2884032,55.28616,0.117932 226 | 3019952,52.80154,0.1134694 227 | 3162278,50.42842,0.1092054 228 | 3311311,48.16181,0.1051296 229 | 3467368,45.9969,0.1012431 230 | 3630780,43.92915,0.09752996 231 | 3801893,41.95415,0.09398062 232 | 3981071,40.06779,0.09059592 233 | 4168693,38.26603,0.08736165 234 | 4365157,36.54507,0.08427379 235 | 4570881,34.90131,0.08132398 236 | 4786299,33.33125,0.07850429 237 | 5011870,31.83158,0.07581493 238 | 5248072,30.39914,0.07324794 239 | 5495406,29.03089,0.07079246 240 | 5754403,27.72392,0.0684486 241 | 6025599,26.47555,0.06621274 242 | 6309576,25.28308,0.0640749 243 | 6606937,24.144,0.06203485 244 | 6918312,23.05592,0.06008625 245 | 7244362,22.01653,0.0582258 246 | 7585778,21.02363,0.05644769 247 | 7943284,20.07513,0.05474886 248 | 8317639,19.16903,0.05312855 249 | 8709637,18.30341,0.05158113 250 | 9120109,17.47645,0.05009941 251 | 9549926,16.68639,0.04868881 252 | 10000000,15.93157,0.04734003 253 | 10471290,15.2104,0.04605214 254 | 10964780,14.52135,0.04482218 255 | 11481540,13.86297,0.04364887 256 | 12022640,13.23386,0.04252754 257 | 12589250,12.6327,0.04145693 258 | 13182560,12.05822,0.04043414 259 | 13803840,11.5092,0.03945636 260 | 14454390,10.98449,0.03852476 261 | 15135610,10.48298,0.03763369 262 | 15848930,10.0036,0.03678168 263 | 16595860,9.545348,0.03596937 264 | 17378000,9.107254,0.03519369 265 | 18197020,8.688385,0.03445271 266 | 19054620,8.287873,0.03374554 267 | 19952630,7.90486,0.03306908 268 | 20892970,7.538542,0.03242406 269 | 21877620,7.188143,0.03180811 270 | 22908680,6.852923,0.03121889 271 | 23988340,6.532175,0.03065644 272 | 25118870,6.22522,0.0301204 273 | 26302680,5.93141,0.02960721 274 | 27542290,5.650126,0.02911828 275 | 28840320,5.380771,0.02865047 276 | 30199520,5.122779,0.02820382 277 | 31622780,4.875604,0.02777731 278 | 33113110,4.638723,0.02737018 279 | 34673680,4.411636,0.0269818 280 | 36307800,4.193866,0.02661011 281 | 38018930,3.984948,0.02625567 282 | 39810710,3.784445,0.02591699 283 | 41686930,3.591932,0.02559341 284 | 43651570,3.407001,0.0252845 285 | 45708800,3.229264,0.02498943 286 | 47862990,3.058343,0.02470811 287 | 50118700,2.89388,0.02443873 288 | 52480720,2.735524,0.02418197 289 | 54954060,2.582943,0.02393681 290 | 57544020,2.43581,0.02370235 291 | 60255990,2.293821,0.02347878 292 | 63095760,2.156673,0.02326526 293 | 66069370,2.024076,0.02306129 294 | 69183120,1.895751,0.02286655 295 | 72443620,1.771425,0.02268037 296 | 75857780,1.650836,0.02250277 297 | 79432840,1.53373,0.02233318 298 | 83176390,1.41986,0.02217125 299 | 87096370,1.308983,0.02201645 300 | 91201090,1.200867,0.02186879 301 | 95499260,1.095284,0.02172783 302 | 100000000,0.9920101,0.02159318 303 | 104713000,0.8908262,0.02146456 304 | 109647800,0.7915271,0.02134187 305 | 114815500,0.6938951,0.02122469 306 | 120226400,0.597736,0.02111276 307 | 125892600,0.502803,0.01999574 308 | 131825600,0.4089907,0.01993923 309 | 138038500,0.3160793,0.0198853 310 | 144543900,0.2239431,0.01983388 311 | 151356200,0.1326141,0.01978481 312 | 158489200,0.04412772,0.01973802 313 | 165958800,0.05573963,0.01973855 314 | 173780000,0.1317696,0.01978632 315 | 181970200,0.2136336,0.01983641 316 | 190546000,0.2955249,0.01988886 317 | 199526300,0.3773877,0.01994385 318 | 208929500,0.4592994,0.02000143 319 | 218776200,0.5413874,0.0200618 320 | 229086600,0.6237746,0.02012502 321 | 239883400,0.7066042,0.02019127 322 | 251188400,0.790006,0.02026074 323 | 263026800,0.8741274,0.02033342 324 | 275422600,0.9590993,0.02040964 325 | 288403200,1.045071,0.02048965 326 | 301994800,1.132176,0.02057337 327 | 316227800,1.220565,0.02066101 328 | 331131500,1.310378,0.020753 329 | 346736800,1.401757,0.02084962 330 | 363078400,1.494859,0.02095061 331 | 380189300,1.589822,0.02105665 332 | 398107500,1.68681,0.02116782 333 | 416869300,1.785966,0.02128421 334 | 436516200,1.887457,0.02140626 335 | 457088000,1.991435,0.02153455 336 | 478630400,2.098075,0.02166878 337 | 501187000,2.207532,0.02180981 338 | 524807800,2.319988,0.02195795 339 | 549540600,2.435611,0.02211292 340 | 575440300,2.554592,0.02227571 341 | 602559200,2.677105,0.02244657 342 | 630957600,2.803353,0.02262614 343 | 660693000,2.933523,0.0228143 344 | 691831200,3.06783,0.02301231 345 | 724435400,3.206471,0.0232203 346 | 758577800,3.349678,0.02343868 347 | 794327600,3.497664,0.02366828 348 | 831763900,3.650674,0.02390966 349 | 870962800,3.808937,0.02416367 350 | 912010900,3.972723,0.02443099 351 | 954991600,4.142273,0.02471221 352 | 1000000000,4.317879,0.02500882 353 | 1047130000,4.499817,0.02532147 354 | 1096478000,4.688374,0.02565057 355 | 1148155000,4.883879,0.02599859 356 | 1202264000,5.086636,0.02636616 357 | 1258926000,5.297007,0.02675449 358 | 1318256000,5.515324,0.02716565 359 | 1380385000,5.741984,0.02760138 360 | 1445439000,5.977359,0.02806314 361 | 1513562000,6.221887,0.02855464 362 | 1584893000,6.475976,0.02907748 363 | 1659588000,6.743997,0.02963521 364 | 1737800000,7.077475,0.03024196 365 | 1819702000,7.427292,0.03089509 366 | 1905460000,7.794355,0.03159853 367 | 1995263000,8.179702,0.03236128 368 | 2089295000,8.584383,0.03319026 369 | 2187762000,9.009602,0.03409537 370 | 2290866000,9.456568,0.03508725 371 | 2398834000,9.926688,0.03618269 372 | 2511884000,10.42138,0.03739563 373 | 2630268000,10.94231,0.03874822 374 | 2754226000,11.49116,0.04026791 375 | 2884032000,12.06988,0.04198371 376 | 3019949000,12.68052,0.04393544 377 | 3162278000,13.32544,0.04617077 378 | 3311315000,14.00714,0.04875042 379 | 3467368000,14.72841,0.05174785 380 | 3630784000,15.49244,0.05525707 381 | 3801893000,16.30268,0.05939584 382 | 3981075000,17.16311,0.0643133 383 | 4168693000,18.07808,0.07020127 384 | 4365162000,19.05268,0.07729582 385 | 4570881000,20.0925,0.0859049 386 | 4786305000,21.20417,0.09642354 387 | 5011870000,22.39505,0.1093635 388 | 5248078000,23.67392,0.1253876 389 | 5495406000,25.05071,0.1453589 390 | 5754402000,26.53733,0.1704157 391 | 6025592000,28.14755,0.2020533 392 | 6309576000,29.898,0.24228 393 | 6606930000,31.80828,0.2937833 394 | 6918312000,33.9024,0.3602147 395 | 7244354000,36.20924,0.4465502 396 | 7585778000,38.76479,0.559715 397 | 7943276000,41.61336,0.709388 398 | 8317639000,44.8114,0.9093502 399 | 8709628000,48.43035,1.179489 400 | 9120110000,52.56367,1.549106 401 | 9549916000,57.33379,2.062164 402 | 10000000000,62.90642,2.786386 403 | -------------------------------------------------------------------------------- /rlc_chart.py: -------------------------------------------------------------------------------- 1 | # RLC Chart by Ken Kundert 2 | # encoding: utf8 3 | 4 | # Description {{{1 5 | """ 6 | *RLC Chart* is a Python library that creates SVG impedance charts with 7 | capacitance and inductance overlays. 8 | """ 9 | __version__ = '1.0.0' 10 | __released__ = '2022-01-25' 11 | 12 | 13 | # License {{{1 14 | # Copyright (C) 2018-2023 Kenneth S. Kundert 15 | # 16 | # This program is free software: you can redistribute it and/or modify 17 | # it under the terms of the GNU General Public License as published by 18 | # the Free Software Foundation, either version 3 of the License, or 19 | # (at your option) any later version. 20 | # 21 | # This program is distributed in the hope that it will be useful, 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | # GNU General Public License for more details. 25 | # 26 | # You should have received a copy of the GNU General Public License 27 | # along with this program. If not, see [http://www.gnu.org/licenses/]. 28 | 29 | 30 | # Imports {{{1 31 | from svgwrite import Drawing 32 | from pathlib import Path 33 | from math import ceil, floor, log10 as log, pi as π 34 | from quantiphy import Quantity 35 | 36 | Quantity.set_prefs( 37 | map_sf = Quantity.map_sf_to_sci_notation, 38 | spacer = Quantity.narrow_non_breaking_space, 39 | output_sf = 'YZEPTGMkmunpfazy', 40 | ) 41 | 42 | # RLC_Chart class {{{1 43 | class RLC_Chart(Drawing): 44 | 45 | # settings {{{2 46 | TRACE_WIDTH = 0.025 47 | TRACE_COLOR = 'black' 48 | MAJOR_LINE_WIDTH = 0.01 49 | MINOR_LINE_WIDTH = 0.005 50 | OUTLINE_LINE_WIDTH = 0.015 51 | OUTLINE_LINE_COLOR = 'black' 52 | FZ_GRID_COLOR = 'grey' 53 | CL_GRID_COLOR = 'grey' 54 | BACKGROUND = 'white' 55 | AXES = 'FZCL' 56 | MINOR_DIVS = '123456789' 57 | DECADE = 1 58 | LEFT_MARGIN = 1 59 | RIGHT_MARGIN = 1 60 | TOP_MARGIN = 1 61 | BOTTOM_MARGIN = 1 62 | FONT_FAMILY = 'sans-serif' 63 | FONT_SIZE = 12 64 | TEXT_COLOR = 'black' 65 | TEXT_OFFSET = 0.15 66 | PIXELS_PER_UNIT = 96 # 96 pixels per inch 67 | 68 | # constructor {{{2 69 | def __init__(self, filename, fmin, fmax, zmin, zmax, **kwargs): 70 | # process arguments {{{3 71 | self.filename = Path(filename) 72 | assert fmin < fmax, "fmin must be less that fmax." 73 | assert 0 < fmin, "fmin must be greater than zero." 74 | assert zmin < zmax, "zmin must be less that zmax." 75 | assert 0 < zmin, "zmin must be greater than zero." 76 | svg_args = {} 77 | for k, v in kwargs.items(): 78 | if hasattr(self, k.upper()): 79 | setattr(self, k.upper(), v) 80 | else: 81 | svg_args[k] = v 82 | text_props = dict( 83 | font_family = self.FONT_FAMILY, 84 | font_size = self.FONT_SIZE, 85 | fill = self.TEXT_COLOR, 86 | ) 87 | self.text_props = text_props 88 | 89 | def to_pixels(d): 90 | return d * self.PIXELS_PER_UNIT 91 | self.to_pixels = to_pixels 92 | 93 | # find bounds {{{3 94 | x0 = floor(log(fmin)) 95 | x1 = ceil(log(fmax)) 96 | y0 = floor(log(zmin)) 97 | y1 = ceil(log(zmax)) 98 | fmin = self.fmin = 10**x0 99 | fmax = self.fmax = 10**x1 100 | zmin = self.zmin = 10**y0 101 | zmax = self.zmax = 10**y1 102 | grid_width = self.DECADE * (x1 - x0) 103 | grid_height = self.DECADE * (y1 - y0) 104 | canvas_width = grid_width + self.LEFT_MARGIN + self.RIGHT_MARGIN 105 | canvas_height = grid_height + self.TOP_MARGIN + self.BOTTOM_MARGIN 106 | self.HEIGHT = to_pixels(canvas_height) 107 | self.WIDTH = to_pixels(canvas_width) 108 | 109 | # create canvas 110 | super().__init__( 111 | filename, 112 | size = (to_pixels(canvas_width), to_pixels(canvas_height)), 113 | **svg_args 114 | ) 115 | 116 | # coordinate transformations {{{3 117 | # coordinate transformations for base units (Hz, Ω) 118 | def x(f): 119 | x = log(f) 120 | X = self.LEFT_MARGIN + grid_width*(x-x0)/(x1-x0) 121 | return to_pixels(X) 122 | 123 | def y(z): 124 | y = log(z) 125 | Y = canvas_height - self.BOTTOM_MARGIN - grid_height*(y-y0)/(y1-y0) 126 | return to_pixels(Y) 127 | 128 | # coordinate transformations for log units (log(Hz), log(Ω)) 129 | def X(x): 130 | X = self.LEFT_MARGIN + grid_width*(x-x0)/(x1-x0) 131 | return to_pixels(X) 132 | 133 | def Y(y): 134 | Y = canvas_height - (grid_height*(y-y0)/(y1-y0) + self.BOTTOM_MARGIN) 135 | return to_pixels(Y) 136 | 137 | self.to_x = x 138 | self.to_y = y 139 | 140 | # Draw traditional FZ log-log grid {{{3 141 | minor_divs = [log(int(d)) for d in self.MINOR_DIVS.lstrip('1')] 142 | 143 | # draw background 144 | attrs = dict(stroke_linecap='round', fill='none', stroke=self.FZ_GRID_COLOR) 145 | grid = self.g(id='grid') 146 | self.add(grid) 147 | background = self.polygon([ 148 | (X(x0), Y(y0)), 149 | (X(x0), Y(y1)), 150 | (X(x1), Y(y1)), 151 | (X(x1), Y(y0)), 152 | ], 153 | fill = self.BACKGROUND, 154 | stroke='none' 155 | ) 156 | grid.add(background) 157 | 158 | # create clipping region 159 | clipper = self.clipPath(id='plotting-region') 160 | self.defs.add(clipper) 161 | clipper.add(background) 162 | 163 | 164 | # minor FZ divisions {{{4 165 | if 'Z' in self.AXES: 166 | attrs['stroke_width'] = to_pixels(self.MINOR_LINE_WIDTH) 167 | for major in range(y0, y1): 168 | for minor in minor_divs: 169 | v = major + minor 170 | grid.add( 171 | self.line(start=(X(x0), Y(v)), end=(X(x1), Y(v)), **attrs) 172 | ) 173 | if 'F' in self.AXES: 174 | for major in range(x0, x1): 175 | for minor in minor_divs: 176 | v = major + minor 177 | grid.add( 178 | self.line(start=(X(v), Y(y0)), end=(X(v), Y(y1)), **attrs) 179 | ) 180 | 181 | # major FZ divisions and labels {{{4 182 | if 'Z' in self.AXES or 'z' in self.AXES: 183 | attrs['stroke_width'] = to_pixels(self.MAJOR_LINE_WIDTH) 184 | for v in range(y0, y1+1): 185 | grid.add(self.line(start=(X(x0), Y(v)), end=(X(x1), Y(v)), **attrs)) 186 | z = 10**v 187 | grid.add(self.text( 188 | Quantity(z, 'Ω').render(), 189 | insert = (X(x0) - to_pixels(self.TEXT_OFFSET), Y(v) + 0.35*self.FONT_SIZE), 190 | text_anchor = 'end', 191 | **text_props 192 | )) 193 | # grid.add(self.text( 194 | # Quantity(1/z, 'Ʊ').render(), 195 | # insert = (X(x1) + to_pixels(self.TEXT_OFFSET), Y(v) + 0.35*self.FONT_SIZE), 196 | # text_anchor = 'start', 197 | # **text_props 198 | # )) 199 | if 'F' in self.AXES or 'f' in self.AXES: 200 | for v in range(x0, x1+1): 201 | grid.add(self.line(start=(X(v), Y(y0)), end=(X(v), Y(y1)), **attrs)) 202 | f = 10**v 203 | grid.add(self.text( 204 | Quantity(f, 'Hz').render(), 205 | insert = (X(v), Y(y0) + to_pixels(self.TEXT_OFFSET) + self.FONT_SIZE), 206 | text_anchor = 'middle', 207 | **text_props 208 | )) 209 | 210 | # draw CL log-log grids {{{3 211 | attrs['stroke'] = self.CL_GRID_COLOR 212 | if 'stroke_width' in attrs: 213 | del attrs['stroke_width'] 214 | 215 | # draw capacitance grid {{{4 216 | def c_start(C): 217 | # find lower right end point of capacitance gridline 218 | fstart = 1/(2*π*zmin*C) 219 | if fstart <= fmax: 220 | return (X(log(fstart)), Y(y0)) 221 | z = 1/(2*π*fmax*C) 222 | if z <= zmax: 223 | return (X(x1), Y(log(z))) 224 | 225 | def c_stop(C): 226 | # find upper left end point of capacitance gridline 227 | fstop = 1/(2*π*zmax*C) 228 | if fstop >= fmin: 229 | return (X(log(fstop)), Y(y1)) 230 | z = 1/(2*π*fmin*C) 231 | if z >= zmin: 232 | return (X(x0), Y(log(z))) 233 | 234 | # minor C divs {{{5 235 | if 'C' in self.AXES: 236 | attrs['stroke_width'] = to_pixels(self.MINOR_LINE_WIDTH) 237 | for v in range(x0, x1+y1-y0+1): 238 | for d in self.MINOR_DIVS: 239 | scale = int(d)/zmin 240 | C = scale*10**-(v+1) 241 | start = c_start(C) 242 | stop = c_stop(C) 243 | if start and stop: 244 | grid.add(self.line(start=start, end=stop, **attrs)) 245 | 246 | # major C divs and labels {{{5 247 | if 'C' in self.AXES or 'c' in self.AXES: 248 | attrs['stroke_width'] = to_pixels(self.MAJOR_LINE_WIDTH) 249 | for v in range(x0, x1+y1-y0): 250 | C = (10**-(v+1))/zmin 251 | start = c_start(C) 252 | stop = c_stop(C) 253 | if start and stop: 254 | grid.add(self.line(start=start, end=stop, **attrs)) 255 | x = stop[0] - self.FONT_SIZE 256 | y = stop[1] - 0.5*self.FONT_SIZE 257 | grid.add(self.text( 258 | Quantity(C, 'F').render(), 259 | insert = (x, y), 260 | text_anchor = 'end', 261 | transform = f'rotate(45, {x}, {y})', 262 | **text_props 263 | )) 264 | 265 | # draw inductance grid {{{4 266 | def l_start(L): 267 | # find lower right end point of inductance gridline 268 | fstart = zmin/(2*π*L) 269 | if fstart >= fmin: 270 | return (X(log(fstart)), Y(y0)) 271 | z = 2*π*fmin*L 272 | if z <= zmax: 273 | return (X(x0), Y(log(z))) 274 | 275 | def l_stop(L): 276 | # find upper left end point of inductance gridline 277 | fstop = zmax/(2*π*L) 278 | if fstop <= fmax: 279 | return (X(log(fstop)), Y(y1)) 280 | z = 2*π*fmax*L 281 | if z >= zmin: 282 | return (X(x1), Y(log(z))) 283 | 284 | # minor L divs {{{5 285 | if 'L' in self.AXES: 286 | attrs['stroke_width'] = to_pixels(self.MINOR_LINE_WIDTH) 287 | for v in range(x0-(y1-y0), x1+1): 288 | for d in self.MINOR_DIVS: 289 | scale = int(d) 290 | L = zmin*scale*10**-(v+1) 291 | start = l_start(L) 292 | stop = l_stop(L) 293 | if start and stop: 294 | grid.add(self.line(start=start, end=stop, **attrs)) 295 | 296 | # major L divs and labels {{{5 297 | if 'L' in self.AXES or 'l' in self.AXES: 298 | attrs['stroke_width'] = to_pixels(self.MAJOR_LINE_WIDTH) 299 | for v in range(x0-(y1-y0), x1): 300 | L = zmin*10**-(v+1) 301 | start = l_start(L) 302 | stop = l_stop(L) 303 | if start and stop: 304 | grid.add(self.line(start=start, end=stop, **attrs)) 305 | x = stop[0] + self.FONT_SIZE 306 | y = stop[1] - 0.5*self.FONT_SIZE 307 | grid.add(self.text( 308 | Quantity(L, 'H').render(), 309 | insert = (x, y), 310 | text_anchor = 'start', 311 | transform = f'rotate(-45, {x}, {y})', 312 | **text_props 313 | )) 314 | 315 | # outline {{{3 316 | attrs.update(dict( 317 | stroke_width = to_pixels(self.OUTLINE_LINE_WIDTH), 318 | stroke = self.OUTLINE_LINE_COLOR 319 | )) 320 | grid.add(self.line(start=(X(x0), Y(y0)), end=(X(x1), Y(y0)), **attrs)) 321 | grid.add(self.line(start=(X(x0), Y(y1)), end=(X(x1), Y(y1)), **attrs)) 322 | grid.add(self.line(start=(X(x0), Y(y0)), end=(X(x0), Y(y1)), **attrs)) 323 | grid.add(self.line(start=(X(x1), Y(y0)), end=(X(x1), Y(y1)), **attrs)) 324 | 325 | self.traces = self.g(id='traces') 326 | self.add(self.traces) 327 | 328 | # add_trace() {{{2 329 | def add_trace(self, frequencies, impedances, name=None, **svg_args): 330 | 331 | kwargs = dict( 332 | stroke = self.TRACE_COLOR, 333 | stroke_width = self.to_pixels(self.TRACE_WIDTH), 334 | stroke_linecap = 'round', 335 | fill = 'none', 336 | ) 337 | kwargs.update(svg_args) 338 | 339 | self.traces.add( 340 | self.polyline( 341 | [ 342 | (self.to_x(f), self.to_y(z)) 343 | for f, z in zip(frequencies, impedances) 344 | ], 345 | clip_path = 'url(#plotting-region)', 346 | **kwargs 347 | ) 348 | ) 349 | 350 | # add_line() {{{2 351 | def add_line(self, start, end, *, r=None, l=None, c=None, f=None, **svg_args): 352 | 353 | kwargs = dict( 354 | stroke = self.OUTLINE_LINE_COLOR, 355 | stroke_width = self.to_pixels(self.OUTLINE_LINE_WIDTH), 356 | stroke_linecap = 'round', 357 | fill = 'none', 358 | ) 359 | kwargs.update(svg_args) 360 | 361 | if r is not None: 362 | f_start, f_end = start, end 363 | z_start = z_end = r 364 | elif l is not None: 365 | f_start, f_end = start, end 366 | z_start = 2*π*start*l 367 | z_end = 2*π*end*l 368 | elif c is not None: 369 | f_start, f_end = start, end 370 | z_start = 1/(2*π*start*c) 371 | z_end = 1/(2*π*end*c) 372 | elif f is not None: 373 | z_start = start 374 | z_end = end 375 | f_start = f_end = f 376 | else: 377 | raise AssertionError('must specify either r, l, c, or f.') 378 | 379 | self.traces.add( 380 | self.line( 381 | start = (self.to_x(f_start), self.to_y(z_start)), 382 | end = (self.to_x(f_end), self.to_y(z_end)), 383 | **kwargs 384 | ) 385 | ) 386 | 387 | # close() {{{2 388 | def close(self): 389 | self.save(pretty=True) 390 | 391 | # context manager {{{2 392 | def __enter__(self): 393 | return self 394 | 395 | def __exit__(self, type, value, traceback): 396 | self.close() 397 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | RLC Chart 2 | ========= 3 | 4 | .. image:: https://pepy.tech/badge/rlc_chart/month 5 | :target: https://pepy.tech/project/rlc_chart 6 | 7 | .. image:: https://img.shields.io/pypi/v/rlc_chart.svg 8 | :target: https://pypi.python.org/pypi/rlc_chart 9 | 10 | .. image:: https://img.shields.io/pypi/pyversions/rlc_chart.svg 11 | :target: https://pypi.python.org/pypi/rlc_chart/ 12 | 13 | :Author: Ken Kundert 14 | :Version: 1.0.0 15 | :Released: 2022-01-25 16 | 17 | 18 | .. _what: 19 | 20 | What? 21 | ----- 22 | 23 | *rlc_chart* is library that renders impedance charts in SVG with the normal 24 | impedance versus frequency log-log grids, but they also include capacitance and 25 | inductance grids. They can be used to directly read component values from 26 | a plot of impedance. This is explained in `Introduction to Phasors 27 | `_. 28 | 29 | Consider the impedance of a leaky capacitor that has series resistance and 30 | inductance parasitics along with a shunt resistor as represented by the 31 | following circuit: 32 | 33 | .. image:: figures/leaky-cap-schematic.svg 34 | :width: 25% 35 | :align: center 36 | 37 | .. image:: figures/leaky-cap-chart.svg 38 | :width: 100% 39 | :align: center 40 | 41 | You can use the various grids on this graph to determine the values of the 42 | various components: C = 1 nF, L = 10 μH, Rs = 2 Ω, Rp = 500 kΩ, and f₀ = 1.6 43 | MHz. You can do this in other ways, but they involve manual calculation. In 44 | addition, an RLC chart is a convenient way of sharing or publishing your 45 | findings. 46 | 47 | Using an RLC chart is often enough to allow you to build a linear model for 48 | common two terminal components. 49 | 50 | 51 | .. _how: 52 | 53 | How? 54 | ---- 55 | 56 | Here is an example of how to use *rlc_chart*:: 57 | 58 | from rlc_chart import RLC_Chart 59 | from math import log10 as log, pi as π 60 | 61 | Rs = 2 62 | Rp = 500_000 63 | C = 1e-9 64 | L = 10e-6 65 | fmin = 1 66 | fmax = 1e8 67 | zmin = 1 68 | zmax = 1e6 69 | mult = 10**((log(fmax) - log(fmin))/400) 70 | f = fmin 71 | freq = [] 72 | impedance = [] 73 | j2π = 2j*π 74 | 75 | # Compute impedance of component 76 | # z1 = (Rs + 1/(jωC + jωL) Rs=2Ω, C=1nF, L=10μH 77 | # z2 = Rp Rp=500kΩ 78 | # z = z1 ‖ z2 79 | while(f <= 1.01*fmax): 80 | jω = j2π*f 81 | z1 = Rs + 1/(jω*C) + jω*L 82 | z2 = Rp 83 | z = z1 * z2 / (z1 + z2) 84 | freq += [f] 85 | impedance += [abs(z)] 86 | f *= mult 87 | 88 | with RLC_Chart('lcr-chart.svg', fmin, fmax, zmin, zmax) as chart: 89 | chart.add_trace(freq, impedance) 90 | 91 | Most of the code builds the two arrays that represent the trace. The impedance 92 | array is expected to contain positive real values. In this case it is the 93 | magnitude that is being plotted, though it is also common to call *add_trace* 94 | twice to show both the real and imaginary parts of the impedance. 95 | 96 | 97 | .. _rlc_chart: 98 | 99 | RLC_Chart 100 | --------- 101 | 102 | The *RLC_Chart* class constructor takes the following required arguments: 103 | 104 | filename: 105 | Path to the output SVG file. 106 | 107 | *fmin*: 108 | The minimum frequency value (left-most value on the chart). This value is 109 | always rounded down the next lower multiple of 10. So for example, if you 110 | give 25 Hz as *fmin*, then 10 Hz is used. 111 | 112 | *fmax*: 113 | The maximum frequency value (right-most value on the chart). This value is 114 | always rounded up the next higher multiple of 10. So for example, if you 115 | give 75 MHz as *fmax*, then 100 MHz is used. 116 | 117 | *zmin*: 118 | The minimum impedance value (bottom-most value on the chart). This value is 119 | always rounded down the next lower multiple of 10. So for example, if you 120 | give 150 mΩ *zmin*, then 100 mΩ is used. 121 | 122 | *zmax*: 123 | The maximum impedance value (top-most value on the chart). This value is 124 | always rounded up the next higher multiple of 10. So for example, if you 125 | give 800 kΩ as *zmax*, then 1 MΩ is used. 126 | 127 | In addition, the following keyword arguments are optional. 128 | 129 | *axes*: 130 | Specifies which axes are desired, where the choices are *f* for frequency, 131 | *z* for impedance, *c* for capacitance, and *l* for inductance. *axes* is 132 | a string that contains any or all of the four characters, or none at all. 133 | If the characters are lower case, then only the major grid lines are drawn, 134 | and if given as upper case letters, both the major and minor grid lines are 135 | drawn. The default is "FZRC". 136 | 137 | The visual clutter in the chart can be reduced by eliminating unneeded grid 138 | lines. 139 | 140 | *trace_width*: 141 | The width of a trace. The default is 0.025 inches. 142 | 143 | *trace_color*: 144 | The default color of the trace. You can use one of the named SVG colors, or 145 | you can use 'rgb(R,G,B)' where *R*, *G*, and *B* are integers between 0 and 146 | 255 that specify the intensity of red, blue, and green components of the 147 | color. 148 | 149 | *major_line_width*: 150 | The width of a major division line. The default is 0.01 inches. 151 | 152 | *minor_line_width*: 153 | The width of a minor division line. The default is 0.005 inches. 154 | 155 | *outline_line_width*: 156 | The width of grid outline. The default is 0.015 inches. 157 | 158 | *outline_line_color*: 159 | The color of the grid outline. The default is 'black'. 160 | 161 | *fz_grid_color*: 162 | The color of the frequency and impedance grid lines. The default is 'grey'. 163 | 164 | *cl_grid_color*: 165 | The color of the capacitance and inductance grid lines. The default is 166 | 'grey'. 167 | 168 | *background*: 169 | The background color of the grid. The default is 'white'. 170 | 171 | *minor_divs*: 172 | The minor divisions to include. The default is '123456789'. Other common 173 | values are '1', '13', '125', and '12468'. 174 | 175 | *decade*: 176 | The size of one decade square. The default is 1 inch. With this value, 177 | a grid that is 6 decades wide and 4 decades high is 6" by 4". 178 | 179 | *left_margin*: 180 | The size of the left margin. The default is 1 inch. 181 | 182 | *right_margin*: 183 | The size of the right margin. The default is 1 inch. 184 | 185 | *top_margin*: 186 | The size of the top margin. The default is 1 inch. 187 | 188 | *bottom_margin*: 189 | The size of the bottom margin. The default is 1 inch. 190 | 191 | *font_family*: 192 | The text font family. The default is "sans-serif". 193 | 194 | *font_size*: 195 | The text font size. The default is 12. 196 | 197 | *text_color*: 198 | The text color size. The default is "black". 199 | 200 | *text_offset*: 201 | The separation between the axis labels and the grid. The default is 0.15 202 | inches. 203 | 204 | *pixels_per_unit*: 205 | This is a scaling factor that allows you specify your dimensions in what 206 | ever units you wish. A value of 96, the default, means that you are 207 | specifying your units in inches. A value of 37.8 allows you specify values 208 | in centimeters. 209 | 210 | In addition, many SVG parameters can be passed into *RLC_Chart*, in which case 211 | they are simply passed on to `svgwrite `_. 212 | 213 | Generally, *RLC_Chart* is the argument of a *with* statement. If you choose not 214 | to do this, then you must explicitly call the *close* method yourself. Other 215 | than *close*, *RLC_Chart* provides several other methods, described next. 216 | 217 | Methods 218 | """"""" 219 | add_trace() 220 | ''''''''''' 221 | 222 | This method adds a trace to the graph. It may be called multiple times to add 223 | additional traces. There are two required arguments: 224 | 225 | *frequency*: 226 | An array of positive real values representing the frequency values of the 227 | points that when connected make up the trace. 228 | 229 | *impedance*: 230 | An array of positive real values representing the impedance values of the 231 | points that when connected make up the trace. 232 | 233 | Each of these arrays can be in the form of a *Python* list or a *numpy* array, 234 | and they must be the same length. 235 | 236 | It is also possible to specify additional keyword arguments, which are passed on 237 | to *svgwrite* and attached to the trace. This can be used to specify trace color 238 | and style. For example, specify *stroke* to specify the trace color. 239 | 240 | to_x() 241 | '''''' 242 | 243 | Given a frequency, *to_x* returns the corresponding canvas *X* coordinate. This 244 | can be used to add SVG features to your chart like labels. 245 | 246 | to_y() 247 | '''''' 248 | 249 | Given an impedance, *to_y* returns the corresponding canvas *Y* coordinate. 250 | This can be used to add SVG features to your chart like labels. 251 | 252 | add_line() 253 | '''''''''' 254 | 255 | Given a start and end value and a component value (*r*, *l*, *c*, or *f*), 256 | *add_line* draws a line on the chart. If you specify *r*, the start and end 257 | values are frequencies and the line is horizontal with the impedance being *r*. 258 | If you specify *f*, the start and end values are impedances and the line is 259 | vertical and the frequency is *f*. If you specify either *c* or *l* the start 260 | and end values are frequencies and the lines are diagonal and the impedance 261 | values are either 2π *f* *l* or 1/(2π *f* *c*). 262 | 263 | It is also possible to specify additional keyword arguments, which are passed on 264 | to *svgwrite* and attached to the line. This can be used to specify line color 265 | and style. For example, specify *stroke* to specify the line color. 266 | 267 | Attributes 268 | """""""""" 269 | 270 | HEIGHT 271 | '''''' 272 | 273 | The height of the canvas, which includes the height of the grid plus the top and 274 | bottom margins. Realize that in SVG drawings, the 0 *Y* value is at the top of 275 | the drawing. Thus *HEIGHT* when used as a *Y* coordinate represents the bottom 276 | of the canvas. 277 | 278 | WIDTH 279 | ''''' 280 | 281 | The width of the canvas, which includes the width of the grid plus the left and 282 | right margins. The 0 *X* value is at the left of the drawing and *WIDTH* when 283 | used as an *X* coordinate represents the right of the canvas. 284 | 285 | 286 | .. _labeling: 287 | 288 | Labeling 289 | -------- 290 | 291 | The chart object returned by *RLC_Chart* is a *svgwrite* *Drawing* object, and 292 | so you can call its methods to add SVG features to your chart. This can be used 293 | to add labels to your charts. Here is an example that demonstrates how to add 294 | labels and lines. It also demonstrates how to read impedance data from a CSV 295 | file:: 296 | 297 | from rlc_chart import RLC_Chart 298 | from inform import fatal, os_error 299 | from pathlib import Path 300 | from math import pi as π 301 | import csv 302 | 303 | fmin = 100 304 | fmax = 10e9 305 | zmin = 0.01 306 | zmax = 1e6 307 | cmod = 1e-9 308 | lmod = 700e-12 309 | rmod = 20e-3 310 | j2π = 2j*π 311 | 312 | def model(f): 313 | jω = j2π*f 314 | return 1/(jω*cmod) + rmod + jω*lmod 315 | 316 | frequency = [] 317 | z_data = [] 318 | r_data = [] 319 | z_model = [] 320 | r_model = [] 321 | try: 322 | contents = Path('C0603C102K3GACTU_imp_esr.csv').read_text() 323 | data = csv.DictReader(contents.splitlines(), delimiter=',') 324 | for row in data: 325 | f = float(row['Frequency']) 326 | z = model(f) 327 | frequency.append(f) 328 | z_data.append(float(row['Impedance'])) 329 | r_data.append(float(row['ESR'])) 330 | z_model.append(abs(z)) 331 | r_model.append(z.real) 332 | 333 | with RLC_Chart('C0603C102K3GACTU.svg', fmin, fmax, zmin, zmax) as chart: 334 | 335 | # add annotations 336 | svg_text_args = dict(font_size=22, fill='black') 337 | 338 | # capacitance annotations 339 | chart.add(chart.text( 340 | "C = 1 nF", 341 | insert = (chart.to_x(150e3), chart.to_y(1.5e3)), 342 | **svg_text_args 343 | )) 344 | chart.add_line(1e3, 190.23e6, c=1e-9) 345 | 346 | # inductance annotations 347 | chart.add(chart.text( 348 | "L = 700 pH", 349 | insert = (chart.to_x(6e9), chart.to_y(30)), 350 | text_anchor = 'end', 351 | **svg_text_args 352 | )) 353 | chart.add_line(190.232e6, 10e9, l=700e-12) 354 | 355 | # resistance annotations 356 | chart.add(chart.text( 357 | "ESR = 20 mΩ", 358 | insert = (chart.to_x(100e3), chart.to_y(25e-3)), 359 | text_anchor = 'start', 360 | **svg_text_args 361 | )) 362 | chart.add_line(100e3, 1e9, r=20e-3) 363 | 364 | # resonant frequency annotations 365 | chart.add(chart.text( 366 | "f₀ = 190 MHz", 367 | insert = (chart.to_x(190.23e6), chart.to_y(400)), 368 | text_anchor = 'middle', 369 | **svg_text_args 370 | )) 371 | chart.add_line(1e-2, 300, f=190.23e6) 372 | 373 | # Q annotations 374 | chart.add(chart.text( 375 | "Q = 42", 376 | insert = (chart.to_x(10e6), chart.to_y(100e-3)), 377 | text_anchor = 'start', 378 | **svg_text_args 379 | )) 380 | chart.add_line(10e6, 190.23e6, r=836.66e-3) 381 | 382 | # title 383 | chart.add(chart.text( 384 | "C0603C102K3GACTU 1nF Ceramic Capacitor", 385 | insert = (chart.WIDTH/2, 36), 386 | font_size = 24, 387 | fill = 'black', 388 | text_anchor = 'middle', 389 | )) 390 | 391 | # add traces last, so they are on top 392 | chart.add_trace(frequency, z_data, stroke='red') 393 | chart.add_trace(frequency, r_data, stroke='blue') 394 | chart.add_trace(frequency, z_model, stroke='red', stroke_dasharray=(10,5)) 395 | chart.add_trace(frequency, r_model, stroke='blue', stroke_dasharray=(10,5)) 396 | 397 | except OSError as e: 398 | fatal(os_error(e)) 399 | 400 | This example demonstrates two different ways to specify the location of the 401 | label. The *chart* object provides the *to_x* and *to_y* methods that convert 402 | data values into coordinates within the grid. This is used to add labels on the 403 | traces. The *chart* object also provides the *HEIGHT* and *WIDTH* attributes. 404 | These can be used to compute coordinates within the entire canvas. This is used 405 | to add a title that is near the top. 406 | 407 | The example also illustrates the use of *add_line* to add dimension lines to the 408 | chart. 409 | 410 | .. image:: figures/C0603C102K3GACTU.svg 411 | :width: 100% 412 | :align: center 413 | 414 | In this figure the solid traces are the data and the dashed traces are the 415 | model. The red traces are the magnitude of the impedance, and the blue traces 416 | are the real part of the impedance, or the ESR. 417 | 418 | Notice that in this chart the resistance at low frequencies drops with 1/*f*, 419 | just like the reactance. In this regard the data differs significantly from the 420 | model. This effect is referred to as dielectric absorption and it is both 421 | common and remarkable. You can read more about it, and how to model it, in 422 | `Modeling Dielectric Absorption in Capacitors 423 | `_. 424 | 425 | 426 | .. _examples: 427 | 428 | Examples 429 | -------- 430 | 431 | NumPy Arrays 432 | """""""""""" 433 | 434 | The first example, given above in how_, demonstrates how to generate an RLC 435 | chart by evaluating formulas in Python. Here the example is repeated 436 | reformulated to use NumPy arrays:: 437 | 438 | from rlc_chart import RLC_Chart 439 | from inform import fatal, os_error 440 | from numpy import logspace, log10 as log, pi as π 441 | 442 | Rs = 2 443 | Rp = 500e3 444 | C = 1e-9 445 | L = 10e-6 446 | fmin = 1 447 | fmax = 100e6 448 | zmin = 1 449 | zmax = 1e6 450 | filename = "leaky-cap-chart.svg" 451 | j2π = 2j*π 452 | 453 | f = logspace(log(fmin), log(fmax), 2000, endpoint=True) 454 | jω = j2π*f 455 | z1 = Rs + 1/(jω*C) + jω*L 456 | z2 = Rp 457 | z = z1 * z2 / (z1 + z2) 458 | 459 | try: 460 | with RLC_Chart(filename, fmin, fmax, zmin, zmax) as chart: 461 | chart.add_trace(f, abs(z.real), stroke='blue') 462 | chart.add_trace(f, abs(z.imag), stroke='red') 463 | chart.add_trace(f, abs(z)) 464 | except OSError as e: 465 | fatal(os_error(e)) 466 | 467 | 468 | CSV Data 469 | """""""" 470 | 471 | The example given in labeling_ demonstrates how to read impedance data from 472 | a CSV (comma separated values) file and use it to create an RLC chart. 473 | It is rather long, and so is not repeated here. 474 | 475 | 476 | Plotting Spectre Data 477 | """"""""""""""""""""" 478 | 479 | If you use the *Spectre* circuit simulator, you can use *psf_utils* with 480 | *rlc_chart* to extract models from simulation results. For example, here is the 481 | model of an inductor given by its manufacturer:: 482 | 483 | subckt MCFE1412TR47_JB (1 2) 484 | R1 (1 7) resistor r=0.036 485 | L5 (2 8) inductor l=20u 486 | C2 (7 8) capacitor c=10.6p 487 | R2 (8 2) resistor r=528 488 | C1 (7 9) capacitor c=28.5p 489 | R5 (9 2) resistor r=3.7 490 | L0 (7 3) inductor l=0.27u 491 | L1 (3 4) inductor l=0.07u 492 | L2 (4 2) inductor l=0.11u 493 | L3 (3 5) inductor l=0.39u 494 | L4 (4 6) inductor l=0.35u 495 | R3 (5 4) resistor r=3.02158381422266 496 | R4 (6 2) resistor r=43.4532529473926 497 | ends MCFE1412TR47_JB 498 | 499 | This model is overly complicated and so expensive to simulate. It requires 13 500 | extra unknowns that the simulator must compute (7 internal nodes and 6 inductor 501 | currents). The impedance of this subcircuit is extracted by grounding one end 502 | and driving the other with a 1 A magnitude AC source. Spectre is then run on 503 | the circuit to generate a ASCII PSF file. Then, the RLC chart for this 504 | subcircuit can be generated with:: 505 | 506 | from psf_utils import PSF 507 | from inform import Error, os_error, fatal 508 | from rlc_chart import RLC_Chart 509 | 510 | try: 511 | psf = PSF('MCFE1412TR47_JB.ac') 512 | sweep = psf.get_sweep() 513 | z_ckt = psf.get_signal('1') 514 | z_mod = psf.get_signal('2') 515 | 516 | with RLC_Chart('MCFE1412TR47_JB.svg', 100, 1e9, 0.01, 1000) as chart: 517 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate), stroke='red') 518 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate), stroke='blue') 519 | 520 | with RLC_Chart('MCFE1412TR47_JB.rxz.svg', 100, 1e9, 0.01, 1000) as chart: 521 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate.real), stroke='green') 522 | chart.add_trace(sweep.abscissa, abs(z_ckt.ordinate.imag), stroke='orange') 523 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate.real), stroke='blue') 524 | chart.add_trace(sweep.abscissa, abs(z_mod.ordinate.imag), stroke='red') 525 | 526 | except Error as e: 527 | e.terminate() 528 | except OSError as e: 529 | fatal(os_error(e)) 530 | 531 | The RLC chart shows that the above subcircuit can be replaced with:: 532 | 533 | subckt MCFE1412TR47_JB (1 2) 534 | L (1 2) inductor l=442.24nH r=36mOhm 535 | C (1 2) capacitor c=27.522pF 536 | R (1 2) resistor r=537.46_Ohm 537 | ends MCFE1412TR47_JB 538 | 539 | This version only requires one additional unknown, the inductor current, and so 540 | is considerably more efficient. 541 | 542 | Here is the RLC chart of both showing the difference, which are inconsequential. 543 | 544 | .. image:: figures/MCFE1412TR47_JB.svg 545 | :width: 100% 546 | :align: center 547 | 548 | The differences are a bit more apparent if the real and imaginary components of 549 | the impedance are plotted separately. 550 | 551 | .. image:: figures/MCFE1412TR47_JB.rxz.svg 552 | :width: 100% 553 | :align: center 554 | 555 | The differences are significant only in the loss exhibited above resonance, 556 | which is usually not of concern. 557 | 558 | 559 | Plotting S-Parameter Data 560 | """"""""""""""""""""""""" 561 | 562 | You may find that the data on a two-terminal component is given as a two-port 563 | S-parameter data file. The following example shows how to read a TouchStone 564 | two-port S-parameter data file, convert the S-parameters into Z-parameters, and 565 | then plot Z12 on an RLC chart:: 566 | 567 | #!/usr/bin/env python3 568 | # Convert S-parameters of inductor, measured as a two port, to impedance 569 | 570 | from inform import fatal, os_error 571 | from rlc_chart import RLC_Chart 572 | from cmath import rect, pi as π 573 | from pathlib import Path 574 | 575 | y11 = [] 576 | y12 = [] 577 | y21 = [] 578 | y22 = [] 579 | Zind = [] 580 | freq = [] 581 | z0 = 50 582 | 583 | try: 584 | data = Path('tfm201610alm_r47mtaa.s2p').read_text() 585 | lines = data.splitlines() 586 | for line in lines: 587 | line = line.strip() 588 | if line[0] in '!#': 589 | continue 590 | f, s11m, s11p, s12m, s12p, s21m, s21p, s22m, s22p = line.split() 591 | s11 = rect(float(s11m), π*float(s11p)/180) 592 | s12 = rect(float(s12m), π*float(s12p)/180) 593 | s21 = rect(float(s21m), π*float(s21p)/180) 594 | s22 = rect(float(s22m), π*float(s22p)/180) 595 | Δ = (1 + s11)*(1 + s22) - s12*s21 596 | y11 = ((1 - s11)*(1 + s22) + s12*s21) / Δ / z0 597 | y12 = -2*s12 / Δ / z0 598 | y21 = -2*s21 / Δ / z0 599 | y22 = ((1 + s11)*(1 - s22) + s12*s21) / Δ / z0 600 | f = float(f) 601 | if f: 602 | freq.append(f) 603 | Zind.append(abs(1/y12)) 604 | 605 | with RLC_Chart('tfm201610alm.svg', 100e3, 1e9, 0.1, 1000) as chart: 606 | chart.add_trace(freq, Zind) 607 | 608 | except OSError as e: 609 | fatal(os_error(e)) 610 | 611 | Here is the resulting RLC chart for a 470 nH inductor where the S-parameters 612 | were downloaded from the TDK website. 613 | 614 | .. image:: figures/tfm201610alm.svg 615 | :width: 100% 616 | :align: center 617 | 618 | 619 | .. _installing: 620 | 621 | Installing 622 | ---------- 623 | 624 | To install use:: 625 | 626 | pip install --user rlc_chart 627 | 628 | 629 | .. _releases: 630 | 631 | Releases 632 | -------- 633 | 634 | Latest development release 635 | """""""""""""""""""""""""" 636 | 637 | | Version: 1.0.0 638 | | Released: 2022-01-25 639 | 640 | 1.0 (2022-01-25) 641 | """""""""""""""" 642 | 643 | - Promote to full release. 644 | - Cosmetic changes to README. 645 | - Added test script (figures/run_tests). 646 | 647 | 648 | 0.1 (2021-03-25) 649 | """""""""""""""" 650 | 651 | - Initial release. 652 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /figures/tfm201610alm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 100 mΩ 76 | 77 | 1 Ω 78 | 79 | 10 Ω 80 | 81 | 100 Ω 82 | 83 | 1 kΩ 84 | 85 | 100 kHz 86 | 87 | 1 MHz 88 | 89 | 10 MHz 90 | 91 | 100 MHz 92 | 93 | 1 GHz 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 10 µF 168 | 169 | 1 µF 170 | 171 | 100 nF 172 | 173 | 10 nF 174 | 175 | 1 nF 176 | 177 | 100 pF 178 | 179 | 10 pF 180 | 181 | 1 pF 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 1 mH 256 | 257 | 100 µH 258 | 259 | 10 µH 260 | 261 | 1 µH 262 | 263 | 100 nH 264 | 265 | 10 nH 266 | 267 | 1 nH 268 | 269 | 100 pH 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | --------------------------------------------------------------------------------