Skip to main content

Self-contained Python toolkit for spherical array processing

Project description

spharray

CI

A self-contained Python toolkit for spherical microphone array processing. This repository is the intended open-source root; migration workspaces, local virtual environments, generated reports, and large reference assets should stay outside this directory.

Features

  • Spherical harmonics: complex and real (tesseral) SH basis matrices, ACN/SN3D normalisation, forward and inverse SHTs, coefficient conversions
  • Spatial sampling: Fibonacci, equiangle, and t-design grids with integration weights
  • Array simulation: free-field plane-wave transfer functions
  • Fixed beamforming: cardioid, hypercardioid (max DI), supercardioid (max F/B ratio), MaxEV (perceptual) weight vectors and pattern evaluation
  • Adaptive beamforming: MVDR and LCMV SH-domain beamformers
  • DOA estimation: plane-wave decomposition (PWD) and MUSIC spatial spectra with peak picking
  • Diffuseness estimation: IE, TV, SV, and CMD estimators from FOA/SH covariance
  • Diffuse-field coherence: sinc-based models for omnidirectional sensor arrays
  • Acoustics: spherical Bessel/Hankel functions, modal coefficients for open/rigid arrays
  • Plotting: 3-D array geometry, 2-D spatial maps, MATLAB-like figure style

Installation

Install the current public release from PyPI:

pip install spharray

For development from a local checkout:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
pip install -e ".[dev]"

Python >= 3.11 is required. Core dependencies: numpy, scipy, matplotlib.

Optional extras:

Extra Adds
audio soundfile for WAV I/O
image scikit-image for image processing utilities
dev pytest, build
notebook jupyterlab, ipykernel

Quick start

import numpy as np
import spharray as sap

# ── Spatial sampling ──────────────────────────────────────────────────────────
grid = sap.array.fibonacci_grid(100)   # SphericalGrid, 100 directions
print(f"weights sum: {grid.weights.sum():.4f}")  # ≈ 4π

# ── SH basis and transforms ───────────────────────────────────────────────────
spec = sap.SHBasisSpec(max_order=3, basis="complex", angle_convention="az_colat")
Y  = sap.sh.matrix(spec, grid)          # (100, 16) complex SH matrix
f  = np.random.randn(grid.size)
nm = sap.sh.direct_sht(f, Y, grid)     # (16,) SH coefficients
f_rec = sap.sh.inverse_sht(nm, Y).real # reconstruct

# ── Fixed beamforming ─────────────────────────────────────────────────────────
b       = sap.beamforming.beam_weights_hypercardioid(3)   # max-DI weights, N=3
thetas  = np.linspace(0, np.pi, 181)
pattern = sap.beamforming.axisymmetric_pattern(thetas, b) # B(0) = 1

# ── Array simulation ──────────────────────────────────────────────────────────
sensor_grid = sap.array.fibonacci_grid(32)
geometry    = sap.ArrayGeometry(radius_m=0.042, sensor_grid=sensor_grid)
src_grid    = sap.array.fibonacci_grid(4)
freqs, H    = sap.array.simulate_plane_wave_array_response(
    fft_len=256, fs=16000.0, geometry=geometry, source_grid=src_grid
)
# H.shape = (129, 32, 4)  — (n_bins, n_mics, n_sources)

# ── DOA estimation ────────────────────────────────────────────────────────────
search = sap.array.fibonacci_grid(300)
Q      = spec.n_coeffs                        # (N+1)^2 = 16
R_eye  = np.eye(Q, dtype=complex)             # diffuse covariance → flat spectrum
result = sap.doa.pwd_spectrum(R_eye, search, spec, n_peaks=1)
print("Peak direction (az, el) rad:", result.peak_dirs_rad[0])

# ── MUSIC spectrum ────────────────────────────────────────────────────────────
y0  = sap.sh.matrix(spec, search)[42]         # steering vector at grid index 42
R1  = np.outer(y0, y0.conj()) + 0.01 * np.eye(Q)
res = sap.doa.music_spectrum(R1, search, spec, n_sources=1)
print("MUSIC peak index:", res.peak_indices[0])  # → 42

# ── Diffuseness ───────────────────────────────────────────────────────────────
frame = (np.random.randn(4, 1024) + 1j * np.random.randn(4, 1024))
psi   = sap.diffuseness.diffuseness_ie(frame)
print(f"IE diffuseness mean: {psi.mean():.3f}")

New user path

Start with docs/getting_started.md. It walks through installation, the first runnable example, and the smallest useful SH workflow. Read docs/concepts.md when terms such as ACN, colatitude, modal coefficient, SHT, or unit front gain are not yet clear.

The most useful runnable tutorials are:

python examples/tutorials/01_sht_and_beamforming.py
python examples/tutorials/02_simulated_doa_pipeline.py
python examples/tutorials/03_modal_equalization_pipeline.py

Notebook users can open examples/notebooks/getting_started.ipynb.

Module reference

Module Key symbols
sap.sh matrix, complex_matrix, real_matrix, direct_sht, inverse_sht, acn_index, complex_to_real_coeffs, real_to_complex_coeffs, replicate_per_order
sap.array fibonacci_grid, equiangle_sampling, get_tdesign_fallback, simulate_plane_wave_array_response, spatial_aliasing_frequency, max_sh_order
sap.acoustics besseljs, besseljsd, besselhs, besselhsd, plane_wave_radial_bn, bn_matrix, sph_modal_coeffs, equalize_modal_coeffs
sap.beamforming beam_weights_cardioid, beam_weights_hypercardioid, beam_weights_supercardioid, beam_weights_maxev, axisymmetric_pattern, mvdr_weights, lcmv_weights, steer_sh_weights, beamform_sh
sap.doa pwd_spectrum, music_spectrum, peak_pick_spectrum, spatial_spectrum_from_map, estimate_sh_cov, forward_backward_cov, diagonal_loading
sap.diffuseness diffuseness_ie, diffuseness_tv, diffuseness_sv, diffuseness_cmd, intensity_vectors_from_foa
sap.coherence diffuse_coherence_matrix_omni, diffuse_coherence_from_weights
sap.coords cart_to_sph, sph_to_cart, az_colat_to_azel, azel_to_az_colat, angular_distance, angular_distance_deg
sap.plotting plot_mic_array, plot_directional_map_from_grid, apply_matlab_like_style, figure_style_context

Beamformer normalisation

All beamformers satisfy B(0) = 1 (unit front gain) in the axisymmetric_pattern convention:

B(θ) = Σ_{n=0}^{N} b_n · (2n+1)/(4π) · P_n(cos θ)

The weight vectors are normalised so that Σ b_n · (2n+1)/(4π) = 1.

Beamformer DI (linear) Notes
Cardioid varies Widest main lobe, no sidelobes
Hypercardioid (N+1)² Maximum directivity index
Supercardioid varies Maximum front-to-back energy ratio
MaxEV varies Von-Hann taper, good perceptual quality

SH conventions

Channel ordering follows ACN (Ambisonic Channel Numbering):

index q = n² + n + m,   n = 0…N,   m = -n…n

Complex SH are orthonormal (SN3D normalisation scaled to 4π). Real SH use tesseral form. Converting complex → real → complex is lossless only for conjugate-symmetric inputs (i.e., inputs that correspond to real-valued spatial functions).

Running tests

python -m pytest tests/ -q
# 248 passed

License

MIT. See LICENSE.

References

  • Rafaely, B. (2015). Fundamentals of Spherical Array Processing. Springer.
  • Zotter, F. & Frank, M. (2019). Ambisonics. Springer.
  • Schmidt, R. O. (1986). Multiple emitter location and signal parameter estimation. IEEE Trans. Antennas Propag., 34(3), 276–280.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spharray-0.3.1.tar.gz (107.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spharray-0.3.1-py3-none-any.whl (90.4 kB view details)

Uploaded Python 3

File details

Details for the file spharray-0.3.1.tar.gz.

File metadata

  • Download URL: spharray-0.3.1.tar.gz
  • Upload date:
  • Size: 107.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for spharray-0.3.1.tar.gz
Algorithm Hash digest
SHA256 5502290b203a43ff51fe6a2ea117d29bd5b1b409279b9efed673be8b7db98013
MD5 1c0d46883a8042147fdd3540a1238f60
BLAKE2b-256 3fe336e0f644ae4ef677e1f6cab0b00587ef60f5e3d2ed4577af1caa77ff76a1

See more details on using hashes here.

File details

Details for the file spharray-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: spharray-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 90.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for spharray-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d4b5f976e7f62e48c9c54590612dfcf000e3c98006387f9fc24fa77e5e8ebf4
MD5 0b9c209419155d1519ff8bafbaa3499e
BLAKE2b-256 e095ca0999e232ac986a4445eede0d2896d18f3f3fb2fd9f8076639dd3aca3d4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page