Metadata-Version: 2.4
Name: gri-signal
Version: 0.2.3
Summary: Signal processing for filtering, spectral analysis, correlation, and beamforming
Project-URL: Homepage, https://geosolresearch.com
Project-URL: Repository, https://gitlab.com/geosol-foss/python/gri-signal
Project-URL: Issues, https://gitlab.com/geosol-foss/python/gri-signal/-/issues
Project-URL: Changelog, https://gitlab.com/geosol-foss/python/gri-signal/-/releases
Author-email: GeoSol Research Inc <contact@geosolresearch.com>
License-Expression: MIT
License-File: LICENSE
Keywords: FFT,beamforming,correlation,filtering,signal-processing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.12
Requires-Dist: numpy>=2.3.3
Requires-Dist: scipy>=1.16.2
Description-Content-Type: text/markdown

[![GeoSol Research Logo](https://geosolresearch.com/logos/foss_logo.png "GeoSol Research")](https://geosolresearch.com)

# Signal Processing Library

Signal processing utilities for signal generation, channel simulation, modulation, sampling, filtering, spectral analysis, correlation, beamforming, pulse detection, and link-budget computation. Requires Python 3.12+.

This library provides functional signal processing tools designed to be composable and efficient. All functions support both 1D and multi-channel (row-major: channels x samples) signals where applicable.

## Mathematical Background

**Filter design tradeoffs.** Three filter implementations are provided for each filter type, each with different characteristics:

- **Butterworth (IIR)**: Maximally flat passband response. Uses zero-phase filtering (`sosfiltfilt`) for zero phase distortion. Lowest order for a given transition width, but infinite impulse response means feedback.
- **FIR**: Linear phase, no feedback (always stable). Hamming window design. Higher order than IIR for equivalent stopband attenuation, but predictable group delay.
- **FFT-domain**: Steepest possible cutoff (brick-wall in frequency). Applied via multiplication in the frequency domain with Hann smoothing at transitions. Best rejection but assumes periodic signals.

**Power spectral density.** PSD estimates the power distribution across frequency, computed via `|FFT(x)|^2 / N` and reported in dB.

**Cross-correlation for delay estimation.** The cross-correlation `R_xy[k] = sum(x[n] * conj(y[n-k]))` peaks at the lag corresponding to the time delay between signals. Quadratic interpolation (`qint`) refines the peak location to sub-sample precision.

References: Oppenheim & Willsky, "Signals and Systems"; Proakis & Manolakis, "Digital Signal Processing."

## Installation

```bash
pip install gri-signal
```

For development:

```bash
git clone https://gitlab.com/geosol-foss/python/gri-signal.git
cd gri-signal
. .init_venv.sh
```

## Signal Flow

The library is organized around the standard signal processing pipeline:

```text
sources -> modulation -> channel -> sampling -> [filters/spectral/correlation/beamforming]
```

## Sources

Signal generation functions for creating test signals and waveforms:

- **`tone(fs, freq, ...)`**: Complex sinusoidal tone with optional windowing
- **`prn(fs, ...)`**: Pseudorandom noise (white or bandlimited)
- **`pri(fs, freq, pri_interval, pulse_width, ...)`**: Pulsed signal with constant PRI
- **`zeros(fs, ...)`**: Zero-filled signal array
- **`ula_steering(aoa_deg, d, wavelength, n_elements)`**: ULA steering vector for multi-channel signal generation
- **`generate_complex_signal(...)`**: Multi-component test signal with tones, chirps, AM, PM

## Channel

Propagation effects for simulating signal transmission:

- **`delay(sig, delay_time, fs)`**: Sub-sample precision time delay via FFT
- **`awgn(sig, snr_db)`**: Additive white Gaussian noise
- **`attenuation(sig, db)`**: Amplitude scaling (gain or loss)
- **`doppler(sig, fs, f_shift, *, f_rate=0.0)`**: Doppler frequency shift with optional linear drift
- **`multipath(sig, fs, delays, amplitudes)`**: Tapped delay line multipath channel

## Modulation

Analog modulation schemes:

- **`mod_am(sig, fs, f_carrier, *, mod_index=1.0)`**: Amplitude modulation
- **`demod_am(sig, fs, f_carrier)`**: AM envelope detection
- **`mod_fm(sig, fs, f_carrier, *, freq_deviation)`**: Frequency modulation
- **`demod_fm(sig, fs, f_carrier, *, freq_deviation)`**: FM frequency discrimination
- **`mod_pm(sig, fs, f_carrier, *, phase_deviation)`**: Phase modulation
- **`demod_pm(sig, fs, f_carrier, *, phase_deviation)`**: PM phase extraction

Digital modulation schemes (baseband, rectangular pulses):

- **`mod_bpsk(bits, fs, symbol_rate)`**: Binary Phase Shift Keying (1 bit/symbol)
- **`demod_bpsk(sig, fs, symbol_rate)`**: BPSK hard decision
- **`mod_qpsk(bits, fs, symbol_rate)`**: Quadrature PSK (2 bits/symbol, Gray coded)
- **`demod_qpsk(sig, fs, symbol_rate)`**: QPSK hard decision
- **`mod_qam(bits, fs, symbol_rate, *, order=16)`**: QAM (4/16/64/256, Gray coded)
- **`demod_qam(sig, fs, symbol_rate, *, order=16)`**: QAM nearest-neighbor decision

IQ conversion:

- **`passband_to_iq(sig, fs, f_center)`**: Real passband to complex baseband
- **`iq_to_passband(sig, fs, f_center)`**: Complex baseband to real passband

## Sampling

Digitization and quantization:

- **`adc(sig, n_bits)`**: Simulate n-bit analog-to-digital converter

## Filters

### Lowpass Filters

Three implementations with different tradeoffs:

- **`lowpass_butter`**: Butterworth IIR filter (scipy SOS, zero-phase via sosfiltfilt)
- **`lowpass_fir`**: FIR filter (Hamming window design, less phase ringing)
- **`lowpass_fft`**: FFT-domain filter (steepest cutoff, Hann smoothing)

### Highpass Filters

- **`highpass_butter`**: Butterworth highpass
- **`highpass_fir`**: FIR highpass
- **`highpass_fft`**: FFT-domain highpass

### Bandpass Filters

- **`bandpass_butter`**: Butterworth bandpass
- **`bandpass_fir`**: FIR bandpass
- **`bandpass_fft`**: FFT-domain bandpass

### Notch (Bandstop) Filters

Attenuate a frequency band while passing all others (useful for interference rejection):

- **`notch_butter`**: Butterworth notch/bandstop
- **`notch_fir`**: FIR notch/bandstop
- **`notch_fft`**: FFT-domain notch/bandstop

### Analytic Bandpass Filters

Complex-valued bandpass filters returning analytic signals for single-sideband processing:

- **`analytic_bandpass_butter`**: Butterworth-based analytic
- **`analytic_bandpass_fir`**: FIR-based analytic
- **`analytic_bandpass_fft`**: FFT-based analytic (handles negative/positive frequencies)

## Spectral Analysis

PSD computation and frequency mapping:

- **`get_psd(fs, sig)`**: Power Spectral Density (dB-scaled, shifted frequency)
- **`get_max_power(fs, sig)`**: Find dominant frequency and power
- **`get_psd_power(f, psd, freq)`**: Query PSD at specific frequency
- **`get_freq_idx(f, fs, n)`**: Frequency-to-bin mapping

Signal quality metrics:

- **`estimate_snr(fs, sig, *, signal_bw, signal_center)`**: Estimate SNR in dB
- **`estimate_noise_floor(fs, sig, *, percentile)`**: Estimate noise floor in dB
- **`sinad(fs, sig, *, fundamental_freq)`**: Signal-to-Noise and Distortion ratio
- **`thd(fs, sig, *, fundamental_freq, num_harmonics)`**: Total Harmonic Distortion

Time-frequency analysis:

- **`effective_bandwidth(fs, sig, *, method)`**: RMS or threshold-based bandwidth in Hz
- **`effective_duration(fs, sig, *, method)`**: RMS or threshold-based duration in seconds
- **`estimate_tbp(fs, sig, *, method)`**: Time-bandwidth product (dimensionless)

## Correlation

- **`xcorr(sig1, sig2, max_lags=None)`**: MATLAB-style cross-correlation with lags
- **`qint(x3p, y3p)`**: Quadratic interpolation for sub-sample peak resolution

## Beamforming

Single-section processing:

- **`adaptive_beamform(sig)`**: Adaptive beamforming with spatial covariance and eigenvalue decomposition
- **`steering_angles(steering_vecs)`**: Compute steering angles from eigenvectors

Batch processing (for multiple sections):

- **`batch_adaptive_beamform(sig)`**: Vectorized beamforming for all sections
- **`batch_steering_angles(steering_vecs)`**: Batch steering angle computation

## Detection

Threshold-based pulse detection on IQ envelopes and scoring against ground-truth labels:

- **`detect_pulses(iq, sample_rate_hz, min_pri_s, *, threshold_frac=0.4)`**: Returns sample indices of detected peaks. Uses `scipy.signal.find_peaks` with PRI-derived minimum separation and zero-padded boundary handling.
- **`match_detections(detected_indices, truth_starts, ...)`**: Returns a `DetectionResult` with matched/missed/false-alarm counts, per-match timing errors, and a `.detection_rate` accessor.

## Link Budget

Standard link-equation primitives in the dB domain:

- **`eirp_dbw(peak_power_w, antenna_gain_dbi)`**: Effective isotropic radiated power (dBW).
- **`fspl_db(range_m, freq_hz)`**: Free-space path loss (dB).
- **`noise_power_dbw(noise_temp_k, bandwidth_hz)`**: Thermal noise power `N = k T B` (dBW).
- **`received_power_dbw(eirp_dbw, range_m, freq_hz, *, atmo_loss_db=0, collector_gain_dbi=0)`**: Received power after FSPL, atmospheric loss, and collector gain.
- **`multipath_received_power_dbw(...)`, `multipath_relative_db(...)`**: Multipath reflection variants.
- **`snr_db(received_power_dbw, noise_power_dbw)`**: Signal-to-noise ratio.

Constants exported alongside: `C` (speed of light), `K_BOLTZMANN`, `K_BOLTZMANN_DB`.

## Example Usage

```python
from gri_signal import (
    # Sources
    tone, prn, pri, ula_steering,
    # Channel
    delay, awgn, attenuation, doppler, multipath,
    # Sampling
    adc,
    # Filters
    lowpass_butter, bandpass_fft,
    # Analysis
    get_psd, xcorr, qint,
)

# Generate a 100 Hz tone
fs = 10e3  # 10 kHz sample rate
sig = tone(fs, 100, duration=1.0)

# Add channel effects
sig = delay(sig, 0.001, fs)  # 1ms delay
sig = doppler(sig, fs, 50.0)  # 50 Hz Doppler shift
sig = awgn(sig, 20.0)  # 20 dB SNR
sig = attenuation(sig, 6.0)  # 6 dB loss

# Digitize
sig = adc(sig, 12)  # 12-bit ADC

# Filter
filtered = lowpass_butter(fs, 200, sig)  # 200 Hz cutoff

# Analyze
freqs, psd_db = get_psd(fs, filtered)

# Generate multi-channel array signal from a source at 30 degrees
wavelength = 3e8 / 915e6  # 915 MHz
steering = ula_steering(30.0, wavelength/2, wavelength, n_elements=4)
array_sig = steering[:, None] * sig  # (4, n_samples)
```

## Design Patterns

All functions follow consistent conventions:

- **Functional**: Pure functions, no classes (composable, testable)
- **Signature**: `func(fs, params, sig, *, optional_params)` for processing functions
- **Input/Output**: Signal arrays with same shape preserved
- **Safety**: Uses `sig.copy()` where needed to avoid modifying original data
- **Multi-channel**: Row-major format (channels as rows)

## Dependencies

- **numpy**: Array operations
- **scipy**: Filter design and signal processing


## Other Projects

Current list of other [GRI FOSS Projects](https://gitlab.com/geosol-foss/python/gri-signal/-/blob/main/.docs_other_projects.md) we are building and maintaining.

## License

MIT License. See [LICENSE](https://gitlab.com/geosol-foss/python/gri-signal/-/blob/main/LICENSE) for details.
