Skip to content

Commit ddce67b

Browse files
committed
Add missing RFNetwork examples and update VectorFitting import syntax
1 parent ac28d7a commit ddce67b

5 files changed

Lines changed: 673 additions & 5 deletions

File tree

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+
}

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)