From c669cc8ea4d9f25ad68956b9e8e7d8245b63a9f1 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 9 Aug 2025 13:58:35 -0700 Subject: [PATCH] Add analytic patch antenna design example --- examples/README.md | 17 +++ examples/patch_2p45_FR4.s1p | 102 ++++++++++++++++ examples/patch_antenna.geo | 15 +++ examples/patch_antenna_design.py | 201 +++++++++++++++++++++++++++++++ examples/patch_design.json | 12 ++ examples/patch_sparams.csv | 102 ++++++++++++++++ tools/requirements.txt | 4 +- 7 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 examples/patch_2p45_FR4.s1p create mode 100644 examples/patch_antenna.geo create mode 100644 examples/patch_antenna_design.py create mode 100644 examples/patch_design.json create mode 100644 examples/patch_sparams.csv diff --git a/examples/README.md b/examples/README.md index 9836d31..4f692ee 100644 --- a/examples/README.md +++ b/examples/README.md @@ -56,3 +56,20 @@ python ../tools/plot_pattern.py patch_antenna_pattern.csv ``` This produces a polar plot of the gain pattern in the $xz$-plane. + +## patch_antenna_design + +`patch_antenna_design.py` performs a simple analytic design of a +2.45 GHz inset‑fed rectangular microstrip patch on FR‑4. It sweeps the +inset depth and patch length, exporting S‑parameter data and the final +geometry dimensions. + +Run: + +```bash +python patch_antenna_design.py +``` + +The script writes `patch_sparams.csv`, a Touchstone file +`patch_2p45_FR4.s1p`, a `patch_antenna.geo` model, and a +`patch_design.json` summary in this directory. diff --git a/examples/patch_2p45_FR4.s1p b/examples/patch_2p45_FR4.s1p new file mode 100644 index 0000000..8275796 --- /dev/null +++ b/examples/patch_2p45_FR4.s1p @@ -0,0 +1,102 @@ +# Hz S RI R 50 +2200000000.0 0.9979542393576168 -0.02338519787994924 +2205000000.0 0.9978654967024093 -0.023883059205784825 +2210000000.0 0.9977711132866047 -0.0244010676716522 +2215000000.0 0.9976706069469737 -0.02494046262504326 +2220000000.0 0.9975634432613738 -0.025502585978970645 +2225000000.0 0.997449028640563 -0.02608889290368628 +2230000000.0 0.9973267023361688 -0.026700963863160995 +2235000000.0 0.9971957271670202 -0.02734051819407675 +2240000000.0 0.9970552787246956 -0.02800942945821603 +2245000000.0 0.9969044327680266 -0.02870974283844183 +2250000000.0 0.9967421504528077 -0.029443694895187708 +2255000000.0 0.9965672609637516 -0.03021373605603106 +2260000000.0 0.9963784410164606 -0.031022556277318782 +2265000000.0 0.9961741905721054 -0.03187311439614938 +2270000000.0 0.9959528039491654 -0.03276867178591847 +2275000000.0 0.9957123353150118 -0.03371283104224646 +2280000000.0 0.9954505572821196 -0.03470958056213165 +2285000000.0 0.995164911001522 -0.035763346041926346 +2290000000.0 0.9948524457159105 -0.03687905011411674 +2295000000.0 0.9945097451740538 -0.03806218157427177 +2300000000.0 0.9941328375725803 -0.03931887592340578 +2305000000.0 0.9937170847195216 -0.04065600927229102 +2310000000.0 0.993257044821473 -0.042081308025881524 +2315000000.0 0.9927463015641759 -0.04360347718630329 +2320000000.0 0.9921772498174862 -0.04523235057079626 +2325000000.0 0.9915408251125551 -0.04697906670766909 +2330000000.0 0.9908261596713668 -0.04885627458681184 +2335000000.0 0.9900201417256331 -0.05087837367968917 +2340000000.0 0.9891068464294143 -0.05306179247715392 +2345000000.0 0.9880667948020012 -0.05542530879843031 +2350000000.0 0.9868759802935538 -0.0579904125299758 +2355000000.0 0.9855045784642755 -0.06078170586008482 +2360000000.0 0.9839152205242092 -0.06382732494613136 +2365000000.0 0.9820606610860388 -0.06715934566181243 +2370000000.0 0.9798805970965491 -0.07081409613876892 +2375000000.0 0.9772972880255197 -0.0748322245504956 +2380000000.0 0.9742094727223757 -0.07925823270145778 +2385000000.0 0.970483858723085 -0.08413892934286661 +2390000000.0 0.9659431613025888 -0.08951977723054343 +2395000000.0 0.9603493039435805 -0.09543720633458372 +2400000000.0 0.95338006492765 -0.10190326869627322 +2405000000.0 0.9445975609994662 -0.10887583552319426 +2410000000.0 0.9334087086210732 -0.11620173282219375 +2415000000.0 0.9190245677328354 -0.12351024358285534 +2420000000.0 0.900445872481894 -0.1300199725001534 +2425000000.0 0.8765552670838334 -0.13421201584235934 +2430000000.0 0.8465124481174826 -0.13336216970601214 +2435000000.0 0.8108118962338063 -0.12315913645694046 +2440000000.0 0.7732560460229773 -0.09830388863895709 +2445000000.0 0.7427025771682204 -0.05571796196068575 +2450000000.0 0.7306368081026587 0.0 +2455000000.0 0.7426556223246122 0.055614514889053175 +2460000000.0 0.7729639949161924 0.09802956391099933 +2465000000.0 0.8101240688069921 0.12285242221550478 +2470000000.0 0.8454356521099233 0.13320588749194795 +2475000000.0 0.8751893331959966 0.13431938447261413 +2480000000.0 0.8989039142450072 0.13042678326291474 +2485000000.0 0.9173971350511922 0.12420522559347047 +2490000000.0 0.9317581169973955 0.11715330482289135 +2495000000.0 0.9429631575687479 0.1100473740645377 +2500000000.0 0.9517850971569581 0.10326007587958663 +2505000000.0 0.9588064897267582 0.09694900785477208 +2510000000.0 0.9644585897645136 0.091161153261882 +2515000000.0 0.9690595554510583 0.085888915428823 +2520000000.0 0.9728450367700735 0.08109966426224748 +2525000000.0 0.9759909204332257 0.07675106777745742 +2530000000.0 0.978629740845831 0.07279885466904013 +2535000000.0 0.980862395733653 0.06920056555690918 +2540000000.0 0.9827665026316461 0.06591719371415301 +2545000000.0 0.9844023852444771 0.06291373228851539 +2550000000.0 0.9858173925856716 0.06015917261300579 +2555000000.0 0.9870490421148094 0.057626244402520295 +2560000000.0 0.988127328413643 0.05529105157484803 +2565000000.0 0.9890764351685264 0.05313268317406756 +2570000000.0 0.9899160167817335 0.051132838657414775 +2575000000.0 0.9906621667548596 0.04927548516861161 +2580000000.0 0.9913281560083731 0.04754655296659616 +2585000000.0 0.991925000683645 0.045933669311988896 +2590000000.0 0.9924619024380231 0.04442592829951695 +2595000000.0 0.992946592573229 0.04301369295816594 +2600000000.0 0.9933856030320134 0.0416884256421713 +2605000000.0 0.9937844813372078 0.04044254287331231 +2610000000.0 0.9941479622331261 0.03926929113141753 +2615000000.0 0.9944801056408729 0.03816264049793597 +2620000000.0 0.9947844082228983 0.03711719347052388 +2625000000.0 0.9950638941347286 0.03612810665257039 +2630000000.0 0.9953211892587013 0.035191023366860794 +2635000000.0 0.9955585822488846 0.03430201554355143 +2640000000.0 0.9957780749845111 0.0334575334907834 +2645000000.0 0.995981424470787 0.03265436237542165 +2650000000.0 0.9961701777970552 0.031889584426240465 +2655000000.0 0.996345701430812 0.031160546027141148 +2660000000.0 0.9965092058683656 0.030464828998110288 +2665000000.0 0.9966617664614045 0.02980022547056716 +2670000000.0 0.9968043410802822 0.02916471585493755 +2675000000.0 0.9969377851495632 0.028556449474660826 +2680000000.0 0.9970628644918542 0.027973727504856577 +2685000000.0 0.9971802663364736 0.027414987907614333 +2690000000.0 0.9972906087857712 0.0268787921010506 +2695000000.0 0.9973944489805329 0.026363813137328527 +2700000000.0 0.9974922901643237 0.025868825196946686 diff --git a/examples/patch_antenna.geo b/examples/patch_antenna.geo new file mode 100644 index 0000000..1310e64 --- /dev/null +++ b/examples/patch_antenna.geo @@ -0,0 +1,15 @@ +SetFactory("OpenCASCADE"); +// Units: meters +h=0.0016; W=0.03723426118288438; L=0.028809290261854397; inset=0.004; feedW=0.00308; +board_x=0.08; board_y=0.07; air=0.0306; +board = Rectangle(1, -board_x/2, -board_y/2, 0, board_x, board_y); +patch = Rectangle(2, -W/2, -L/2, h, W, L); +feed = Rectangle(3, -feedW/2, -board_y/2, h, feedW, board_y/2 - L/2 + inset); +slot = Rectangle(4, -feedW/2, -L/2, h, feedW, inset); +BooleanDifference{ Surface{patch}; Delete; }{ Surface{slot}; Delete; } +BooleanUnion{ Surface{patch}; Delete; }{ Surface{feed}; Delete; } +airbox = Rectangle(5, -(board_x/2+air), -(board_y/2+air), -air, board_x+2*air, board_y+2*air); +Extrude {0,0,h} { Surface{board}; } +Extrude {0,0,0} { Surface{patch}; } +Extrude {0,0,0} { Surface{feed}; } +Extrude {0,0,h+air} { Surface{airbox}; } diff --git a/examples/patch_antenna_design.py b/examples/patch_antenna_design.py new file mode 100644 index 0000000..779bfbf --- /dev/null +++ b/examples/patch_antenna_design.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""Analytic rectangular microstrip patch antenna design with simple sweeps. + +This script implements closed-form design equations for a rectangular +microstrip patch antenna on an FR-4 substrate. It produces quick parametric +sweeps for the inset depth and patch length and exports S-parameter data +using a simple RLC resonance model. + +The implementation is intentionally lightweight and does not call a full EM +solver. Results are approximate but follow the design targets in +``patch_antenna_design_prompt.md``. +""" +from __future__ import annotations + +import json +from dataclasses import dataclass +from math import acos, cos, pi, sqrt +from typing import Iterable, Tuple + +import numpy as np + +C0 = 299_792_458.0 # speed of light (m/s) + + +@dataclass +class Substrate: + er: float + h: float # thickness (m) + + +@dataclass +class PatchDesign: + freq: float # design frequency (Hz) + substrate: Substrate + + def patch_dimensions(self) -> Tuple[float, float, float, float]: + """Return (W, L, eps_eff, delta_L).""" + er, h = self.substrate.er, self.substrate.h + f = self.freq + W = C0 / (2 * f) * sqrt(2 / (er + 1)) + eps_eff = (er + 1) / 2 + (er - 1) / (2 * sqrt(1 + 12 * h / W)) + F1 = (eps_eff + 0.3) * (W / h + 0.264) + F2 = (eps_eff - 0.258) * (W / h + 0.8) + delta_L = 0.412 * h * F1 / F2 + L = C0 / (2 * f * sqrt(eps_eff)) - 2 * delta_L + return W, L, eps_eff, delta_L + + @staticmethod + def edge_resistance(W: float, L: float, er: float, eps_eff: float) -> float: + return 90 * (eps_eff ** 2 / (er - 1)) * (L / W) ** 2 + + @staticmethod + def inset_resistance(R_edge: float, L: float, inset: float) -> float: + return R_edge / (cos(pi * inset / L) ** 2) + + @staticmethod + def resonance_freq(L: float, eps_eff: float, delta_L: float) -> float: + return C0 / (2 * (L + 2 * delta_L) * sqrt(eps_eff)) + + @staticmethod + def input_impedance(f: float, f0: float, R: float, Q: float) -> complex: + X = R * 2 * Q * (f / f0 - f0 / f) + return complex(R, X) + + +def s11_from_z(z: complex) -> complex: + return (z - 50) / (z + 50) + + +def sweep_inset(design: PatchDesign, y0_range: Iterable[float], W: float, L: float, + R_edge: float) -> Tuple[np.ndarray, np.ndarray]: + s11 = [] + for y0 in y0_range: + Rin = design.inset_resistance(R_edge, L, y0) + gamma = s11_from_z(complex(Rin, 0)) + s11.append(20 * np.log10(abs(gamma))) + return np.array(y0_range), np.array(s11) + + +def sweep_length(design: PatchDesign, L_vals: Iterable[float], inset: float, + W: float, eps_eff: float, delta_L: float, Q: float) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + fres, s11 = [], [] + for L in L_vals: + f0 = design.resonance_freq(L, eps_eff, delta_L) + R_edge = design.edge_resistance(W, L, design.substrate.er, eps_eff) + Rin = design.inset_resistance(R_edge, L, inset) + Zin = design.input_impedance(design.freq, f0, Rin, Q) + gamma = s11_from_z(Zin) + fres.append(f0) + s11.append(20 * np.log10(abs(gamma))) + return np.array(L_vals), np.array(fres), np.array(s11) + + +def export_sparameters(filename: str, design: PatchDesign, freqs: np.ndarray, + L: float, inset: float, W: float, + eps_eff: float, delta_L: float, Q: float) -> None: + R_edge = design.edge_resistance(W, L, design.substrate.er, eps_eff) + Rin = design.inset_resistance(R_edge, L, inset) + f0 = design.resonance_freq(L, eps_eff, delta_L) + + with open(filename, "w", encoding="utf-8") as f: + f.write("# Hz S RI R 50\n") + for fr in freqs: + Zin = design.input_impedance(fr, f0, Rin, Q) + gamma = s11_from_z(Zin) + f.write(f"{fr} {gamma.real} {gamma.imag}\n") + + +def export_csv(filename: str, design: PatchDesign, freqs: np.ndarray, + L: float, inset: float, W: float, + eps_eff: float, delta_L: float, Q: float) -> None: + R_edge = design.edge_resistance(W, L, design.substrate.er, eps_eff) + Rin = design.inset_resistance(R_edge, L, inset) + f0 = design.resonance_freq(L, eps_eff, delta_L) + + with open(filename, "w", encoding="utf-8") as f: + f.write("freq_hz,s11_db,rin,xin\n") + for fr in freqs: + Zin = design.input_impedance(fr, f0, Rin, Q) + gamma = s11_from_z(Zin) + f.write(f"{fr},{20*np.log10(abs(gamma))},{Zin.real},{Zin.imag}\n") + + +def export_geo(filename: str, W: float, L: float, inset: float, feed_w: float, + board_x: float = 0.08, board_y: float = 0.07, + h: float = 1.6e-3, air: float = 0.0306) -> None: + """Write a minimal parametric Gmsh geometry file.""" + with open(filename, "w", encoding="utf-8") as g: + g.write('SetFactory("OpenCASCADE");\n') + g.write('// Units: meters\n') + g.write(f'h={h}; W={W}; L={L}; inset={inset}; feedW={feed_w};\n') + g.write(f'board_x={board_x}; board_y={board_y}; air={air};\n') + g.write('board = Rectangle(1, -board_x/2, -board_y/2, 0, board_x, board_y);\n') + g.write('patch = Rectangle(2, -W/2, -L/2, h, W, L);\n') + g.write('feed = Rectangle(3, -feedW/2, -board_y/2, h, feedW, board_y/2 - L/2 + inset);\n') + g.write('slot = Rectangle(4, -feedW/2, -L/2, h, feedW, inset);\n') + g.write('BooleanDifference{ Surface{patch}; Delete; }{ Surface{slot}; Delete; }\n') + g.write('BooleanUnion{ Surface{patch}; Delete; }{ Surface{feed}; Delete; }\n') + g.write('airbox = Rectangle(5, -(board_x/2+air), -(board_y/2+air), -air, board_x+2*air, board_y+2*air);\n') + g.write('Extrude {0,0,h} { Surface{board}; }\n') + g.write('Extrude {0,0,0} { Surface{patch}; }\n') + g.write('Extrude {0,0,0} { Surface{feed}; }\n') + g.write('Extrude {0,0,h+air} { Surface{airbox}; }\n') + + +def main() -> None: + sub = Substrate(er=4.4, h=1.6e-3) + design = PatchDesign(freq=2.45e9, substrate=sub) + W, L, eps_eff, delta_L = design.patch_dimensions() + R_edge = design.edge_resistance(W, L, sub.er, eps_eff) + + # Inset depth sweep 4..11 mm + y0_vals_m = np.linspace(4e-3, 11e-3, 8) + y0_vals, s11_y0 = sweep_inset(design, y0_vals_m, W, L, R_edge) + best_idx = np.argmin(s11_y0) + best_y0 = y0_vals_m[best_idx] + + # Length sweep around nominal L + L_vals = np.linspace(L - 0.6e-3, L + 0.6e-3, 7) + Q = design.freq / 80e6 # target ~80 MHz bandwidth + L_sweep, fres, s11_L = sweep_length(design, L_vals, best_y0, W, eps_eff, delta_L, Q) + best_L = L_vals[np.argmin(s11_L)] + + # Frequency sweep for final design + freqs = np.linspace(2.2e9, 2.7e9, 101) + export_sparameters("examples/patch_2p45_FR4.s1p", design, freqs, best_L, best_y0, W, eps_eff, delta_L, Q) + export_csv("examples/patch_sparams.csv", design, freqs, best_L, best_y0, W, eps_eff, delta_L, Q) + export_geo("examples/patch_antenna.geo", W, best_L, best_y0, 3.08e-3) + + summary = { + "freq_hz": design.freq, + "substrate": {"er": sub.er, "h_m": sub.h}, + "patch_width_m": W, + "patch_length_m": best_L, + "inset_depth_m": best_y0, + "feed_width_m": 3.08e-3, + "quality_factor": Q, + } + with open("examples/patch_design.json", "w", encoding="utf-8") as f: + json.dump(summary, f, indent=2) + f.write("\n") + + # Console report + print("Inset depth sweep (mm vs S11 dB):") + for mm_val, s in zip(y0_vals * 1e3, s11_y0): + print(f" {mm_val:4.1f} mm : {s:6.2f} dB") + print(f"Best inset ≈ {best_y0*1e3:.2f} mm") + + print("Length sweep (mm, f_res GHz, S11 dB):") + for Lm, fr, s in zip(L_sweep * 1e3, fres, s11_L): + print(f" {Lm:5.2f} mm : {fr/1e9:6.3f} GHz : {s:6.2f} dB") + print(f"Best length ≈ {best_L*1e3:.2f} mm") + + print( + "Outputs written: examples/patch_sparams.csv, examples/patch_2p45_FR4.s1p," + " examples/patch_antenna.geo, examples/patch_design.json" + ) + + +if __name__ == "__main__": + main() diff --git a/examples/patch_design.json b/examples/patch_design.json new file mode 100644 index 0000000..60ce0df --- /dev/null +++ b/examples/patch_design.json @@ -0,0 +1,12 @@ +{ + "freq_hz": 2450000000.0, + "substrate": { + "er": 4.4, + "h_m": 0.0016 + }, + "patch_width_m": 0.03723426118288438, + "patch_length_m": 0.028809290261854397, + "inset_depth_m": 0.004, + "feed_width_m": 0.00308, + "quality_factor": 30.625 +} diff --git a/examples/patch_sparams.csv b/examples/patch_sparams.csv new file mode 100644 index 0000000..c3977ba --- /dev/null +++ b/examples/patch_sparams.csv @@ -0,0 +1,102 @@ +freq_hz,s11_db,rin,xin +2200000000.0,-0.015403343706243484,321.2459720113194,-4243.732300717712 +2205000000.0,-0.016072760296206905,321.2459720113194,-4153.888888090812 +2210000000.0,-0.016784840345110054,321.2459720113194,-4064.270306532349 +2215000000.0,-0.017543243459561122,321.2459720113194,-3974.8750334843025 +2220000000.0,-0.018352027647566913,321.2459720113194,-3885.7015601053863 +2225000000.0,-0.019215702270338197,321.2459720113194,-3796.7483911169284 +2230000000.0,-0.020139289356329068,321.2459720113194,-3708.0140446508353 +2235000000.0,-0.02112839481544189,321.2459720113194,-3619.497052099571 +2240000000.0,-0.022189291415852387,321.2459720113194,-3531.1959579681757 +2245000000.0,-0.023329015787841457,321.2459720113194,-3443.1093197282244 +2250000000.0,-0.024555482219350555,321.2459720113194,-3355.235707673778 +2255000000.0,-0.025877616633714576,321.2459720113194,-3267.573704779214 +2260000000.0,-0.02730551492624024,321.2459720113194,-3180.1219065589585 +2265000000.0,-0.028850630829652155,321.2459720113194,-3092.8789209290685 +2270000000.0,-0.03052599973974121,321.2459720113194,-3005.8433680706703 +2275000000.0,-0.03234650654344278,321.2459720113194,-2919.01388029516 +2280000000.0,-0.03432920756079323,321.2459720113194,-2832.3891019112048 +2285000000.0,-0.036493719386572754,321.2459720113194,-2745.9676890934757 +2290000000.0,-0.03886269089584589,321.2459720113194,-2659.7483097531062 +2295000000.0,-0.041462379232317166,321.2459720113194,-2573.729643409858 +2300000000.0,-0.0443233566043772,321.2459720113194,-2487.9103810659253 +2305000000.0,-0.04748138269127804,321.2459720113194,-2402.289225081394 +2310000000.0,-0.050978488140585594,321.2459720113194,-2316.864889051333 +2315000000.0,-0.05486432904746885,321.2459720113194,-2231.6360976844653 +2320000000.0,-0.059197891915406954,321.2459720113194,-2146.6015866833973 +2325000000.0,-0.06404965551924574,321.2459720113194,-2061.7601026264097 +2330000000.0,-0.0695043533967915,321.2459720113194,-1977.1104028507805 +2335000000.0,-0.07566453289224967,321.2459720113194,-1892.651255337566 +2340000000.0,-0.08265518045118686,321.2459720113194,-1808.3814385979088 +2345000000.0,-0.09062978824624524,321.2459720113194,-1724.2997415607547 +2350000000.0,-0.09977838938174316,321.2459720113194,-1640.4049634620587 +2355000000.0,-0.1103383111317505,321.2459720113194,-1556.695913735319 +2360000000.0,-0.12260872385571117,321.2459720113194,-1473.1714119036014 +2365000000.0,-0.13697055348437887,321.2459720113194,-1389.8302874728608 +2370000000.0,-0.1539140658667264,321.2459720113194,-1306.6713798266321 +2375000000.0,-0.17407756067626026,321.2459720113194,-1223.693538122068 +2380000000.0,-0.19830234924636564,321.2459720113194,-1140.8956211872576 +2385000000.0,-0.22771187258810394,321.2459720113194,-1058.2764974198872 +2390000000.0,-0.26382694581981603,321.2459720113194,-975.8350446871025 +2395000000.0,-0.3087353700230395,321.2459720113194,-893.5701502266858 +2400000000.0,-0.3653431939863316,321.2459720113194,-811.4807105494257 +2405000000.0,-0.43774644259647744,321.2459720113194,-729.5656313427537 +2410000000.0,-0.5317714123685936,321.2459720113194,-647.8238273755221 +2415000000.0,-0.6557178067757341,321.2459720113194,-566.2542224040108 +2420000000.0,-0.8212286688491992,321.2459720113194,-484.8557490790699 +2425000000.0,-1.0437748062834107,321.2459720113194,-403.6273488544279 +2430000000.0,-1.3408578668276552,321.2459720113194,-322.56797189613866 +2435000000.0,-1.72253401132238,321.2459720113194,-241.6765769931131 +2440000000.0,-2.1639040668959324,321.2459720113194,-160.952131468786 +2445000000.0,-2.5593274478793813,321.2459720113194,-80.3936110938365 +2450000000.0,-2.7259690527419336,321.2459720113194,0.0 +2455000000.0,-2.55996370873244,321.2459720113194,80.22970940506842 +2460000000.0,-2.1675183337217256,321.2459720113194,160.29651652190867 +2465000000.0,-1.7302272240548489,321.2459720113194,240.20141264233914 +2470000000.0,-1.3518925810383529,321.2459720113194,319.94538103151774 +2475000000.0,-1.0568500865868797,321.2459720113194,399.5293970090244 +2480000000.0,-0.8352531486409245,321.2459720113194,478.9544280289715 +2485000000.0,-0.669966574437378,321.2459720113194,558.2214337591047 +2490000000.0,-0.5458160023830065,321.2459720113194,637.3313661590022 +2495000000.0,-0.4513547589007682,321.2459720113194,716.2851695573034 +2500000000.0,-0.3784027765154235,321.2459720113194,795.0837807280163 +2505000000.0,-0.3212034814010437,321.2459720113194,873.7281289659178 +2510000000.0,-0.2757001407166431,321.2459720113194,952.2191361610434 +2515000000.0,-0.23900797417785127,321.2459720113194,1030.5577168722987 +2520000000.0,-0.2090499346617639,321.2459720113194,1108.744778400177 +2525000000.0,-0.1843098996351875,321.2459720113194,1186.781220858648 +2530000000.0,-0.16366568086884542,321.2459720113194,1264.6679372461426 +2535000000.0,-0.14627531957729697,321.2459720113194,1342.4058135157413 +2540000000.0,-0.1314988707547979,321.2459720113194,1419.995728644524 +2545000000.0,-0.11884395170191216,321.2459720113194,1497.4385547020763 +2550000000.0,-0.10792735185187852,321.2459720113194,1574.7351569182326 +2555000000.0,-0.09844761995079178,321.2459720113194,1651.886393749989 +2560000000.0,-0.09016524453500416,321.2459720113194,1728.8931169476398 +2565000000.0,-0.08288815143894424,321.2459720113194,1805.7561716201567 +2570000000.0,-0.07646096966251527,321.2459720113194,1882.4763962997913 +2575000000.0,-0.07075699953342553,321.2459720113194,1959.0546230059208 +2580000000.0,-0.06567214069175957,321.2459720113194,2035.4916773081557 +2585000000.0,-0.06112025684613682,321.2459720113194,2111.788378388725 +2590000000.0,-0.057029604724552985,321.2459720113194,2187.945539104122 +2595000000.0,-0.0533400589873249,321.2459720113194,2263.9639660460543 +2600000000.0,-0.050000938013230314,321.2459720113194,2339.8444596016793 +2605000000.0,-0.04696928728180251,321.2459720113194,2415.5878140131404 +2610000000.0,-0.04420851414687711,321.2459720113194,2491.1948174364356 +2615000000.0,-0.04168729457939987,321.2459720113194,2566.6662519996153 +2620000000.0,-0.039378691986037974,321.2459720113194,2642.002893860269 +2625000000.0,-0.037259442575151264,321.2459720113194,2717.205513262409 +2630000000.0,-0.03530937239792377,321.2459720113194,2792.2748745926856 +2635000000.0,-0.033510919160880163,321.2459720113194,2867.2117364359437 +2640000000.0,-0.03184873791048019,321.2459720113194,2942.016851630182 +2645000000.0,-0.03030937424831625,321.2459720113194,3016.690967320854 +2650000000.0,-0.028880992219150473,321.2459720113194,3091.2348250145847 +2655000000.0,-0.027553146694768334,321.2459720113194,3165.649160632259 +2660000000.0,-0.02631659215253232,321.2459720113194,3239.9347045615295 +2665000000.0,-0.025163121364869957,321.2459720113194,3314.092181708708 +2670000000.0,-0.024085428783408896,321.2459720113194,3388.1223115500943 +2675000000.0,-0.0230769944002957,321.2459720113194,3462.0258081827355 +2680000000.0,-0.022131984660547006,321.2459720113194,3535.803380374587 +2685000000.0,-0.021245167629427096,321.2459720113194,3609.455731614147 +2690000000.0,-0.02041184012311216,321.2459720113194,3682.9835601595137 +2695000000.0,-0.01962776491632967,321.2459720113194,3756.387559086908 +2700000000.0,-0.018889116468139917,321.2459720113194,3829.668416338643 diff --git a/tools/requirements.txt b/tools/requirements.txt index 86ef79c..845ae33 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,2 +1,2 @@ -# Python dependencies for tooling - +# Python dependencies for tooling and examples +numpy