feat: 完成阵列抽象类, 常见阵列初始化函数开发

This commit is contained in:
2025-07-04 15:12:07 +08:00
parent bf8d7fd556
commit 148eb810ad
3 changed files with 560 additions and 50 deletions
+228
View File
@@ -0,0 +1,228 @@
import numpy as np
from typing import Tuple, Optional
from .base_array import *
def create_ula(
elem_num: int,
spacing: float,
position_errors: Optional[np.ndarray] = None,
gain_errors: Optional[np.ndarray] = None,
phase_errors: Optional[np.ndarray] = None
) -> Array1D:
"""
Create a Uniform Linear Array (ULA) along the x-axis with specified element count and spacing.
Args:
elem_num (int): Number of elements M (must be a positive integer).
spacing (float): Distance between adjacent elements (in meters or wavelength units, must be > 0).
position_errors (Optional[np.ndarray]): Optional perturbation offsets for each element, (M,), (2, M), or (3, M).
gain_errors (Optional[np.ndarray]): Optional gain error factors (amplitude scale), shape (M,).
phase_errors (Optional[np.ndarray]): Optional phase errors (in radians), shape (M,).
Returns:
Array1D: Initialized ULA object with 1D position array.
Raises:
ValueError: If elem_num is not a positive integer or spacing is not positive.
Notes:
- Element positions are on the x-axis only.
- `positions` is a 1D NumPy array of shape (M,), i.e., scalar x-coordinates.
"""
if not isinstance(elem_num, int) or elem_num <= 0:
raise ValueError(f"elem_num must be a positive integer, got {elem_num}.")
if not isinstance(spacing, (float, int)) or spacing <= 0:
raise ValueError(f"spacing must be a positive number, got {spacing}.")
# Uniform 1D positions: [0, spacing, ..., (M-1)*spacing]
indices = np.arange(elem_num)
positions = spacing * indices # shape: (M,)
return Array1D(
positions=positions,
type=ArrayType.ULA,
spacing=spacing,
position_errors=position_errors,
gain_errors=gain_errors,
phase_errors=phase_errors
)
def create_ura(
array_size: Tuple[int, int],
spacing: float,
position_errors: Optional[np.ndarray] = None,
gain_errors: Optional[np.ndarray] = None,
phase_errors: Optional[np.ndarray] = None
) -> Array2D:
"""
Create a Uniform Rectangular Array (URA) in the xy-plane.
The array consists of elements placed on a 2D rectangular grid, with uniform
spacing in both x and y directions. The array lies entirely in the z=0 plane.
Args:
array_size (Tuple[int, int]): Number of elements along (cols, rows), i.e., (x-axis, y-axis).
The total number of elements is M = cols * rows.
spacing (float): Distance between adjacent elements (in meters or wavelength units).
position_errors (Optional[np.ndarray]): Optional position perturbations of shape (2, M) or (3, M).
gain_errors (Optional[np.ndarray]): Optional gain errors of shape (M,).
phase_errors (Optional[np.ndarray]): Optional phase errors (in radians), shape (M,).
Returns:
Array2D: Initialized uniform rectangular array object.
Raises:
ValueError: If array dimensions are not positive integers or spacing is not positive.
Notes:
- Output positions are of shape (2, M), each column is [x; y] of an element.
- Elements are ordered row-first: for each row, elements increase left to right (x).
- The array is centered at the origin only if manually shifted after creation.
"""
num_cols, num_rows = array_size
if not isinstance(num_cols, int) or num_cols <= 0 or not isinstance(num_cols, int) or num_cols <= 0:
raise ValueError(f"array_size must be two positive integers, got ({num_cols}, {num_cols}).")
if not isinstance(spacing, (float, int)) or spacing <= 0:
raise ValueError(f"spacing must be positive, got {spacing}.")
total_elements = num_cols * num_rows
# Generate 2D grid coordinates in xy-plane
x_coords = spacing * np.arange(num_cols) # x: fast-changing axis
y_coords = spacing * np.arange(num_rows) # y: slow-changing axis
xv, yv = np.meshgrid(x_coords, y_coords) # shape (cols, rows)
# Flatten and stack into (2, M) position matrix
positions = np.zeros((2, total_elements), dtype=float)
positions[0, :] = xv.flatten() # x-axis
positions[1, :] = yv.flatten() # y-axis
return Array2D(
positions=positions,
type=ArrayType.URA,
spacing=spacing,
position_errors=position_errors,
gain_errors=gain_errors,
phase_errors=phase_errors
)
def create_uca(
elem_num: int,
radius: float,
position_errors: Optional[np.ndarray] = None,
gain_errors: Optional[np.ndarray] = None,
phase_errors: Optional[np.ndarray] = None
) -> Array2D:
"""
Create a Uniform Circular Array (UCA) centered at the origin in the xy-plane.
The array elements are evenly spaced along the circumference of a circle,
with the first element at angle 0 (x-axis), and proceeding counterclockwise.
Args:
elem_num (int): Number of elements M (must be a positive integer).
radius (float): Radius of the circular array (in meters or wavelength units, must be > 0).
position_errors (Optional[np.ndarray]): Optional position perturbations, shape (2, M) or (3, M).
gain_errors (Optional[np.ndarray]): Optional gain error factors (amplitude scale), shape (M,).
phase_errors (Optional[np.ndarray]): Optional phase errors (in radians), shape (M,).
Returns:
Array2D: Initialized array object with circular geometry.
Raises:
ValueError:
- If elem_num is not a positive integer.
- If radius is not a positive number.
Notes:
- The output positions are of shape (2, M), representing [x; y] coordinates.
- The nominal element spacing (along the arc) is returned as the 'spacing' field.
- The array lies entirely in the xy-plane (z = 0 is implied).
"""
if not isinstance(elem_num, int) or elem_num <= 0:
raise ValueError(f"elem_num ({elem_num}) must be a positive integer.")
if not isinstance(radius, (float, int)) or radius <= 0:
raise ValueError(f"radius ({radius}) must be a positive number.")
# Compute angular positions (uniform spacing)
theta = np.linspace(0, 2 * np.pi, elem_num, endpoint=False)
# Compute Cartesian coordinates in 2D
x = radius * np.cos(theta)
y = radius * np.sin(theta)
# Store as 2×N position matrix: positions[0, :] = x, positions[1, :] = y
positions = np.zeros((2, elem_num), dtype=float)
positions[0, :] = x
positions[1, :] = y
# Compute approximate arc-length spacing between adjacent elements
step = 2 * np.pi / elem_num
spacing = 2 * radius * np.sin(step / 2) # chord length ≈ arc length for small step
return Array2D(
positions=positions,
type=ArrayType.UCA,
spacing=spacing,
position_errors=position_errors,
gain_errors=gain_errors,
phase_errors=phase_errors
)
def create_cyl_array_with_uca(
elem_per_layer: int,
radius: float,
num_layers: int,
layer_spacing: float,
gain_errors: Optional[np.ndarray] = None,
phase_errors: Optional[np.ndarray] = None,
position_errors: Optional[np.ndarray] = None
) -> Array3D:
"""
Create a cylindrical array composed of stacked UCAs along the z-axis.
Args:
elem_per_layer (int): Number of elements per circular UCA layer (must be > 0).
radius (float): Radius of each UCA layer (must be > 0).
num_layers (int): Number of stacked layers along z-axis (must be > 0).
layer_spacing (float): Distance between adjacent layers along z-axis (must be > 0).
gain_errors (Optional[np.ndarray]): Optional gain errors, shape (M,).
phase_errors (Optional[np.ndarray]): Optional phase errors, shape (M,).
position_errors (Optional[np.ndarray]): Optional position errors, shape (3, M).
Returns:
Array3D: Array object with cylindrical geometry.
Raises:
ValueError: If any input is invalid (non-positive integer or float).
"""
if elem_per_layer <= 0 or num_layers <= 0:
raise ValueError("elem_per_layer and num_layers must be positive integers.")
if radius <= 0:
raise ValueError("radius must be a positive number.")
if layer_spacing <= 0:
raise ValueError("layer_spacing must be a positive number.")
total_elements = elem_per_layer * num_layers
positions = np.zeros((3, total_elements), dtype=float)
theta = np.linspace(0, 2 * np.pi, elem_per_layer, endpoint=False)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
for i in range(num_layers):
z = layer_spacing * i
idx_start = i * elem_per_layer
idx_end = (i + 1) * elem_per_layer
positions[0, idx_start:idx_end] = x
positions[1, idx_start:idx_end] = y
positions[2, idx_start:idx_end] = z
return Array3D(
positions=positions,
type=ArrayType.CYLINDRICAL,
spacing=layer_spacing,
position_errors=position_errors,
gain_errors=gain_errors,
phase_errors=phase_errors
)