├── .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 |
62 |
--------------------------------------------------------------------------------
/figures/Golden/leaky-cap-schematic.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
279 |
--------------------------------------------------------------------------------