Skip to content

Commit 7927f63

Browse files
authored
Merge pull request #3 from jhillairet/rf_updates
RF toolbox update
2 parents ac28d7a + aabc1f2 commit 7927f63

10 files changed

Lines changed: 694 additions & 21 deletions

File tree

.github/workflows/pypi_deployment.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ jobs:
1616
with:
1717
fetch-depth: 0
1818

19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v5
21+
with:
22+
enable-cache: true
23+
1924
- name: Set up Python
20-
uses: actions/setup-python@v3
25+
uses: actions/setup-python@v5
2126
with:
2227
python-version: '3.x'
2328

24-
- name: Install dependencies
25-
run: |
26-
python -m pip install --upgrade pip
27-
pip install build wheel setuptools
28-
2929
- name: Build package
30-
run: python -m build
30+
run: uv build
3131

3232
- name: Publish package
3333
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/tests.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ jobs:
55
runs-on: ubuntu-latest
66
steps:
77
- uses: actions/checkout@v4
8+
- name: Install uv
9+
uses: astral-sh/setup-uv@v5
10+
with:
11+
enable-cache: true
812
- name: Set up Python
913
uses: actions/setup-python@v5
1014
with:
1115
python-version: '3.x'
1216
- name: Install dependencies
13-
run: |
14-
python -m pip install --upgrade pip
15-
pip install -e .[test]
17+
run: uv sync --group test
1618
- name: Test with pytest
17-
run: |
18-
pytest tests/ -v
19+
run: uv run pytest tests/ -v

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
</p>
55

66
<p align="center">
7-
<strong>RF engineering blocks for PathSim</strong>
7+
<strong>RF engineering toolbox for PathSim</strong>
88
</p>
99

1010
<p align="center">
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "34829d58-0d57-4e51-a072-85ba31ddae69",
6+
"metadata": {
7+
"editable": true,
8+
"slideshow": {
9+
"slide_type": ""
10+
},
11+
"tags": []
12+
},
13+
"source": [
14+
"# RF Network - One Port"
15+
]
16+
},
17+
{
18+
"cell_type": "markdown",
19+
"id": "a17a7d0b-3409-42db-9c73-928f1343e349",
20+
"metadata": {},
21+
"source": [
22+
"This is an example of a simulation of a Radio Frequency (RF) network with PathSim."
23+
]
24+
},
25+
{
26+
"cell_type": "raw",
27+
"id": "f7785b45-d223-4b21-9199-776db9bf9a47",
28+
"metadata": {
29+
"editable": true,
30+
"raw_mimetype": "text/x-rst",
31+
"slideshow": {
32+
"slide_type": ""
33+
},
34+
"tags": []
35+
},
36+
"source": [
37+
"In this example, we are using the block :class:`.RFNetwork` provided in PathSim to create the state-space model of an N-port RF network. This block uses the `scikit-rf <https://scikit-rf.readthedocs.io/en/latest/>`__ package to convert the frequency domain data into a state-space model. This conversion is performed using a `Vector Fitting <https://scikit-rf.readthedocs.io/en/latest/tutorials/VectorFitting.html>`__ method for fitting a rational function (model) to a set of frequency‐domain.\n"
38+
]
39+
},
40+
{
41+
"cell_type": "markdown",
42+
"id": "c9b536c4-9450-4b39-b719-a1f85f6f32b7",
43+
"metadata": {
44+
"editable": true,
45+
"slideshow": {
46+
"slide_type": ""
47+
},
48+
"tags": []
49+
},
50+
"source": [
51+
"Let's first make all the necessary import"
52+
]
53+
},
54+
{
55+
"cell_type": "code",
56+
"execution_count": null,
57+
"id": "489bad7b-a5a3-4a12-b57d-8464e02a220c",
58+
"metadata": {
59+
"editable": true,
60+
"slideshow": {
61+
"slide_type": ""
62+
},
63+
"tags": []
64+
},
65+
"outputs": [],
66+
"source": [
67+
"import matplotlib.pyplot as plt\n",
68+
"\n",
69+
"# Apply PathSim docs matplotlib style for consistent, theme-friendly figures\n",
70+
"plt.style.use('../pathsim_docs.mplstyle')\n",
71+
"\n",
72+
"from pathsim import Simulation, Connection\n",
73+
"from pathsim.blocks import Spectrum, GaussianPulseSource\n",
74+
"from pathsim.solvers import RKBS32\n",
75+
"\n",
76+
"from pathsim_rf import RFNetwork # requires the scikit-rf package to be installed"
77+
]
78+
},
79+
{
80+
"cell_type": "raw",
81+
"id": "143be8a6-51b1-45ad-ba2b-5bc06011ad07",
82+
"metadata": {
83+
"editable": true,
84+
"raw_mimetype": "text/restructuredtext",
85+
"slideshow": {
86+
"slide_type": ""
87+
},
88+
"tags": []
89+
},
90+
"source": [
91+
"The block :class:`.RFNetwork` takes as input either a `Touchstone file <https://en.wikipedia.org/wiki/Touchstone_file>`__ (.sNp file) or a scikit-rf `Network <https://scikit-rf.readthedocs.io/en/latest/api/network.html>`__. An N-port network has N inputs and N outputs.\n"
92+
]
93+
},
94+
{
95+
"cell_type": "code",
96+
"execution_count": null,
97+
"id": "d89dffe4-7717-44b5-aaf2-3c618a3fd282",
98+
"metadata": {},
99+
"outputs": [],
100+
"source": [
101+
"# The RF-Network block is created from a scikit-rf Network object example.\n",
102+
"# Here we use a frequency measurement example of a 1-port RF network (included in scikit-rf)\n",
103+
"import skrf as rf # for the example\n",
104+
"rfntwk = RFNetwork(rf.data.ring_slot_meas)"
105+
]
106+
},
107+
{
108+
"cell_type": "markdown",
109+
"id": "7dec1f58-7924-442d-9e91-d1719c31b947",
110+
"metadata": {},
111+
"source": [
112+
"Under the hood, scikit-rf performs a Vector Fitting of the frequency data and creates a PathSim State-Space model."
113+
]
114+
},
115+
{
116+
"cell_type": "raw",
117+
"id": "be668818-88e6-4648-8267-d11966f9cdda",
118+
"metadata": {
119+
"editable": true,
120+
"raw_mimetype": "text/restructuredtext",
121+
"slideshow": {
122+
"slide_type": ""
123+
},
124+
"tags": []
125+
},
126+
"source": [
127+
"In the following, we use a gaussian pulse to simulate the impulse response of the RF block. A spectrum analyzer (:class:`Spectrum`) is used to display the frequency response of the response. This frequency response is then compared to the original frequency data."
128+
]
129+
},
130+
{
131+
"cell_type": "code",
132+
"execution_count": null,
133+
"id": "e1308665-3910-44f5-8d63-a7a45881b388",
134+
"metadata": {
135+
"editable": true,
136+
"slideshow": {
137+
"slide_type": ""
138+
},
139+
"tags": []
140+
},
141+
"outputs": [],
142+
"source": [
143+
"# Gaussian pulse simulating an impulse response\n",
144+
"# Note that the scikit-rf Network object is passed as the 'network' parameter of the block,\n",
145+
"# which is convenient to access its frequency data.\n",
146+
"src = GaussianPulseSource(f_max=rfntwk.network.frequency.stop)\n",
147+
"\n",
148+
"# Spectrum analyser setup with the start and stop frequencies of the RF network\n",
149+
"spc = Spectrum(\n",
150+
" freq=rfntwk.network.f,\n",
151+
" labels=[\"pulse\", \"response\"]\n",
152+
")"
153+
]
154+
},
155+
{
156+
"cell_type": "code",
157+
"execution_count": null,
158+
"id": "67f427af-deb1-417c-b602-2d454fac9489",
159+
"metadata": {
160+
"editable": true,
161+
"slideshow": {
162+
"slide_type": ""
163+
},
164+
"tags": []
165+
},
166+
"outputs": [],
167+
"source": [
168+
"# create the system connections and simulation setup\n",
169+
"sim = Simulation(\n",
170+
" blocks=[src, rfntwk, spc],\n",
171+
" connections=[\n",
172+
" Connection(src, rfntwk, spc[0]),\n",
173+
" Connection(rfntwk, spc[1])\n",
174+
" ],\n",
175+
" tolerance_lte_abs=1e-16, # this is due to the super tiny states\n",
176+
" tolerance_lte_rel=1e-5, # so error control is dominated by the relative truncation error\n",
177+
" Solver=RKBS32,\n",
178+
")\n",
179+
"\n",
180+
"sim.run(1e-9)"
181+
]
182+
},
183+
{
184+
"cell_type": "markdown",
185+
"id": "d659c9e6-560d-4a40-a11d-cc00faf00e7d",
186+
"metadata": {},
187+
"source": [
188+
"Below, we compare the PathSim's frequency response to the original measurement data and to their Vector Fitted model calculated with scikit-rf. The Vector Fitted model and the PathSim's frequency response are in perfect agreement:"
189+
]
190+
},
191+
{
192+
"cell_type": "code",
193+
"execution_count": null,
194+
"id": "b2d1f099-c806-4c11-b588-ed5ab1ba12fc",
195+
"metadata": {
196+
"editable": true,
197+
"slideshow": {
198+
"slide_type": ""
199+
},
200+
"tags": []
201+
},
202+
"outputs": [],
203+
"source": [
204+
"# model frequency response H(f) recovered from the spectrum block\n",
205+
"freq, (G_pulse, G_filt) = spc.read()\n",
206+
"H_filt_sim = G_filt / G_pulse\n",
207+
"\n",
208+
"# plot the original S11 data, the vector-fitted model and the recovered frequency response\n",
209+
"fig, ax = plt.subplots()\n",
210+
"ax.plot(rfntwk.network.f/1e9, abs(rfntwk.network.s[:, 0, 0]), '.', label=\"S11 measurements\", alpha=0.5)\n",
211+
"ax.plot(freq/1e9, abs(rfntwk.vf.get_model_response(0, 0, freqs=freq)), lw=2, label=\"scikit-rf vector-fitting model\")\n",
212+
"ax.plot(freq/1e9, abs(H_filt_sim), '--', lw=2, label=\"pathsim impulse response\")\n",
213+
"ax.set_xlabel(\"Frequency [GHz]\")\n",
214+
"ax.set_ylabel(\"S11 magnitude\")\n",
215+
"ax.legend()\n",
216+
"plt.show()"
217+
]
218+
}
219+
],
220+
"metadata": {
221+
"kernelspec": {
222+
"display_name": "pathsim-rf",
223+
"language": "python",
224+
"name": "pathsim-rf"
225+
},
226+
"language_info": {
227+
"codemirror_mode": {
228+
"name": "ipython",
229+
"version": 3
230+
},
231+
"file_extension": ".py",
232+
"mimetype": "text/x-python",
233+
"name": "python",
234+
"nbconvert_exporter": "python",
235+
"pygments_lexer": "ipython3",
236+
"version": "3.10.13"
237+
}
238+
},
239+
"nbformat": 4,
240+
"nbformat_minor": 5
241+
}

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[project]
66
name = "pathsim-rf"
77
dynamic = ["version"]
8-
description = "RF Engineering Blocks for PathSim"
8+
description = "RF Engineering toolbox for PathSim"
99
readme = "README.md"
1010
requires-python = ">=3.9"
1111
license = {text = "MIT"}
@@ -32,7 +32,11 @@ dependencies = [
3232
"scikit-rf>=0.30",
3333
]
3434

35-
[project.optional-dependencies]
35+
[dependency-groups]
36+
dev = [
37+
{ include-group = "docs" },
38+
{ include-group = "test" },
39+
]
3640
test = [
3741
"pytest>=7.0",
3842
"pytest-cov>=4.0",

src/pathsim_rf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
PathSim-RF: RF Engineering Blocks for PathSim
2+
PathSim-RF: RF Engineering toolbox for PathSim
33
44
A toolbox providing specialized blocks for RF and microwave engineering
55
simulations in the PathSim framework.

src/pathsim_rf/network.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pathlib import Path
1515

1616
import skrf as rf
17+
from skrf.vectorFitting import VectorFitting
1718

1819
from pathsim.blocks.lti import StateSpace
1920

@@ -35,8 +36,8 @@ class RFNetwork(StateSpace):
3536
scikit-rf RF Network object, or file to load information from.
3637
Supported formats are touchstone file V1 (.s?p) or V2 (.ts).
3738
auto_fit : bool
38-
If True (default), use ``skrf.VectorFitting.auto_fit`` for automatic
39-
pole selection. If False, use ``skrf.VectorFitting.vector_fit``.
39+
If True (default), use ``skrf.vectorFitting.VectorFitting.auto_fit`` for automatic
40+
pole selection. If False, use ``skrf.vectorFitting.VectorFitting.vector_fit``.
4041
**kwargs
4142
Additional keyword arguments forwarded to the vector fitting function.
4243
@@ -52,14 +53,14 @@ def __init__(self, ntwk: rf.Network | str | Path, auto_fit: bool = True, **kwarg
5253

5354
# select the vector fitting function from scikit-rf
5455
vf_fun_name = "auto_fit" if auto_fit else "vector_fit"
55-
vf_fun = getattr(rf.VectorFitting, vf_fun_name)
56+
vf_fun = getattr(VectorFitting, vf_fun_name)
5657

5758
# filter kwargs for the selected vf function
5859
vf_fun_keys = signature(vf_fun).parameters
5960
vf_kwargs = {k: v for k, v in kwargs.items() if k in vf_fun_keys}
6061

6162
# apply vector fitting
62-
vf = rf.VectorFitting(ntwk)
63+
vf = VectorFitting(ntwk)
6364
getattr(vf, vf_fun_name)(**vf_kwargs)
6465
A, B, C, D, _ = vf._get_ABCDE()
6566

@@ -82,6 +83,6 @@ def s(self, freqs: np.ndarray) -> np.ndarray:
8283
s : :py:class:`~numpy.ndarray`
8384
Complex-valued S-matrices (fxNxN) calculated at frequencies ``freqs``.
8485
"""
85-
return rf.VectorFitting._get_s_from_ABCDE(
86+
return VectorFitting._get_s_from_ABCDE(
8687
freqs, self.A, self.B, self.C, self.D, 0
8788
)

0 commit comments

Comments
 (0)