phased-array-modeling 1.1.0__tar.gz → 1.2.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.
Files changed (25) hide show
  1. {phased_array_modeling-1.1.0/phased_array_modeling.egg-info → phased_array_modeling-1.2.0}/PKG-INFO +1 -1
  2. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/__init__.py +26 -4
  3. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/visualization.py +214 -0
  4. phased_array_modeling-1.2.0/phased_array/wideband.py +732 -0
  5. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0/phased_array_modeling.egg-info}/PKG-INFO +1 -1
  6. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array_modeling.egg-info/SOURCES.txt +3 -1
  7. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/pyproject.toml +1 -1
  8. phased_array_modeling-1.2.0/tests/test_wideband.py +333 -0
  9. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/LICENSE +0 -0
  10. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/README.md +0 -0
  11. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/beamforming.py +0 -0
  12. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/core.py +0 -0
  13. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/export.py +0 -0
  14. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/geometry.py +0 -0
  15. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/impairments.py +0 -0
  16. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array/utils.py +0 -0
  17. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array_modeling.egg-info/dependency_links.txt +0 -0
  18. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array_modeling.egg-info/requires.txt +0 -0
  19. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/phased_array_modeling.egg-info/top_level.txt +0 -0
  20. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/setup.cfg +0 -0
  21. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/tests/test_beamforming.py +0 -0
  22. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/tests/test_core.py +0 -0
  23. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/tests/test_export.py +0 -0
  24. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/tests/test_geometry.py +0 -0
  25. {phased_array_modeling-1.1.0 → phased_array_modeling-1.2.0}/tests/test_impairments.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phased-array-modeling
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Comprehensive Python library for phased array antenna modeling and visualization
5
5
  Author-email: John Hodge <jah70@vt.edu>
6
6
  License: MIT
@@ -32,7 +32,7 @@ Example
32
32
  >>> theta, phi, pattern_dB = pa.compute_full_pattern(geom.x, geom.y, weights, k)
33
33
  """
34
34
 
35
- __version__ = "1.1.0"
35
+ __version__ = "1.2.0"
36
36
 
37
37
  # Beamforming functions
38
38
  from .beamforming import ( # Amplitude tapers; Null steering; Multiple beams
@@ -81,13 +81,20 @@ from .utils import (azel_to_thetaphi, create_theta_phi_grid, create_uv_grid,
81
81
  normalize_pattern, rad2deg, theta_phi_to_uv,
82
82
  thetaphi_to_azel, uv_to_theta_phi, wavelength_to_k)
83
83
  # Visualization functions
84
- from .visualization import ( # 2D matplotlib plots; UV-space; 3D Plotly plots; Utilities
84
+ from .visualization import ( # 2D matplotlib plots; UV-space; 3D Plotly plots; Wideband
85
85
  compute_pattern_uv_space, create_pattern_animation_plotly,
86
- plot_array_geometry, plot_array_geometry_3d_plotly,
86
+ plot_array_geometry, plot_array_geometry_3d_plotly, plot_beam_squint,
87
87
  plot_comparison_patterns, plot_pattern_2d,
88
88
  plot_pattern_3d_cartesian_plotly, plot_pattern_3d_plotly,
89
89
  plot_pattern_contour, plot_pattern_polar, plot_pattern_uv_plotly,
90
- plot_pattern_uv_space)
90
+ plot_pattern_uv_space, plot_pattern_vs_frequency,
91
+ plot_pattern_vs_frequency_plotly, plot_subarray_delays)
92
+ # Wideband / TTD functions
93
+ from .wideband import (analyze_instantaneous_bandwidth, compare_steering_modes,
94
+ compute_beam_squint, compute_pattern_vs_frequency,
95
+ compute_subarray_delays_ttd,
96
+ compute_subarray_weights_hybrid, steering_delays_ttd,
97
+ steering_vector_hybrid, steering_vector_ttd)
91
98
 
92
99
  __all__ = [
93
100
  # Version
@@ -200,4 +207,19 @@ __all__ = [
200
207
  "load_pattern_npz",
201
208
  "export_coupling_matrix_csv",
202
209
  "export_summary_report",
210
+ # Wideband / TTD
211
+ "steering_vector_ttd",
212
+ "steering_delays_ttd",
213
+ "steering_vector_hybrid",
214
+ "compute_subarray_delays_ttd",
215
+ "compute_beam_squint",
216
+ "analyze_instantaneous_bandwidth",
217
+ "compute_pattern_vs_frequency",
218
+ "compute_subarray_weights_hybrid",
219
+ "compare_steering_modes",
220
+ # Wideband visualization
221
+ "plot_beam_squint",
222
+ "plot_pattern_vs_frequency",
223
+ "plot_pattern_vs_frequency_plotly",
224
+ "plot_subarray_delays",
203
225
  ]
@@ -823,3 +823,217 @@ def create_pattern_animation_plotly(
823
823
  )
824
824
 
825
825
  return fig
826
+
827
+
828
+ # ============== Wideband / Beam Squint Plots ==============
829
+
830
+ def plot_beam_squint(
831
+ frequencies: np.ndarray,
832
+ squint_data: Dict[str, np.ndarray],
833
+ center_frequency: float,
834
+ title: str = "Beam Squint vs Frequency",
835
+ figsize: Tuple[int, int] = (10, 6)
836
+ ) -> Any:
837
+ """
838
+ Plot beam squint comparison for different steering modes.
839
+
840
+ Parameters
841
+ ----------
842
+ frequencies : ndarray
843
+ Frequency values in Hz
844
+ squint_data : dict
845
+ Dictionary of {mode_name: squint_array} in degrees
846
+ center_frequency : float
847
+ Center frequency in Hz (for normalization)
848
+ title : str
849
+ Plot title
850
+ figsize : tuple
851
+ Figure size
852
+
853
+ Returns
854
+ -------
855
+ ax : matplotlib axis
856
+ """
857
+ if not MATPLOTLIB_AVAILABLE:
858
+ raise ImportError("matplotlib is required for this function")
859
+
860
+ fig, ax = plt.subplots(figsize=figsize)
861
+
862
+ # Normalize frequency to percentage of center
863
+ freq_percent = (frequencies - center_frequency) / center_frequency * 100
864
+
865
+ colors = {'phase': 'red', 'hybrid': 'blue', 'ttd': 'green'}
866
+ labels = {'phase': 'Phase-only', 'hybrid': 'Hybrid (TTD + Phase)', 'ttd': 'True-Time Delay'}
867
+
868
+ for mode, squint in squint_data.items():
869
+ color = colors.get(mode, None)
870
+ label = labels.get(mode, mode)
871
+ ax.plot(freq_percent, squint, 'o-', label=label, color=color, linewidth=2, markersize=6)
872
+
873
+ ax.axhline(0, color='gray', linestyle='--', alpha=0.5)
874
+ ax.axvline(0, color='gray', linestyle='--', alpha=0.5)
875
+
876
+ ax.set_xlabel('Frequency Offset (%)')
877
+ ax.set_ylabel('Beam Squint (degrees)')
878
+ ax.set_title(title)
879
+ ax.grid(True, alpha=0.3)
880
+ ax.legend()
881
+
882
+ return ax
883
+
884
+
885
+ def plot_pattern_vs_frequency(
886
+ angles: np.ndarray,
887
+ frequencies: np.ndarray,
888
+ patterns: np.ndarray,
889
+ center_frequency: float,
890
+ title: str = "Pattern vs Frequency",
891
+ min_dB: float = -40.0,
892
+ figsize: Tuple[int, int] = (12, 8)
893
+ ) -> Any:
894
+ """
895
+ Plot radiation patterns at multiple frequencies as a waterfall/heatmap.
896
+
897
+ Parameters
898
+ ----------
899
+ angles : ndarray
900
+ Angle values in degrees
901
+ frequencies : ndarray
902
+ Frequency values in Hz
903
+ patterns : ndarray
904
+ 2D array (n_freq x n_angles) of patterns in dB
905
+ center_frequency : float
906
+ Center frequency for labeling
907
+ title : str
908
+ Plot title
909
+ min_dB : float
910
+ Minimum dB for colormap
911
+ figsize : tuple
912
+ Figure size
913
+
914
+ Returns
915
+ -------
916
+ ax : matplotlib axis
917
+ """
918
+ if not MATPLOTLIB_AVAILABLE:
919
+ raise ImportError("matplotlib is required for this function")
920
+
921
+ fig, ax = plt.subplots(figsize=figsize)
922
+
923
+ # Normalize frequency to percentage
924
+ freq_percent = (frequencies - center_frequency) / center_frequency * 100
925
+
926
+ patterns_clipped = np.clip(patterns, min_dB, 0)
927
+
928
+ im = ax.pcolormesh(angles, freq_percent, patterns_clipped, cmap='jet', shading='auto')
929
+ plt.colorbar(im, ax=ax, label='Gain (dB)')
930
+
931
+ ax.set_xlabel('Angle (degrees)')
932
+ ax.set_ylabel('Frequency Offset (%)')
933
+ ax.set_title(title)
934
+
935
+ return ax
936
+
937
+
938
+ def plot_pattern_vs_frequency_plotly(
939
+ angles: np.ndarray,
940
+ frequencies: np.ndarray,
941
+ patterns: np.ndarray,
942
+ center_frequency: float,
943
+ title: str = "Pattern vs Frequency",
944
+ min_dB: float = -40.0
945
+ ) -> Any:
946
+ """
947
+ Interactive Plotly plot of patterns vs frequency.
948
+
949
+ Parameters
950
+ ----------
951
+ angles : ndarray
952
+ Angle values in degrees
953
+ frequencies : ndarray
954
+ Frequency values in Hz
955
+ patterns : ndarray
956
+ 2D array (n_freq x n_angles) in dB
957
+ center_frequency : float
958
+ Center frequency
959
+ title : str
960
+ Plot title
961
+ min_dB : float
962
+ Minimum dB
963
+
964
+ Returns
965
+ -------
966
+ fig : plotly Figure
967
+ """
968
+ if not PLOTLY_AVAILABLE:
969
+ raise ImportError("plotly is required for this function")
970
+
971
+ freq_percent = (frequencies - center_frequency) / center_frequency * 100
972
+ patterns_clipped = np.clip(patterns, min_dB, 0)
973
+
974
+ fig = go.Figure(data=go.Heatmap(
975
+ x=angles,
976
+ y=freq_percent,
977
+ z=patterns_clipped,
978
+ colorscale='Jet',
979
+ zmin=min_dB,
980
+ zmax=0,
981
+ colorbar=dict(title='Gain (dB)')
982
+ ))
983
+
984
+ fig.update_layout(
985
+ title=title,
986
+ xaxis_title='Angle (degrees)',
987
+ yaxis_title='Frequency Offset (%)'
988
+ )
989
+
990
+ return fig
991
+
992
+
993
+ def plot_subarray_delays(
994
+ architecture,
995
+ delays: np.ndarray,
996
+ title: str = "Subarray Time Delays",
997
+ figsize: Tuple[int, int] = (10, 8)
998
+ ) -> Any:
999
+ """
1000
+ Visualize TTD values across subarrays.
1001
+
1002
+ Parameters
1003
+ ----------
1004
+ architecture : SubarrayArchitecture
1005
+ Subarray architecture with centers
1006
+ delays : ndarray
1007
+ Time delay for each subarray in seconds
1008
+ title : str
1009
+ Plot title
1010
+ figsize : tuple
1011
+ Figure size
1012
+
1013
+ Returns
1014
+ -------
1015
+ ax : matplotlib axis
1016
+ """
1017
+ if not MATPLOTLIB_AVAILABLE:
1018
+ raise ImportError("matplotlib is required for this function")
1019
+
1020
+ fig, ax = plt.subplots(figsize=figsize)
1021
+
1022
+ centers = architecture.subarray_centers
1023
+ delays_ns = delays * 1e9 # Convert to nanoseconds
1024
+
1025
+ scatter = ax.scatter(centers[:, 0], centers[:, 1], c=delays_ns,
1026
+ cmap='viridis', s=200, edgecolors='black')
1027
+ plt.colorbar(scatter, ax=ax, label='Delay (ns)')
1028
+
1029
+ # Add labels
1030
+ for i, (x, y) in enumerate(centers):
1031
+ ax.annotate(f'SA{i}', (x, y), ha='center', va='center', fontsize=8)
1032
+
1033
+ ax.set_xlabel('X (m)')
1034
+ ax.set_ylabel('Y (m)')
1035
+ ax.set_title(title)
1036
+ ax.set_aspect('equal')
1037
+ ax.grid(True, alpha=0.3)
1038
+
1039
+ return ax