Metadata-Version: 2.4
Name: gri-sigsim
Version: 0.2.3
Summary: Object-oriented RF signal simulation with chainable methods and encapsulated state
Project-URL: Homepage, https://geosolresearch.com
Project-URL: Repository, https://gitlab.com/geosol-foss/python/gri-sigsim
Project-URL: Issues, https://gitlab.com/geosol-foss/python/gri-sigsim/-/issues
Project-URL: Changelog, https://gitlab.com/geosol-foss/python/gri-sigsim/-/releases
Author-email: GeoSol Research Inc <contact@geosolresearch.com>
License-Expression: MIT
License-File: LICENSE
Keywords: RF,TOA,channel,signal-simulation,waveform
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: gri-signal>=0.2.0
Requires-Dist: gri-utils>=0.3.3
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)

# SigSim (Signal Simulation)

Object-oriented RF signal simulation library that wraps gri-signal's functional API. Provides chainable methods, encapsulated state, and self-contained result objects for intuitive simulation workflows. Requires Python 3.12+.

gri-sigsim is to gri-signal what gri-pos is to gri-utils: an OOP layer over functional primitives. The functional API in gri-signal provides composable pure functions for filtering, correlation, and spectral analysis. gri-sigsim wraps these into stateful classes with method chaining, making multi-step simulation workflows more concise and readable.

## Key Features

- **Signal**: Core container with chainable filtering, spectral analysis, and correlation
- **Waveforms**: PulsedWaveform (PRI), PrnWaveform (noise-like), ToneWaveform
- **Channel**: Composable propagation model (delay, Doppler, attenuation, multipath, AWGN)
- **Receiver**: Receive chain with noise figure, ADC quantization, and IQ imbalance
- **Results**: Self-contained PSD and Correlation objects with query methods
- **Estimators**: ToaEstimator with PEAK, QINT, THRESHOLD, and CFAR methods

## Installation

```bash
pip install gri-sigsim
```

## Quick Start

```python
from gri_sigsim import Signal, PulsedWaveform, Channel, Receiver

# Generate a pulsed waveform
waveform = PulsedWaveform(
    freq_hz=10e3,
    pri_interval_s=1e-3,
    pulse_width_s=100e-6,
)
transmitted = waveform.generate(duration_s=0.01, sample_rate_hz=1e6)

# Define channel with builder pattern
channel = (
    Channel()
    .with_delay(3.33e-6)
    .with_doppler(150.0)
    .with_attenuation(80.0)
    .with_awgn(snr_db=15.0)
)

# Define receiver with impairments
receiver = Receiver(
    sample_rate_hz=1e6,
    bandwidth_hz=100e3,
    noise_figure_db=3.0,
    adc_bits=12,
    iq_gain_imbalance_db=0.5,
)

# Receive signal through channel
received = receiver.receive(transmitted, channel)

# Spectral analysis
psd = received.psd()
peak_power, peak_freq = psd.peak()

# TOA estimation via correlation
ref = waveform.generate(duration_s=100e-6, sample_rate_hz=1e6)
toa = received.correlate(ref).estimate_toa()
```

## Signal Processing

```python
from gri_sigsim import Signal
from gri_sigsim._enums import FilterMethod

# Create signal from numpy array
sig = Signal(data, sample_rate_hz=1e6, center_freq_hz=100e6)

# Chainable filtering (butter, fir, or fft methods)
filtered = (
    sig.lowpass(cutoff_hz=10e3, method=FilterMethod.BUTTER)
    .highpass(cutoff_hz=1e3)
    .bandpass(low_hz=2e3, high_hz=8e3)
)

# Spectral analysis
psd = sig.psd()
snr = sig.estimate_snr()
bandwidth = sig.effective_bandwidth()

# Correlation
corr = sig.correlate(reference_sig)
delay_s = corr.peak_delay_s
refined_delay = corr.peak_delay_refined_s()  # Sub-sample precision
```

## Waveform Generation

```python
from gri_sigsim import PulsedWaveform, PrnWaveform, ToneWaveform

# Pulsed waveform (PRI-based)
pulsed = PulsedWaveform(
    freq_hz=10e3,
    pri_interval_s=1e-3,
    pulse_width_s=100e-6,
)
sig = pulsed.generate(duration_s=0.1, sample_rate_hz=1e6)

# PRN (noise-like) waveform
prn = PrnWaveform(hz_low=1e3, hz_high=10e3)
sig = prn.generate(duration_s=0.1, sample_rate_hz=100e3, seed=42)

# Tone waveform
tone = ToneWaveform(freq_hz=1000.0, amplitude=1.0, phase_rad=0.0)
sig = tone.generate(duration_s=0.1, sample_rate_hz=10e3)
```

## Channel Modeling

```python
from gri_sigsim import Channel

# Builder pattern for channel composition
channel = (
    Channel()
    .with_delay(1e-3)           # Propagation delay
    .with_doppler(100.0)        # Frequency shift
    .with_attenuation(80.0)     # Path loss in dB
    .with_awgn(snr_db=20.0)     # Additive noise
    .with_multipath(            # Multipath reflections
        delays_s=[0, 1e-6, 2e-6],
        amplitudes=[1.0, 0.5, 0.25],
    )
)

# Factory method for common case
channel = Channel.free_space(
    delay_s=1e-3,
    doppler_hz=100.0,
    attenuation_db=80.0,
)

# Apply to signal
received = channel.apply(transmitted)
```

## TOA Estimation

```python
from gri_sigsim.estimators import ToaEstimator, ToaMethod

# Simple TOA from correlation
corr = signal.correlate(reference)
toa = corr.estimate_toa()

# Advanced estimation with configurable method
estimator = ToaEstimator(
    method=ToaMethod.CFAR,  # or PEAK, QINT, THRESHOLD
    guard_cells=4,
    ref_cells=16,
)
toa = estimator.estimate_from_signals(signal, reference)
```

## Future Integration with gri-geosim

gri-sigsim is designed for future integration with gri-geosim for geometry-driven simulations:

```python
# Future implementation in gri-geosim
class RfScenario(Scenario):
    def simulate_rf(self, waveform, receiver, time) -> list[Signal]:
        for platform, emitter in self._pairs():
            geometry = self._compute_geometry(platform, emitter, time)
            channel = Channel.free_space(
                delay_s=geometry.range_m / C,
                doppler_hz=geometry.doppler_hz,
                attenuation_db=geometry.path_loss_db,
            )
            sig = waveform.generate(...)
            received = receiver.receive(sig, channel)
            results.append(received)
        return results
```

## Dependencies

This is a Layer 3 application-level package in the GRI FOSS ecosystem:

**Direct dependencies:**

- **gri-signal**: Functional signal processing (filters, FFT, correlation)
- **numpy**, **scipy**: Numerical computing

**Future integrations:**

- **gri-geosim**: Platform geometry for realistic channel modeling
- **gri-tropo**: Tropospheric delay effects
- **gri-iono**: Ionospheric delay effects


## Other Projects

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

## License

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