Metadata-Version: 2.4
Name: lora-receiver
Version: 0.1.0
Summary: LoRa satellite receiver - sync, demodulate, and decode
Author: ShayanMajumder
License-Expression: MIT
Project-URL: Homepage, https://github.com/ShayanMajumder/lora-sat-receiver
Project-URL: Repository, https://github.com/ShayanMajumder/lora-sat-receiver
Project-URL: Bug Tracker, https://github.com/ShayanMajumder/lora-sat-receiver/issues
Keywords: lora,satellite,sdr,chirp-spread-spectrum,gnuradio
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Ham Radio
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Dynamic: license-file

# lora-receiver

A pure-Python LoRa satellite receiver implementing the 3-stage synchronization algorithm from [Xhonneux, Dallemagne, et al. (2021)](https://arxiv.org/abs/2106.03022).

## Features

- **Preamble detection** in complex baseband IQ recordings
- **3-stage synchronizer**: Carrier Frequency Offset (CFO) and Symbol Timing Offset (STO) estimation and correction
- **Demodulation** of LoRa symbols from corrected signals
- **Packet decoding** with both explicit headers (standard LoRa) and implicit headers (no header, forced parameters)
- **Multi-packet detection** using a sliding-window approach across a single IQ recording
- **CRC-16 verification** for payload integrity

## Installation

```bash
pip install lora-receiver
```

Requires Python 3.9+.

## Quick Start

```python
import numpy as np
from lora_receiver import LoRaDecoder

# Load IQ samples (complex64)
iq = np.fromfile("recording.iq", dtype=np.complex64)

# Create decoder for SF=10, BW=125 kHz, fs=125 kHz, fc=437 MHz
decoder = LoRaDecoder(sf=10, bw=125_000, fs=125_000, fc=437e6)

# Run full decode pipeline
result = decoder.full_decode(iq)

# Inspect results
print("Payload (raw):", result["payload_bytes"])
print("Payload (text):", result.get("payload_text", ""))
print("CRC passed:", result["crc_ok"])
```

### Multi-packet decoding

```python
from lora_receiver import detect_packets

results = detect_packets(iq, sf=10, bw=125_000, fs=125_000, fc=437e6)
for i, r in enumerate(results):
    print(f"Packet {i}: CRC={'PASS' if r['crc_ok'] else 'FAIL'}, "
          f"start={r['sample_start']}, SNR={r.get('snr_est', 'N/A')}")
```

## API Overview

### `LoRaDecoder(sf, bw, fs, fc, ...)`

| Parameter | Description |
|-----------|-------------|
| `sf` | Spreading factor (7-12) |
| `bw` | Bandwidth in Hz |
| `fs` | Sampling rate in Hz |
| `fc` | Center frequency in Hz |
| `N_detect` | Preamble detection windows (default: 4) |
| `N_preamble_up` | Number of preamble upchirps (default: 8) |
| `N_netid` | Net ID upchirps (default: 0) |
| `N_sfd_down` | SFD downchirps (default: 2.25) |

### `LoRaDecoder.full_decode(iq)` -> dict

Returns a dict with keys: `sf`, `bw`, `payload_bytes`, `payload_text`, `crc_ok`, `crc_calculated`, `header`, `sync_results`, `snr_est`, `sample_start`.

### `LoRaDecoder.sync(iq)` -> (corrected_iq, sync_results)

Runs the 3-stage synchronizer and returns frequency-corrected, time-aligned IQ.

### `detect_packets(iq, sf, bw, fs, fc, ...)` -> list[dict]

Sliding-window multi-packet detector. Returns a list of `full_decode` result dicts.

## Dependencies

- `numpy` -- array operations and FFT
- `scipy` -- signal resampling

## License

MIT -- see [LICENSE](LICENSE).
