plot-antenna 2.0__tar.gz → 2.2__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.
@@ -1,4 +1,4 @@
1
- Copyright (C) 2022-23 Dr. Ralf Schlatterbeck Open Source Consulting.
1
+ Copyright (C) 2022-25 Dr. Ralf Schlatterbeck Open Source Consulting.
2
2
  Reichergasse 131, A-3411 Weidling.
3
3
  Web: http://www.runtux.com Email: office@runtux.com
4
4
  All rights reserved
@@ -1,16 +1,15 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: plot-antenna
3
- Version: 2.0
3
+ Version: 2.2
4
4
  Summary: Antenna plotting program for plotting antenna simulation results
5
5
  Home-page: https://github.com/schlatterbeck/plot-antenna
6
6
  Author: Ralf Schlatterbeck
7
7
  Author-email: Ralf Schlatterbeck <rsc@runtux.com>
8
- License: MIT License
8
+ License-Expression: MIT
9
9
  Project-URL: Homepage, https://github.com/schlatterbeck/plot-antenna
10
10
  Project-URL: Bug Tracker, https://github.com/schlatterbeck/plot-antenna/issues
11
11
  Platform: Any
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Operating System :: OS Independent
15
14
  Classifier: Programming Language :: Python
16
15
  Classifier: Intended Audience :: Science/Research
@@ -25,6 +24,11 @@ Requires-Dist: plotly
25
24
  Provides-Extra: test
26
25
  Requires-Dist: pytest; extra == "test"
27
26
  Requires-Dist: kaleido; extra == "test"
27
+ Dynamic: author
28
+ Dynamic: home-page
29
+ Dynamic: license-file
30
+ Dynamic: platform
31
+ Dynamic: requires-python
28
32
 
29
33
  Antenna Plotting Program
30
34
  ========================
@@ -84,6 +88,31 @@ above. The pattern look smoother but a 3D-view in matplotlib_ will be
84
88
  very slow due to the large number of points. This problem does not occur
85
89
  when using the plotly_ backend.
86
90
 
91
+ Sometimes we do not want the azimuth plot at the maximum elevation angle
92
+ or the elevation plot at the maximum azimuth angle. You can specify the
93
+ elevation angle for the azimuth plot with the ``--angle-elevation``
94
+ option and the azimuth angle for the elevation plot with
95
+ ``--angle-azimuth``. An example azimuth plot of the same antenna at an
96
+ elevation angle of 15° can be plotted with::
97
+
98
+ plot-antenna --azimuth --angle-elevation=15 test/12-el-1deg.pout
99
+
100
+ .. figure:: https://raw.githubusercontent.com/schlatterbeck/plot-antenna/master/test/12-el-azi-angle-ele15.png
101
+ :align: center
102
+
103
+ As you can see, the azimuth-plot is scaled to the maximum gain of the
104
+ antenna. Sometimes we want to scale the gain to the maximum *at that
105
+ elevation (or azimuth) angle*. This can be achieved with the
106
+ ``--scale-by-angle`` option::
107
+
108
+ plot-antenna --azi --angle-ele=15 --scale-by-angle test/12-el-1deg.pout
109
+
110
+ .. figure:: https://raw.githubusercontent.com/schlatterbeck/plot-antenna/master/test/12-el-azi-angle-e15-sc.png
111
+ :align: center
112
+
113
+ You can see that now the pattern is scaled to the maximum *at that
114
+ elevation angle*. The outer ring now has 12.92 dBi instead of 14.50 dBi.
115
+
87
116
  The plot program also has a ``--help``
88
117
  option for further information. In particular the scaling of the antenna
89
118
  plot can be selected using the ``--scaling-method`` option with an
@@ -198,30 +227,107 @@ Note that for the measurement-data the unit of the data is not in dBi
198
227
  but (because it was measured and not calibrated to dBi) in dBm. The
199
228
  measurements were separate for horizontal and vertical polarization.
200
229
 
230
+ The program for plotting the measurements is in
231
+ ``plot_antenna/contrib.py``. It can serve as an example of how to plot
232
+ your own data with `plot-antenna`_. The eznec program in
233
+ ``plot_antenna/eznec.py`` might even be better in this regard. See the
234
+ next section on documentation of the `plot-antenna`_ API.
235
+
236
+ Plot-Antenna API
237
+ ----------------
238
+
239
+ The main class to plot things is the ``Gain_Plot`` class. It gets the
240
+ command-line arguments and the gain data to plot. Note that the class is
241
+ a little mis-named now because it can also do all the other plots (e.g.
242
+ standing wave ratio, SWR). The gain data passed to the constructor of
243
+ ``Gain_Plot`` gets a dictionary of ``Gain_Data`` objects. The keys of
244
+ the dictionary are tuples ``(frequency, string)`` where the frequency is
245
+ the frequency of the ``Gain_Data`` and the string is used for describing
246
+ what is plotted. Since `plot-antenna`_ can have traces for the different
247
+ polarizations in the same plot, usually the string is one of ``H`` for
248
+ horizontal polarization, ``V`` for vertical polarization and ``sum`` for
249
+ the sum of all polarizations. Of course only the sum can be provided if
250
+ we do not want multiple polarizations.
251
+
252
+ If you are not plotting gain but, say, only SWR data, the gain data
253
+ object passed to the ``Gain_Plot`` constructor may be ``None``.
254
+
255
+ The ``Gain_Data`` object gets a list of frequencies in the constructor.
256
+ It has an internal ``pattern`` dictionary which stores the gain values
257
+ by a tuple of ``(theta, phi)`` where ``theta`` is the elevation angle
258
+ (measured from the zenith=0 degrees) and the azimuth angle phi measured
259
+ from the positive X-axis. The gain values in this data structure are in
260
+ dBi (Decibel over an isotropic radiator). There is currently no way to
261
+ directly pass a numpy array with the gains. A simple program to
262
+ construct an azimuth plot of an antenna that has the same pattern in all
263
+ directions (gain=0dB) would be::
264
+
265
+ import numpy as np
266
+ from plot_antenna import plot_antenna
267
+
268
+ frequency = 430.0
269
+ polarization = 'sum'
270
+ key = (frequency, polarization)
271
+ gdict = {key: plot_antenna.Gain_Data ([frequency])}
272
+ data = gdict [key].pattern
273
+ for azi in np.arange (0, 361, 10):
274
+ data [(90.0, azi)] = 0.0
275
+ gp = plot_antenna.Gain_Plot (args, gdict)
276
+ gp.compute ()
277
+ gp.plot ()
278
+
279
+ The parsed arguments can typically be constructed by calling one of the
280
+ argument parsing functions. These need not be given the real command
281
+ line arguments but can be called with an empty string list, e.g.::
282
+
283
+ # Initialize command options with general options
284
+ cmd = plot_antenna.options_general ()
285
+ # Add gain options
286
+ plot_antenna.options_gain (cmd)
287
+ # Parse empty arguments resulting in default args
288
+ args = plot_antenna.process_args (cmd, [])
289
+ # The filename is needed internally for computing default title
290
+ args.filename = ''
291
+ # Title
292
+ args.title = 'My Title'
293
+ # We want an azimuth plot
294
+ args.azimuth = True
295
+ # We might want to ship result to running browser with plotly
296
+ # args.show_in_browser = True
297
+
298
+ The ``cmd`` variable is a python ``ArgumentParser`` object. So if you
299
+ are parsing command line arguments you can add your own options before
300
+ calling ``process_args``
301
+
302
+ If not parsing argument from the command line and arguments should be
303
+ changed this can be done by directly modifying args, e.g.::
304
+
305
+ args.title = 'This is the title of my plot'
306
+
307
+ A full but short implementation of a usage of this API can be found in
308
+ the companion program for reading EZNEC data in
309
+ ``plot_antenna/eznec.py``. This example can be found in ``example.py``.
310
+
311
+
201
312
  .. [1] L. B. Cebik. Radiation plots: Polar or rectangular; log or linear.
202
313
  In Antenna Modeling Notes [2], chapter 48, pages 366–379. Available
203
- in Cebik's `Antenna modelling notes episode 48`_
314
+ in Cebik's `Antenna modelling notes episode 48`_ or `from web
315
+ archive`_
204
316
  .. [2] L. B. Cebik. Antenna Modeling Notes, volume 2. antenneX Online
205
- Magazine, 2003. Available with antenna models from the `Cebik
206
- collection`_.
207
-
208
- .. _`Cebik collection`:
209
- http://on5au.be/Books/allmodnotes.zip
210
- .. _`Antenna modelling notes episode 48`:
211
- http://on5au.be/content/amod/amod48.html
212
- .. _nec2c: https://packages.debian.org/stable/hamradio/nec2c
213
- .. _pymininec: https://github.com/schlatterbeck/pymininec
214
- .. _matplotlib: https://matplotlib.org/
215
- .. _plotly: https://github.com/plotly/plotly.py
216
- .. _pandas: https://pandas.pydata.org/
217
- .. _Mininec: https://github.com/Kees-PA3KJ/MiniNec
218
- .. _ASAP: http://raylcross.net/asap/index.html
219
- .. _EZNEC: https://eznec.com/
220
- .. _plot-antenna: https://github.com/schlatterbeck/plot-antenna
317
+ Magazine, 2003. Available `Cebik collection`_
221
318
 
222
319
  Release Notes
223
320
  -------------
224
321
 
322
+ v2.1: Scale by angle
323
+
324
+ - New option ``--scale-by-angle`` that allows to scale the azimuth or
325
+ elevation pattern to the maximum at the current elevation- or azimuth
326
+ angle instead of the global maximum, thanks to Daniel Bruschinski for
327
+ suggesting this
328
+ - Add a little documentation how to use the API, thanks to Alex, VE3NEA
329
+ for suggesting this in a github issue.
330
+
225
331
  v2.0: More input formats
226
332
 
227
333
  - Import from EZNEC_ exported pattern data
@@ -329,3 +435,17 @@ v1.1: Specification of azimuth / elevation angle
329
435
  v1.0: Initial Release
330
436
 
331
437
  .. _`patched pySmithPlot`: https://github.com/schlatterbeck/pySmithPlot
438
+ .. _`Cebik collection`:
439
+ https://q82.uk/projects/cebik/modelling/W4RNL%20Antenna%20Modelling%20Notes%20Volume%202.pdf
440
+ .. _`from web archive`: https://web.archive.org/web/20230816222342/http://on5au.be/content/amod/amod48.html
441
+ .. _`Antenna modelling notes episode 48`:
442
+ https://q82.uk/projects/cebik/modelling/48.%20Radiation%20Plots%20%20-%20Polar%20or%20Rectangular.%20Log%20or%20Linear.pdf
443
+ .. _nec2c: https://packages.debian.org/stable/hamradio/nec2c
444
+ .. _pymininec: https://github.com/schlatterbeck/pymininec
445
+ .. _matplotlib: https://matplotlib.org/
446
+ .. _plotly: https://github.com/plotly/plotly.py
447
+ .. _Mininec: https://github.com/Kees-PA3KJ/MiniNec
448
+ .. _ASAP: http://raylcross.net/asap/index.html
449
+ .. _EZNEC: https://eznec.com/
450
+ .. _plot-antenna: https://github.com/schlatterbeck/plot-antenna
451
+
@@ -56,6 +56,31 @@ above. The pattern look smoother but a 3D-view in matplotlib_ will be
56
56
  very slow due to the large number of points. This problem does not occur
57
57
  when using the plotly_ backend.
58
58
 
59
+ Sometimes we do not want the azimuth plot at the maximum elevation angle
60
+ or the elevation plot at the maximum azimuth angle. You can specify the
61
+ elevation angle for the azimuth plot with the ``--angle-elevation``
62
+ option and the azimuth angle for the elevation plot with
63
+ ``--angle-azimuth``. An example azimuth plot of the same antenna at an
64
+ elevation angle of 15° can be plotted with::
65
+
66
+ plot-antenna --azimuth --angle-elevation=15 test/12-el-1deg.pout
67
+
68
+ .. figure:: https://raw.githubusercontent.com/schlatterbeck/plot-antenna/master/test/12-el-azi-angle-ele15.png
69
+ :align: center
70
+
71
+ As you can see, the azimuth-plot is scaled to the maximum gain of the
72
+ antenna. Sometimes we want to scale the gain to the maximum *at that
73
+ elevation (or azimuth) angle*. This can be achieved with the
74
+ ``--scale-by-angle`` option::
75
+
76
+ plot-antenna --azi --angle-ele=15 --scale-by-angle test/12-el-1deg.pout
77
+
78
+ .. figure:: https://raw.githubusercontent.com/schlatterbeck/plot-antenna/master/test/12-el-azi-angle-e15-sc.png
79
+ :align: center
80
+
81
+ You can see that now the pattern is scaled to the maximum *at that
82
+ elevation angle*. The outer ring now has 12.92 dBi instead of 14.50 dBi.
83
+
59
84
  The plot program also has a ``--help``
60
85
  option for further information. In particular the scaling of the antenna
61
86
  plot can be selected using the ``--scaling-method`` option with an
@@ -170,30 +195,107 @@ Note that for the measurement-data the unit of the data is not in dBi
170
195
  but (because it was measured and not calibrated to dBi) in dBm. The
171
196
  measurements were separate for horizontal and vertical polarization.
172
197
 
198
+ The program for plotting the measurements is in
199
+ ``plot_antenna/contrib.py``. It can serve as an example of how to plot
200
+ your own data with `plot-antenna`_. The eznec program in
201
+ ``plot_antenna/eznec.py`` might even be better in this regard. See the
202
+ next section on documentation of the `plot-antenna`_ API.
203
+
204
+ Plot-Antenna API
205
+ ----------------
206
+
207
+ The main class to plot things is the ``Gain_Plot`` class. It gets the
208
+ command-line arguments and the gain data to plot. Note that the class is
209
+ a little mis-named now because it can also do all the other plots (e.g.
210
+ standing wave ratio, SWR). The gain data passed to the constructor of
211
+ ``Gain_Plot`` gets a dictionary of ``Gain_Data`` objects. The keys of
212
+ the dictionary are tuples ``(frequency, string)`` where the frequency is
213
+ the frequency of the ``Gain_Data`` and the string is used for describing
214
+ what is plotted. Since `plot-antenna`_ can have traces for the different
215
+ polarizations in the same plot, usually the string is one of ``H`` for
216
+ horizontal polarization, ``V`` for vertical polarization and ``sum`` for
217
+ the sum of all polarizations. Of course only the sum can be provided if
218
+ we do not want multiple polarizations.
219
+
220
+ If you are not plotting gain but, say, only SWR data, the gain data
221
+ object passed to the ``Gain_Plot`` constructor may be ``None``.
222
+
223
+ The ``Gain_Data`` object gets a list of frequencies in the constructor.
224
+ It has an internal ``pattern`` dictionary which stores the gain values
225
+ by a tuple of ``(theta, phi)`` where ``theta`` is the elevation angle
226
+ (measured from the zenith=0 degrees) and the azimuth angle phi measured
227
+ from the positive X-axis. The gain values in this data structure are in
228
+ dBi (Decibel over an isotropic radiator). There is currently no way to
229
+ directly pass a numpy array with the gains. A simple program to
230
+ construct an azimuth plot of an antenna that has the same pattern in all
231
+ directions (gain=0dB) would be::
232
+
233
+ import numpy as np
234
+ from plot_antenna import plot_antenna
235
+
236
+ frequency = 430.0
237
+ polarization = 'sum'
238
+ key = (frequency, polarization)
239
+ gdict = {key: plot_antenna.Gain_Data ([frequency])}
240
+ data = gdict [key].pattern
241
+ for azi in np.arange (0, 361, 10):
242
+ data [(90.0, azi)] = 0.0
243
+ gp = plot_antenna.Gain_Plot (args, gdict)
244
+ gp.compute ()
245
+ gp.plot ()
246
+
247
+ The parsed arguments can typically be constructed by calling one of the
248
+ argument parsing functions. These need not be given the real command
249
+ line arguments but can be called with an empty string list, e.g.::
250
+
251
+ # Initialize command options with general options
252
+ cmd = plot_antenna.options_general ()
253
+ # Add gain options
254
+ plot_antenna.options_gain (cmd)
255
+ # Parse empty arguments resulting in default args
256
+ args = plot_antenna.process_args (cmd, [])
257
+ # The filename is needed internally for computing default title
258
+ args.filename = ''
259
+ # Title
260
+ args.title = 'My Title'
261
+ # We want an azimuth plot
262
+ args.azimuth = True
263
+ # We might want to ship result to running browser with plotly
264
+ # args.show_in_browser = True
265
+
266
+ The ``cmd`` variable is a python ``ArgumentParser`` object. So if you
267
+ are parsing command line arguments you can add your own options before
268
+ calling ``process_args``
269
+
270
+ If not parsing argument from the command line and arguments should be
271
+ changed this can be done by directly modifying args, e.g.::
272
+
273
+ args.title = 'This is the title of my plot'
274
+
275
+ A full but short implementation of a usage of this API can be found in
276
+ the companion program for reading EZNEC data in
277
+ ``plot_antenna/eznec.py``. This example can be found in ``example.py``.
278
+
279
+
173
280
  .. [1] L. B. Cebik. Radiation plots: Polar or rectangular; log or linear.
174
281
  In Antenna Modeling Notes [2], chapter 48, pages 366–379. Available
175
- in Cebik's `Antenna modelling notes episode 48`_
282
+ in Cebik's `Antenna modelling notes episode 48`_ or `from web
283
+ archive`_
176
284
  .. [2] L. B. Cebik. Antenna Modeling Notes, volume 2. antenneX Online
177
- Magazine, 2003. Available with antenna models from the `Cebik
178
- collection`_.
179
-
180
- .. _`Cebik collection`:
181
- http://on5au.be/Books/allmodnotes.zip
182
- .. _`Antenna modelling notes episode 48`:
183
- http://on5au.be/content/amod/amod48.html
184
- .. _nec2c: https://packages.debian.org/stable/hamradio/nec2c
185
- .. _pymininec: https://github.com/schlatterbeck/pymininec
186
- .. _matplotlib: https://matplotlib.org/
187
- .. _plotly: https://github.com/plotly/plotly.py
188
- .. _pandas: https://pandas.pydata.org/
189
- .. _Mininec: https://github.com/Kees-PA3KJ/MiniNec
190
- .. _ASAP: http://raylcross.net/asap/index.html
191
- .. _EZNEC: https://eznec.com/
192
- .. _plot-antenna: https://github.com/schlatterbeck/plot-antenna
285
+ Magazine, 2003. Available `Cebik collection`_
193
286
 
194
287
  Release Notes
195
288
  -------------
196
289
 
290
+ v2.1: Scale by angle
291
+
292
+ - New option ``--scale-by-angle`` that allows to scale the azimuth or
293
+ elevation pattern to the maximum at the current elevation- or azimuth
294
+ angle instead of the global maximum, thanks to Daniel Bruschinski for
295
+ suggesting this
296
+ - Add a little documentation how to use the API, thanks to Alex, VE3NEA
297
+ for suggesting this in a github issue.
298
+
197
299
  v2.0: More input formats
198
300
 
199
301
  - Import from EZNEC_ exported pattern data
@@ -301,3 +403,17 @@ v1.1: Specification of azimuth / elevation angle
301
403
  v1.0: Initial Release
302
404
 
303
405
  .. _`patched pySmithPlot`: https://github.com/schlatterbeck/pySmithPlot
406
+ .. _`Cebik collection`:
407
+ https://q82.uk/projects/cebik/modelling/W4RNL%20Antenna%20Modelling%20Notes%20Volume%202.pdf
408
+ .. _`from web archive`: https://web.archive.org/web/20230816222342/http://on5au.be/content/amod/amod48.html
409
+ .. _`Antenna modelling notes episode 48`:
410
+ https://q82.uk/projects/cebik/modelling/48.%20Radiation%20Plots%20%20-%20Polar%20or%20Rectangular.%20Log%20or%20Linear.pdf
411
+ .. _nec2c: https://packages.debian.org/stable/hamradio/nec2c
412
+ .. _pymininec: https://github.com/schlatterbeck/pymininec
413
+ .. _matplotlib: https://matplotlib.org/
414
+ .. _plotly: https://github.com/plotly/plotly.py
415
+ .. _Mininec: https://github.com/Kees-PA3KJ/MiniNec
416
+ .. _ASAP: http://raylcross.net/asap/index.html
417
+ .. _EZNEC: https://eznec.com/
418
+ .. _plot-antenna: https://github.com/schlatterbeck/plot-antenna
419
+
@@ -0,0 +1 @@
1
+ 2.2
@@ -0,0 +1 @@
1
+ VERSION="2.2"
@@ -3,6 +3,7 @@
3
3
  import sys
4
4
  from csv import DictReader
5
5
  from . import plot_antenna as aplot
6
+ from .plot_antenna import Impedance_Data
6
7
 
7
8
  def parse_eznec_data (args):
8
9
  """ Parses eznec date exported with 'FF Tab'
@@ -15,12 +16,13 @@ def parse_eznec_data (args):
15
16
  state = 'start'
16
17
  head_seen = False
17
18
  gdata_dict = {}
19
+ impedance = None
18
20
  with open (args.filename, 'r') as rfile:
19
21
  for line in rfile:
20
22
  line = line.strip ()
21
23
  if state == 'start':
22
- line = line.replace (',', '.')
23
24
  if line.startswith ('Frequency'):
25
+ line = line.replace (',', '.')
24
26
  if not line.endswith ('MHz'):
25
27
  raise ValueError \
26
28
  ('Unsupported frequency format: %s' % line)
@@ -43,7 +45,12 @@ def parse_eznec_data (args):
43
45
  angle += 90
44
46
  head_seen = False
45
47
  continue
48
+ if line.startswith ('"Freq MHz","Src #","R","X"'):
49
+ state = 'impedance'
50
+ impedance = {}
51
+ continue
46
52
  if state in ('azi', 'ele'):
53
+ line = line.replace (',', '.')
47
54
  if not head_seen:
48
55
  l = ' '.join (line.split ())
49
56
  if l != 'Deg V dB H dB Tot dB V Pha H Pha':
@@ -78,21 +85,38 @@ def parse_eznec_data (args):
78
85
  azi = angle
79
86
  #print (ele, azi, pol, gain)
80
87
  gdata.pattern [(ele, azi)] = gain
81
- return gdata_dict
82
- # end def parse_csv_measurement_data
88
+ continue
89
+ if state == 'impedance':
90
+ f,src,r,x,swr1,swr2 = (float (x) for x in line.split (','))
91
+ z = r + 1j * x
92
+ impedance [f] = Impedance_Data (f, z)
93
+ return gdata_dict, impedance
94
+ # end def parse_eznec_data
83
95
 
84
96
  def main_eznec (argv = sys.argv [1:], pic_io = None):
85
97
  """ Parse eznec far field data.
86
98
  """
87
99
  cmd = aplot.options_general ()
88
100
  aplot.options_gain (cmd)
101
+ aplot.options_swr (cmd)
89
102
  cmd.add_argument ('filename', help = 'EZNEC far field data to plot')
90
103
  args = aplot.process_args (cmd, argv)
91
104
  if pic_io is not None:
92
105
  args.output_file = pic_io
93
106
  args.save_format = 'png'
94
- gdata = parse_eznec_data (args)
107
+ gdata, idata = parse_eznec_data (args)
108
+ if idata and not args.plot_vswr:
109
+ args.plot_vswr = True
110
+ if gdata and not args.elevation and not args.azimuth and not args.plot3d:
111
+ args.plot3d = True
112
+ if not gdata:
113
+ args.plot3d = False
114
+ args.elevation = False
115
+ args.azimuth = False
116
+ if not idata:
117
+ args.plot_vswr = False
95
118
  gp = aplot.Gain_Plot (args, gdata)
119
+ gp.idata = idata
96
120
  gp.compute ()
97
121
  gp.plot ()
98
122
  # end def main_eznec