rftools-io 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rftools_io-0.1.0/LICENSE +21 -0
- rftools_io-0.1.0/PKG-INFO +232 -0
- rftools_io-0.1.0/README.md +195 -0
- rftools_io-0.1.0/pyproject.toml +58 -0
- rftools_io-0.1.0/rftools/__init__.py +68 -0
- rftools_io-0.1.0/rftools/_catalog.py +3968 -0
- rftools_io-0.1.0/rftools/_cli.py +168 -0
- rftools_io-0.1.0/rftools/calculators/__init__.py +2 -0
- rftools_io-0.1.0/rftools/calculators/antenna.py +236 -0
- rftools_io-0.1.0/rftools/calculators/audio.py +487 -0
- rftools_io-0.1.0/rftools/calculators/emc.py +472 -0
- rftools_io-0.1.0/rftools/calculators/general.py +649 -0
- rftools_io-0.1.0/rftools/calculators/motor.py +546 -0
- rftools_io-0.1.0/rftools/calculators/pcb.py +417 -0
- rftools_io-0.1.0/rftools/calculators/power.py +632 -0
- rftools_io-0.1.0/rftools/calculators/protocol.py +333 -0
- rftools_io-0.1.0/rftools/calculators/rf.py +800 -0
- rftools_io-0.1.0/rftools/calculators/sensor.py +505 -0
- rftools_io-0.1.0/rftools/calculators/signal.py +391 -0
- rftools_io-0.1.0/rftools/calculators/thermal.py +204 -0
- rftools_io-0.1.0/rftools/calculators/unit_conversion.py +409 -0
- rftools_io-0.1.0/rftools/client.py +389 -0
- rftools_io-0.1.0/rftools/exceptions.py +38 -0
- rftools_io-0.1.0/rftools/types.py +69 -0
- rftools_io-0.1.0/rftools_io.egg-info/PKG-INFO +232 -0
- rftools_io-0.1.0/rftools_io.egg-info/SOURCES.txt +33 -0
- rftools_io-0.1.0/rftools_io.egg-info/dependency_links.txt +1 -0
- rftools_io-0.1.0/rftools_io.egg-info/entry_points.txt +2 -0
- rftools_io-0.1.0/rftools_io.egg-info/requires.txt +12 -0
- rftools_io-0.1.0/rftools_io.egg-info/top_level.txt +1 -0
- rftools_io-0.1.0/setup.cfg +4 -0
- rftools_io-0.1.0/tests/test_cli.py +69 -0
- rftools_io-0.1.0/tests/test_client.py +136 -0
- rftools_io-0.1.0/tests/test_stubs.py +53 -0
- rftools_io-0.1.0/tests/test_types.py +41 -0
rftools_io-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rftools.io
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rftools-io
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for rftools.io — 203 RF & electronics calculators
|
|
5
|
+
Author-email: "rftools.io" <hello@rftools.io>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://rftools.io
|
|
8
|
+
Project-URL: Documentation, https://rftools.io/docs/python
|
|
9
|
+
Project-URL: Repository, https://github.com/rftools/rftools-py
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/rftools/rftools-py/issues
|
|
11
|
+
Keywords: rf,electronics,calculator,antenna,pcb,signal
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: pydantic>=2.0
|
|
27
|
+
Requires-Dist: click>=8.1
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
31
|
+
Requires-Dist: respx>=0.21; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
33
|
+
Requires-Dist: build; extra == "dev"
|
|
34
|
+
Requires-Dist: twine; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# rftools
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/rftools/)
|
|
41
|
+
[](https://pypi.org/project/rftools/)
|
|
42
|
+
|
|
43
|
+
Python client for **rftools.io** — 203 RF & electronics calculators accessible from Python and the command line.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install rftools-io
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start (free tier)
|
|
52
|
+
|
|
53
|
+
No API key needed. Free tier: 5 calls/day per IP.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import rftools
|
|
57
|
+
|
|
58
|
+
result = rftools.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
59
|
+
print(result["returnLoss"]) # 9.54 dB
|
|
60
|
+
print(result.values) # {"returnLoss": 9.54, "reflectionCoeff": 0.333, ...}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Authenticated Usage (API tier)
|
|
64
|
+
|
|
65
|
+
Get an API key at [rftools.io/pricing](https://rftools.io/pricing) ($19/mo, 10,000 calls/month).
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import rftools
|
|
69
|
+
|
|
70
|
+
client = rftools.Client(api_key="rfc_live_xxx")
|
|
71
|
+
# or set environment variable: export RFTOOLS_API_KEY=rfc_live_xxx
|
|
72
|
+
|
|
73
|
+
result = client.calculate("free-space-path-loss", {"frequency": 2400, "distance": 100})
|
|
74
|
+
print(result["pathLoss"]) # dB
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Batch Calculations
|
|
78
|
+
|
|
79
|
+
Run up to 50 calculations in a single HTTP request (API tier only):
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
results = client.batch([
|
|
83
|
+
("vswr-return-loss", {"vswr": 1.5}),
|
|
84
|
+
("vswr-return-loss", {"vswr": 2.0}),
|
|
85
|
+
("free-space-path-loss", {"frequency": 2400, "distance": 50}),
|
|
86
|
+
])
|
|
87
|
+
for r in results:
|
|
88
|
+
if r.ok:
|
|
89
|
+
print(r.values)
|
|
90
|
+
else:
|
|
91
|
+
print(f"Error: {r.error}")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Typed Category Stubs
|
|
95
|
+
|
|
96
|
+
IDE-friendly typed functions with parameter defaults matching the web UI:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from rftools.calculators import rf, pcb, antenna, power
|
|
100
|
+
|
|
101
|
+
# RF
|
|
102
|
+
result = rf.vswr_return_loss(vswr=2.5)
|
|
103
|
+
result = rf.free_space_path_loss(frequency=2400.0, distance=100.0)
|
|
104
|
+
|
|
105
|
+
# Antenna
|
|
106
|
+
result = antenna.dipole_antenna(frequency=433.0)
|
|
107
|
+
result = antenna.parabolic_dish_antenna(frequency=10000.0, diameter=0.6)
|
|
108
|
+
|
|
109
|
+
# PCB
|
|
110
|
+
result = pcb.trace_width_current(current=2.0, tempRise=10.0, thickness=1.0)
|
|
111
|
+
|
|
112
|
+
# Power
|
|
113
|
+
result = power.voltage_divider(vin=12.0, r1=10000.0, r2=10000.0)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
All 13 categories available: `rf`, `pcb`, `power`, `signal`, `antenna`, `general`, `motor`, `protocol`, `emc`, `thermal`, `sensor`, `unit_conversion`, `audio`.
|
|
117
|
+
|
|
118
|
+
## CLI
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Run a calculation
|
|
122
|
+
rftools calc vswr-return-loss --vswr 2.5
|
|
123
|
+
|
|
124
|
+
# With API key
|
|
125
|
+
RFTOOLS_API_KEY=rfc_xxx rftools calc free-space-path-loss --frequency 2400 --distance 100
|
|
126
|
+
|
|
127
|
+
# JSON output (pipe to jq)
|
|
128
|
+
rftools calc vswr-return-loss --vswr 2.5 --json | jq '.values.returnLoss'
|
|
129
|
+
|
|
130
|
+
# List all calculators
|
|
131
|
+
rftools list
|
|
132
|
+
|
|
133
|
+
# Filter by category
|
|
134
|
+
rftools list --category rf
|
|
135
|
+
|
|
136
|
+
# Show calculator inputs/outputs
|
|
137
|
+
rftools info free-space-path-loss
|
|
138
|
+
|
|
139
|
+
# Library version
|
|
140
|
+
rftools version
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Error Handling
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from rftools.exceptions import AuthError, RateLimitError, ValidationError, NotFoundError, APIError
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
result = client.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
150
|
+
except RateLimitError as e:
|
|
151
|
+
print(f"Quota exceeded. Retry after: {e.retry_after}s")
|
|
152
|
+
except AuthError:
|
|
153
|
+
print("Invalid API key")
|
|
154
|
+
except NotFoundError:
|
|
155
|
+
print("Unknown calculator slug")
|
|
156
|
+
except ValidationError as e:
|
|
157
|
+
print(f"Bad inputs: {e.detail}")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
| Exception | When |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `AuthError` | Invalid or missing API key |
|
|
163
|
+
| `RateLimitError` | Monthly quota exceeded |
|
|
164
|
+
| `ValidationError` | Bad inputs (HTTP 422) |
|
|
165
|
+
| `NotFoundError` | Unknown calculator slug |
|
|
166
|
+
| `APIError` | Unexpected HTTP error |
|
|
167
|
+
|
|
168
|
+
## Async Support
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import asyncio
|
|
172
|
+
import rftools
|
|
173
|
+
|
|
174
|
+
async def main():
|
|
175
|
+
async with rftools.AsyncClient(api_key="rfc_live_xxx") as client:
|
|
176
|
+
result = await client.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
177
|
+
print(result["returnLoss"])
|
|
178
|
+
|
|
179
|
+
asyncio.run(main())
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Ideal for running many calculations concurrently in FastAPI or async scripts.
|
|
183
|
+
|
|
184
|
+
## Browse the Catalog
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# List all calculators
|
|
188
|
+
calcs = rftools.list_calculators()
|
|
189
|
+
print(f"{len(calcs)} calculators available")
|
|
190
|
+
|
|
191
|
+
# Filter by category
|
|
192
|
+
rf_calcs = rftools.list_calculators(category="rf")
|
|
193
|
+
for c in rf_calcs:
|
|
194
|
+
print(f"{c.slug}: {c.title}")
|
|
195
|
+
|
|
196
|
+
# Get a single calculator's metadata
|
|
197
|
+
info = rftools.get_calculator("vswr-return-loss")
|
|
198
|
+
print(info.inputs) # tuple of InputField
|
|
199
|
+
print(info.outputs) # tuple of OutputField
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## All Calculator Categories
|
|
203
|
+
|
|
204
|
+
| Category | Count | Example |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| `rf` | 26 | `vswr-return-loss`, `free-space-path-loss`, `rf-link-budget` |
|
|
207
|
+
| `pcb` | 13 | `trace-width-current`, `via-calculator`, `microstrip-impedance` |
|
|
208
|
+
| `power` | 20 | `voltage-divider`, `led-resistor`, `battery-life` |
|
|
209
|
+
| `signal` | 13 | `filter-designer`, `op-amp-gain`, `pwm-duty-cycle` |
|
|
210
|
+
| `antenna` | 8 | `dipole-antenna`, `eirp-calculator`, `parabolic-dish-antenna` |
|
|
211
|
+
| `general` | 21 | `lc-resonance`, `ohms-law`, `rc-time-constant` |
|
|
212
|
+
| `motor` | 18 | `brushless-dc-motor`, `stepper-motor`, `servo-motor` |
|
|
213
|
+
| `protocol` | 11 | `uart-baud-rate`, `i2c-pullup`, `can-bus-bit-timing` |
|
|
214
|
+
| `emc` | 16 | `emi-filter-lc`, `shielding-effectiveness`, `ground-loop` |
|
|
215
|
+
| `thermal` | 6 | `thermal-resistance`, `heat-sink`, `junction-temperature` |
|
|
216
|
+
| `sensor` | 17 | `thermistor-ntc`, `strain-gauge`, `hall-effect` |
|
|
217
|
+
| `unit-conversion` | 17 | `dbm-watts`, `frequency-wavelength`, `temperature` |
|
|
218
|
+
| `audio` | 17 | `speaker-crossover`, `amplifier-gain`, `room-acoustics` |
|
|
219
|
+
|
|
220
|
+
## Contributing
|
|
221
|
+
|
|
222
|
+
Regenerate stubs after calculator changes:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
python scripts/generate_stubs.py --frontend-dir /path/to/rfhub/frontend
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Then bump the version in `pyproject.toml` and publish a new release.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# rftools
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/rftools/)
|
|
4
|
+
[](https://pypi.org/project/rftools/)
|
|
5
|
+
|
|
6
|
+
Python client for **rftools.io** — 203 RF & electronics calculators accessible from Python and the command line.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install rftools-io
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start (free tier)
|
|
15
|
+
|
|
16
|
+
No API key needed. Free tier: 5 calls/day per IP.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import rftools
|
|
20
|
+
|
|
21
|
+
result = rftools.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
22
|
+
print(result["returnLoss"]) # 9.54 dB
|
|
23
|
+
print(result.values) # {"returnLoss": 9.54, "reflectionCoeff": 0.333, ...}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Authenticated Usage (API tier)
|
|
27
|
+
|
|
28
|
+
Get an API key at [rftools.io/pricing](https://rftools.io/pricing) ($19/mo, 10,000 calls/month).
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import rftools
|
|
32
|
+
|
|
33
|
+
client = rftools.Client(api_key="rfc_live_xxx")
|
|
34
|
+
# or set environment variable: export RFTOOLS_API_KEY=rfc_live_xxx
|
|
35
|
+
|
|
36
|
+
result = client.calculate("free-space-path-loss", {"frequency": 2400, "distance": 100})
|
|
37
|
+
print(result["pathLoss"]) # dB
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Batch Calculations
|
|
41
|
+
|
|
42
|
+
Run up to 50 calculations in a single HTTP request (API tier only):
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
results = client.batch([
|
|
46
|
+
("vswr-return-loss", {"vswr": 1.5}),
|
|
47
|
+
("vswr-return-loss", {"vswr": 2.0}),
|
|
48
|
+
("free-space-path-loss", {"frequency": 2400, "distance": 50}),
|
|
49
|
+
])
|
|
50
|
+
for r in results:
|
|
51
|
+
if r.ok:
|
|
52
|
+
print(r.values)
|
|
53
|
+
else:
|
|
54
|
+
print(f"Error: {r.error}")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Typed Category Stubs
|
|
58
|
+
|
|
59
|
+
IDE-friendly typed functions with parameter defaults matching the web UI:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from rftools.calculators import rf, pcb, antenna, power
|
|
63
|
+
|
|
64
|
+
# RF
|
|
65
|
+
result = rf.vswr_return_loss(vswr=2.5)
|
|
66
|
+
result = rf.free_space_path_loss(frequency=2400.0, distance=100.0)
|
|
67
|
+
|
|
68
|
+
# Antenna
|
|
69
|
+
result = antenna.dipole_antenna(frequency=433.0)
|
|
70
|
+
result = antenna.parabolic_dish_antenna(frequency=10000.0, diameter=0.6)
|
|
71
|
+
|
|
72
|
+
# PCB
|
|
73
|
+
result = pcb.trace_width_current(current=2.0, tempRise=10.0, thickness=1.0)
|
|
74
|
+
|
|
75
|
+
# Power
|
|
76
|
+
result = power.voltage_divider(vin=12.0, r1=10000.0, r2=10000.0)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
All 13 categories available: `rf`, `pcb`, `power`, `signal`, `antenna`, `general`, `motor`, `protocol`, `emc`, `thermal`, `sensor`, `unit_conversion`, `audio`.
|
|
80
|
+
|
|
81
|
+
## CLI
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Run a calculation
|
|
85
|
+
rftools calc vswr-return-loss --vswr 2.5
|
|
86
|
+
|
|
87
|
+
# With API key
|
|
88
|
+
RFTOOLS_API_KEY=rfc_xxx rftools calc free-space-path-loss --frequency 2400 --distance 100
|
|
89
|
+
|
|
90
|
+
# JSON output (pipe to jq)
|
|
91
|
+
rftools calc vswr-return-loss --vswr 2.5 --json | jq '.values.returnLoss'
|
|
92
|
+
|
|
93
|
+
# List all calculators
|
|
94
|
+
rftools list
|
|
95
|
+
|
|
96
|
+
# Filter by category
|
|
97
|
+
rftools list --category rf
|
|
98
|
+
|
|
99
|
+
# Show calculator inputs/outputs
|
|
100
|
+
rftools info free-space-path-loss
|
|
101
|
+
|
|
102
|
+
# Library version
|
|
103
|
+
rftools version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Error Handling
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from rftools.exceptions import AuthError, RateLimitError, ValidationError, NotFoundError, APIError
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
result = client.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
113
|
+
except RateLimitError as e:
|
|
114
|
+
print(f"Quota exceeded. Retry after: {e.retry_after}s")
|
|
115
|
+
except AuthError:
|
|
116
|
+
print("Invalid API key")
|
|
117
|
+
except NotFoundError:
|
|
118
|
+
print("Unknown calculator slug")
|
|
119
|
+
except ValidationError as e:
|
|
120
|
+
print(f"Bad inputs: {e.detail}")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
| Exception | When |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `AuthError` | Invalid or missing API key |
|
|
126
|
+
| `RateLimitError` | Monthly quota exceeded |
|
|
127
|
+
| `ValidationError` | Bad inputs (HTTP 422) |
|
|
128
|
+
| `NotFoundError` | Unknown calculator slug |
|
|
129
|
+
| `APIError` | Unexpected HTTP error |
|
|
130
|
+
|
|
131
|
+
## Async Support
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import asyncio
|
|
135
|
+
import rftools
|
|
136
|
+
|
|
137
|
+
async def main():
|
|
138
|
+
async with rftools.AsyncClient(api_key="rfc_live_xxx") as client:
|
|
139
|
+
result = await client.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
140
|
+
print(result["returnLoss"])
|
|
141
|
+
|
|
142
|
+
asyncio.run(main())
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Ideal for running many calculations concurrently in FastAPI or async scripts.
|
|
146
|
+
|
|
147
|
+
## Browse the Catalog
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# List all calculators
|
|
151
|
+
calcs = rftools.list_calculators()
|
|
152
|
+
print(f"{len(calcs)} calculators available")
|
|
153
|
+
|
|
154
|
+
# Filter by category
|
|
155
|
+
rf_calcs = rftools.list_calculators(category="rf")
|
|
156
|
+
for c in rf_calcs:
|
|
157
|
+
print(f"{c.slug}: {c.title}")
|
|
158
|
+
|
|
159
|
+
# Get a single calculator's metadata
|
|
160
|
+
info = rftools.get_calculator("vswr-return-loss")
|
|
161
|
+
print(info.inputs) # tuple of InputField
|
|
162
|
+
print(info.outputs) # tuple of OutputField
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## All Calculator Categories
|
|
166
|
+
|
|
167
|
+
| Category | Count | Example |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `rf` | 26 | `vswr-return-loss`, `free-space-path-loss`, `rf-link-budget` |
|
|
170
|
+
| `pcb` | 13 | `trace-width-current`, `via-calculator`, `microstrip-impedance` |
|
|
171
|
+
| `power` | 20 | `voltage-divider`, `led-resistor`, `battery-life` |
|
|
172
|
+
| `signal` | 13 | `filter-designer`, `op-amp-gain`, `pwm-duty-cycle` |
|
|
173
|
+
| `antenna` | 8 | `dipole-antenna`, `eirp-calculator`, `parabolic-dish-antenna` |
|
|
174
|
+
| `general` | 21 | `lc-resonance`, `ohms-law`, `rc-time-constant` |
|
|
175
|
+
| `motor` | 18 | `brushless-dc-motor`, `stepper-motor`, `servo-motor` |
|
|
176
|
+
| `protocol` | 11 | `uart-baud-rate`, `i2c-pullup`, `can-bus-bit-timing` |
|
|
177
|
+
| `emc` | 16 | `emi-filter-lc`, `shielding-effectiveness`, `ground-loop` |
|
|
178
|
+
| `thermal` | 6 | `thermal-resistance`, `heat-sink`, `junction-temperature` |
|
|
179
|
+
| `sensor` | 17 | `thermistor-ntc`, `strain-gauge`, `hall-effect` |
|
|
180
|
+
| `unit-conversion` | 17 | `dbm-watts`, `frequency-wavelength`, `temperature` |
|
|
181
|
+
| `audio` | 17 | `speaker-crossover`, `amplifier-gain`, `room-acoustics` |
|
|
182
|
+
|
|
183
|
+
## Contributing
|
|
184
|
+
|
|
185
|
+
Regenerate stubs after calculator changes:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
python scripts/generate_stubs.py --frontend-dir /path/to/rfhub/frontend
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Then bump the version in `pyproject.toml` and publish a new release.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rftools-io"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for rftools.io — 203 RF & electronics calculators"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "rftools.io", email = "hello@rftools.io" }]
|
|
13
|
+
keywords = ["rf", "electronics", "calculator", "antenna", "pcb", "signal"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Scientific/Engineering",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.9"
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.27",
|
|
29
|
+
"pydantic>=2.0",
|
|
30
|
+
"click>=8.1",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8.0",
|
|
36
|
+
"pytest-asyncio>=0.23",
|
|
37
|
+
"respx>=0.21",
|
|
38
|
+
"ruff>=0.4",
|
|
39
|
+
"build",
|
|
40
|
+
"twine",
|
|
41
|
+
"pytest-cov>=4.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
rftools = "rftools._cli:cli"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://rftools.io"
|
|
49
|
+
Documentation = "https://rftools.io/docs/python"
|
|
50
|
+
Repository = "https://github.com/rftools/rftools-py"
|
|
51
|
+
"Bug Tracker" = "https://github.com/rftools/rftools-py/issues"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["."]
|
|
55
|
+
include = ["rftools*"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# rftools/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
rftools — Python client for rftools.io (203 RF & electronics calculators).
|
|
4
|
+
|
|
5
|
+
Quick start (free tier, 5 calls/day):
|
|
6
|
+
import rftools
|
|
7
|
+
result = rftools.calculate("vswr-return-loss", {"vswr": 2.5})
|
|
8
|
+
print(result["returnLoss"]) # 9.54
|
|
9
|
+
|
|
10
|
+
Authenticated (API tier, 10,000 calls/month):
|
|
11
|
+
client = rftools.Client(api_key="rfc_live_xxx")
|
|
12
|
+
result = client.calculate("free-space-path-loss", {"frequency": 2400, "distance": 100})
|
|
13
|
+
"""
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Client",
|
|
19
|
+
"AsyncClient",
|
|
20
|
+
"calculate",
|
|
21
|
+
"list_calculators",
|
|
22
|
+
"get_calculator",
|
|
23
|
+
"CalculatorResult",
|
|
24
|
+
"CalculatorInfo",
|
|
25
|
+
"InputField",
|
|
26
|
+
"OutputField",
|
|
27
|
+
"RFToolsError",
|
|
28
|
+
"AuthError",
|
|
29
|
+
"RateLimitError",
|
|
30
|
+
"ValidationError",
|
|
31
|
+
"NotFoundError",
|
|
32
|
+
"APIError",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
from rftools.client import Client, AsyncClient
|
|
36
|
+
from rftools.types import CalculatorResult, CalculatorInfo, InputField, OutputField
|
|
37
|
+
from rftools.exceptions import (
|
|
38
|
+
RFToolsError, AuthError, RateLimitError, ValidationError, NotFoundError, APIError,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
_default_client: Client | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_default_client() -> Client:
|
|
45
|
+
global _default_client
|
|
46
|
+
env_key = os.environ.get("RFTOOLS_API_KEY")
|
|
47
|
+
if _default_client is None or (env_key and _default_client._api_key != env_key):
|
|
48
|
+
_default_client = Client(api_key=env_key)
|
|
49
|
+
return _default_client
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def calculate(slug: str, inputs: dict) -> CalculatorResult:
|
|
53
|
+
"""
|
|
54
|
+
Run a calculator using the free tier (no API key required).
|
|
55
|
+
|
|
56
|
+
5 calls/day per IP. For higher limits, use ``rftools.Client(api_key=...)``.
|
|
57
|
+
"""
|
|
58
|
+
return _get_default_client().calculate(slug, inputs)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def list_calculators(category: str | None = None) -> list[CalculatorInfo]:
|
|
62
|
+
"""Return metadata for all 203 calculators, optionally filtered by category."""
|
|
63
|
+
return _get_default_client().list_calculators(category=category)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_calculator(slug: str) -> CalculatorInfo:
|
|
67
|
+
"""Return metadata for a single calculator by slug."""
|
|
68
|
+
return _get_default_client().get_calculator(slug)
|