adsb-generator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adsb_generator-0.1.0/LICENSE +21 -0
- adsb_generator-0.1.0/PKG-INFO +223 -0
- adsb_generator-0.1.0/README.md +194 -0
- adsb_generator-0.1.0/pyproject.toml +47 -0
- adsb_generator-0.1.0/setup.cfg +4 -0
- adsb_generator-0.1.0/src/adsb_generator/__init__.py +9 -0
- adsb_generator-0.1.0/src/adsb_generator/algorithms.py +84 -0
- adsb_generator-0.1.0/src/adsb_generator/channel.py +248 -0
- adsb_generator-0.1.0/src/adsb_generator/encoder.py +155 -0
- adsb_generator-0.1.0/src/adsb_generator/generator.py +85 -0
- adsb_generator-0.1.0/src/adsb_generator/message.py +198 -0
- adsb_generator-0.1.0/src/adsb_generator.egg-info/PKG-INFO +223 -0
- adsb_generator-0.1.0/src/adsb_generator.egg-info/SOURCES.txt +22 -0
- adsb_generator-0.1.0/src/adsb_generator.egg-info/dependency_links.txt +1 -0
- adsb_generator-0.1.0/src/adsb_generator.egg-info/requires.txt +4 -0
- adsb_generator-0.1.0/src/adsb_generator.egg-info/top_level.txt +1 -0
- adsb_generator-0.1.0/tests/test_altitude.py +71 -0
- adsb_generator-0.1.0/tests/test_channel.py +893 -0
- adsb_generator-0.1.0/tests/test_cpr.py +91 -0
- adsb_generator-0.1.0/tests/test_crc.py +110 -0
- adsb_generator-0.1.0/tests/test_encoder.py +383 -0
- adsb_generator-0.1.0/tests/test_generator.py +214 -0
- adsb_generator-0.1.0/tests/test_message.py +478 -0
- adsb_generator-0.1.0/tests/test_trk.py +75 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Hamri (adamhamri9)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adsb-generator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: `adsb-generator` produces realistic I/Q (In-phase/Quadrature) samples of clean and impaired ADS-B (Mode S Downlink Format 17) baseband signals, along with the raw 112-bit message and all applied parameters. It implements an infinite iterator that streams reproducible samples with full control over message type distributions, transmission parameters, and channel impairments.
|
|
5
|
+
Author-email: Adam Hamri <adamhamri9@proton.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adamhamri9/adsb-datagen
|
|
8
|
+
Project-URL: Repository, https://github.com/adamhamri9/adsb-datagen
|
|
9
|
+
Project-URL: Issues, https://github.com/adamhamri9/adsb-datagen/issues
|
|
10
|
+
Keywords: adsb,mode-s,aviation,signal-generation,iq-samples,baseband
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Topic :: Communications :: Ham Radio
|
|
22
|
+
Requires-Python: <3.14,>=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: numpy>=1.26.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# adsb-generator
|
|
31
|
+
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
|
|
35
|
+
**Synthetic ADS-B data generator.**
|
|
36
|
+
|
|
37
|
+
`adsb-generator` produces realistic I/Q (In-phase/Quadrature) samples of clean and impaired ADS-B (Mode S Downlink Format 17) baseband signals, along with the raw 112-bit message and all applied parameters. It implements an infinite iterator that streams reproducible samples with full control over message type distributions, transmission parameters, and channel impairments.
|
|
38
|
+
|
|
39
|
+
## Key Features
|
|
40
|
+
|
|
41
|
+
- **End-to-end pipeline**: random message generation, PPM encoding, and RF channel simulation in a single call.
|
|
42
|
+
- **Four ADS-B message types**: identification, surface position, airborne position, and airborne velocity with configurable emission probabilities.
|
|
43
|
+
- **Realistic channel impairments**: Gaussian Noise(both AWGN and Correlated) , frequency & phase offset, IQ imbalance, and DC offset, all sampled from configurable probability distributions.
|
|
44
|
+
- **Reproducibility**: deterministic output via a shared seed across all pipeline stages.
|
|
45
|
+
- **Configurable distributions**: override any transmission or channel parameter distribution to model specific receiver conditions or hardware behavior.
|
|
46
|
+
- **NumPy-native**: all signals are `np.complex64` arrays, ready for direct use with any downstream processing tool.
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
|
|
50
|
+
- Python 3.10+
|
|
51
|
+
- NumPy
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install adsb-generator
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from adsb_generator import ADSBGenerator
|
|
63
|
+
|
|
64
|
+
# Create a generator with a fixed seed for reproducibility
|
|
65
|
+
gen = ADSBGenerator(seed=42)
|
|
66
|
+
|
|
67
|
+
# Each iteration yields an ADSBSample with the raw message,
|
|
68
|
+
# clean signal, and channel-impaired signal
|
|
69
|
+
for sample in gen:
|
|
70
|
+
print(f"Message type : {sample.message_type.value}")
|
|
71
|
+
print(f"Raw message : {sample.message:#028x}")
|
|
72
|
+
print(f"Clean signal : {sample.clean_signal.shape} complex64 samples")
|
|
73
|
+
print(f"Noisy signal : {sample.channel_signal.shape} complex64 samples")
|
|
74
|
+
print(f"SNR (dB) : {sample.channel_params['snr_db']:.1f}")
|
|
75
|
+
print(f"Amplitude : {sample.tx_params['amplitude']:.3f}")
|
|
76
|
+
break
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Customizing Distributions
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from adsb_generator import ADSBGenerator, MessageType, ChannelParams
|
|
83
|
+
|
|
84
|
+
# Favor airborne positions, restrict SNR to low-moderate range
|
|
85
|
+
gen = ADSBGenerator(
|
|
86
|
+
message_type_probs={
|
|
87
|
+
MessageType.AIRBORNE_POSITION: 0.60,
|
|
88
|
+
MessageType.AIRBORNE_VELOCITY: 0.20,
|
|
89
|
+
MessageType.IDENTIFICATION: 0.10,
|
|
90
|
+
MessageType.SURFACE_POSITION: 0.10,
|
|
91
|
+
},
|
|
92
|
+
channel_params_distributions={
|
|
93
|
+
ChannelParams.SNR_DB: [
|
|
94
|
+
[3.0, 8.0, 0.70],
|
|
95
|
+
[8.0, 15.0, 0.30],
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
sample_rate=2e6,
|
|
99
|
+
seed=12345,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
sample = next(gen)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## API Reference
|
|
106
|
+
|
|
107
|
+
### `ADSBGenerator`
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
ADSBGenerator(
|
|
111
|
+
message_type_probs: dict[MessageType | str, float] | None = None,
|
|
112
|
+
tx_params_distributions: dict[TXParams | str, list[list[float]]] | None = None,
|
|
113
|
+
channel_params_distributions: dict[ChannelParams | str, list[list[float]]] | None = None,
|
|
114
|
+
sample_rate: float = 2e6,
|
|
115
|
+
seed: int | None = None,
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Parameter | Type | Default | Description |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
| `message_type_probs` | `dict` or `None` | Equal 25% per type | Mapping of `MessageType` to emission probabilities (must sum to 1.0). |
|
|
122
|
+
| `tx_params_distributions` | `dict` or `None` | Single amplitude band | Mapping of `TXParams` to `[[min, max, weight], ...]` intervals. |
|
|
123
|
+
| `channel_params_distributions` | `dict` or `None` | Typical ADS-B conditions | Mapping of `ChannelParams` to `[[min, max, weight], ...]` intervals. |
|
|
124
|
+
| `sample_rate` | `float` | `2e6` | Sampling rate in samples per second. |
|
|
125
|
+
| `seed` | `int` or `None` | Random | Seed for deterministic output across all pipeline stages. |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
### `ADSBSample`
|
|
130
|
+
|
|
131
|
+
Dataclass returned by each iteration of `ADSBGenerator`.
|
|
132
|
+
|
|
133
|
+
| Field | Type | Description |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `message` | `int` | Complete 112-bit ADS-B message (including 24-bit CRC) as an integer. |
|
|
136
|
+
| `message_type` | `MessageType` | The type of ADS-B message generated. |
|
|
137
|
+
| `clean_signal` | `np.ndarray` | Complex baseband I/Q signal before channel impairments (`complex64`). |
|
|
138
|
+
| `tx_params` | `dict[TXParams, float]` | Transmission parameters applied during encoding. |
|
|
139
|
+
| `channel_signal` | `np.ndarray` | Complex baseband I/Q signal after channel impairments (`complex64`). |
|
|
140
|
+
| `channel_params` | `dict[ChannelParams, float]` | Channel parameters applied to the signal. |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `MessageType`
|
|
145
|
+
|
|
146
|
+
| Value | Description |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `IDENTIFICATION` | Aircraft identification (callsign) messages (TC 1--4). |
|
|
149
|
+
| `SURFACE_POSITION` | Surface position messages (TC 5--8). |
|
|
150
|
+
| `AIRBORNE_POSITION` | Airborne position messages (TC 9--18, 20--22). |
|
|
151
|
+
| `AIRBORNE_VELOCITY` | Airborne velocity messages (TC 19). |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### `ADSBEncoder`
|
|
156
|
+
|
|
157
|
+
Encodes 112-bit messages into complex baseband I/Q samples using PPM per the Mode S standard.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
ADSBEncoder(
|
|
161
|
+
sample_rate: float = 2e6,
|
|
162
|
+
tx_params_distributions: dict[TXParams | str, list[list[float]]] | None = None,
|
|
163
|
+
seed: int | None = None,
|
|
164
|
+
)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| Method | Returns | Description |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `encode(msg: int)` | `tuple[np.ndarray, dict[TXParams, float]]` | Encodes a 112-bit message into a 120-us baseband I/Q signal. |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### `ADSBChannel`
|
|
174
|
+
|
|
175
|
+
Simulates realistic RF channel impairments on baseband I/Q signals.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
ADSBChannel(
|
|
179
|
+
sample_rate: float = 2e6,
|
|
180
|
+
channel_params_distributions: dict[ChannelParams | str, list[list[float]]] | None = None,
|
|
181
|
+
seed: int | None = None,
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| Method | Returns | Description |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `apply(signal: np.ndarray)` | `tuple[np.ndarray, dict[ChannelParams, float]]` | Applies impairments: IQ imbalance, DC offset, frequency offset, phase offset, AWGN. |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `ChannelParams`
|
|
192
|
+
|
|
193
|
+
| Value | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `SNR_DB` | Signal-to-noise ratio in dB. |
|
|
196
|
+
| `NOISE_CORRELATION` | I/Q noise correlation coefficient (-1.0 to 1.0). |
|
|
197
|
+
| `FREQUENCY_OFFSET` | Carrier frequency offset in Hz. |
|
|
198
|
+
| `PHASE_OFFSET` | Phase offset in radians. |
|
|
199
|
+
| `DC_OFFSET_I` | DC offset on the in-phase component. |
|
|
200
|
+
| `DC_OFFSET_Q` | DC offset on the quadrature component. |
|
|
201
|
+
| `IQ_GAIN_IMBALANCE` | Gain imbalance between I and Q channels. |
|
|
202
|
+
| `IQ_PHASE_IMBALANCE` | Phase imbalance between I and Q channels (degrees). |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### `ADSBAlgorithms`
|
|
207
|
+
|
|
208
|
+
Static utility class providing ADS-B encoding algorithms.
|
|
209
|
+
|
|
210
|
+
| Method | Returns | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `calculate_crc(data: int)` | `int` | Computes 24-bit CRC parity using polynomial `0xFFF409`. |
|
|
213
|
+
| `encode_cpr(lat, lon, odd)` | `tuple[int, int]` | Encodes lat/lon into CPR 17-bit values. |
|
|
214
|
+
| `encode_altitude(alt: int)` | `int` | Encodes altitude in feet into 12-bit Gillham-coded format. |
|
|
215
|
+
| `encode_ground_track(degrees, valid)` | `tuple[int, int]` | Encodes ground track heading into the 7-bit format. |
|
|
216
|
+
|
|
217
|
+
## Distribution Format
|
|
218
|
+
|
|
219
|
+
All configurable distributions use the format `[[min_val, max_val, weight], ...]` where weights for a given parameter must sum to `1.0` (+/- 0.01 tolerance). Keys can be enum members or their string values (e.g., `ChannelParams.SNR_DB` or `"snr_db"`).
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
[MIT](LICENSE) - Copyright (c) 2026 Adam Hamri (adamhamri9)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# adsb-generator
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
|
|
6
|
+
**Synthetic ADS-B data generator.**
|
|
7
|
+
|
|
8
|
+
`adsb-generator` produces realistic I/Q (In-phase/Quadrature) samples of clean and impaired ADS-B (Mode S Downlink Format 17) baseband signals, along with the raw 112-bit message and all applied parameters. It implements an infinite iterator that streams reproducible samples with full control over message type distributions, transmission parameters, and channel impairments.
|
|
9
|
+
|
|
10
|
+
## Key Features
|
|
11
|
+
|
|
12
|
+
- **End-to-end pipeline**: random message generation, PPM encoding, and RF channel simulation in a single call.
|
|
13
|
+
- **Four ADS-B message types**: identification, surface position, airborne position, and airborne velocity with configurable emission probabilities.
|
|
14
|
+
- **Realistic channel impairments**: Gaussian Noise(both AWGN and Correlated) , frequency & phase offset, IQ imbalance, and DC offset, all sampled from configurable probability distributions.
|
|
15
|
+
- **Reproducibility**: deterministic output via a shared seed across all pipeline stages.
|
|
16
|
+
- **Configurable distributions**: override any transmission or channel parameter distribution to model specific receiver conditions or hardware behavior.
|
|
17
|
+
- **NumPy-native**: all signals are `np.complex64` arrays, ready for direct use with any downstream processing tool.
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.10+
|
|
22
|
+
- NumPy
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install adsb-generator
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from adsb_generator import ADSBGenerator
|
|
34
|
+
|
|
35
|
+
# Create a generator with a fixed seed for reproducibility
|
|
36
|
+
gen = ADSBGenerator(seed=42)
|
|
37
|
+
|
|
38
|
+
# Each iteration yields an ADSBSample with the raw message,
|
|
39
|
+
# clean signal, and channel-impaired signal
|
|
40
|
+
for sample in gen:
|
|
41
|
+
print(f"Message type : {sample.message_type.value}")
|
|
42
|
+
print(f"Raw message : {sample.message:#028x}")
|
|
43
|
+
print(f"Clean signal : {sample.clean_signal.shape} complex64 samples")
|
|
44
|
+
print(f"Noisy signal : {sample.channel_signal.shape} complex64 samples")
|
|
45
|
+
print(f"SNR (dB) : {sample.channel_params['snr_db']:.1f}")
|
|
46
|
+
print(f"Amplitude : {sample.tx_params['amplitude']:.3f}")
|
|
47
|
+
break
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Customizing Distributions
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from adsb_generator import ADSBGenerator, MessageType, ChannelParams
|
|
54
|
+
|
|
55
|
+
# Favor airborne positions, restrict SNR to low-moderate range
|
|
56
|
+
gen = ADSBGenerator(
|
|
57
|
+
message_type_probs={
|
|
58
|
+
MessageType.AIRBORNE_POSITION: 0.60,
|
|
59
|
+
MessageType.AIRBORNE_VELOCITY: 0.20,
|
|
60
|
+
MessageType.IDENTIFICATION: 0.10,
|
|
61
|
+
MessageType.SURFACE_POSITION: 0.10,
|
|
62
|
+
},
|
|
63
|
+
channel_params_distributions={
|
|
64
|
+
ChannelParams.SNR_DB: [
|
|
65
|
+
[3.0, 8.0, 0.70],
|
|
66
|
+
[8.0, 15.0, 0.30],
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
sample_rate=2e6,
|
|
70
|
+
seed=12345,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
sample = next(gen)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### `ADSBGenerator`
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
ADSBGenerator(
|
|
82
|
+
message_type_probs: dict[MessageType | str, float] | None = None,
|
|
83
|
+
tx_params_distributions: dict[TXParams | str, list[list[float]]] | None = None,
|
|
84
|
+
channel_params_distributions: dict[ChannelParams | str, list[list[float]]] | None = None,
|
|
85
|
+
sample_rate: float = 2e6,
|
|
86
|
+
seed: int | None = None,
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Parameter | Type | Default | Description |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `message_type_probs` | `dict` or `None` | Equal 25% per type | Mapping of `MessageType` to emission probabilities (must sum to 1.0). |
|
|
93
|
+
| `tx_params_distributions` | `dict` or `None` | Single amplitude band | Mapping of `TXParams` to `[[min, max, weight], ...]` intervals. |
|
|
94
|
+
| `channel_params_distributions` | `dict` or `None` | Typical ADS-B conditions | Mapping of `ChannelParams` to `[[min, max, weight], ...]` intervals. |
|
|
95
|
+
| `sample_rate` | `float` | `2e6` | Sampling rate in samples per second. |
|
|
96
|
+
| `seed` | `int` or `None` | Random | Seed for deterministic output across all pipeline stages. |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### `ADSBSample`
|
|
101
|
+
|
|
102
|
+
Dataclass returned by each iteration of `ADSBGenerator`.
|
|
103
|
+
|
|
104
|
+
| Field | Type | Description |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `message` | `int` | Complete 112-bit ADS-B message (including 24-bit CRC) as an integer. |
|
|
107
|
+
| `message_type` | `MessageType` | The type of ADS-B message generated. |
|
|
108
|
+
| `clean_signal` | `np.ndarray` | Complex baseband I/Q signal before channel impairments (`complex64`). |
|
|
109
|
+
| `tx_params` | `dict[TXParams, float]` | Transmission parameters applied during encoding. |
|
|
110
|
+
| `channel_signal` | `np.ndarray` | Complex baseband I/Q signal after channel impairments (`complex64`). |
|
|
111
|
+
| `channel_params` | `dict[ChannelParams, float]` | Channel parameters applied to the signal. |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### `MessageType`
|
|
116
|
+
|
|
117
|
+
| Value | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `IDENTIFICATION` | Aircraft identification (callsign) messages (TC 1--4). |
|
|
120
|
+
| `SURFACE_POSITION` | Surface position messages (TC 5--8). |
|
|
121
|
+
| `AIRBORNE_POSITION` | Airborne position messages (TC 9--18, 20--22). |
|
|
122
|
+
| `AIRBORNE_VELOCITY` | Airborne velocity messages (TC 19). |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### `ADSBEncoder`
|
|
127
|
+
|
|
128
|
+
Encodes 112-bit messages into complex baseband I/Q samples using PPM per the Mode S standard.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
ADSBEncoder(
|
|
132
|
+
sample_rate: float = 2e6,
|
|
133
|
+
tx_params_distributions: dict[TXParams | str, list[list[float]]] | None = None,
|
|
134
|
+
seed: int | None = None,
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Method | Returns | Description |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `encode(msg: int)` | `tuple[np.ndarray, dict[TXParams, float]]` | Encodes a 112-bit message into a 120-us baseband I/Q signal. |
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `ADSBChannel`
|
|
145
|
+
|
|
146
|
+
Simulates realistic RF channel impairments on baseband I/Q signals.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
ADSBChannel(
|
|
150
|
+
sample_rate: float = 2e6,
|
|
151
|
+
channel_params_distributions: dict[ChannelParams | str, list[list[float]]] | None = None,
|
|
152
|
+
seed: int | None = None,
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Method | Returns | Description |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `apply(signal: np.ndarray)` | `tuple[np.ndarray, dict[ChannelParams, float]]` | Applies impairments: IQ imbalance, DC offset, frequency offset, phase offset, AWGN. |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### `ChannelParams`
|
|
163
|
+
|
|
164
|
+
| Value | Description |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `SNR_DB` | Signal-to-noise ratio in dB. |
|
|
167
|
+
| `NOISE_CORRELATION` | I/Q noise correlation coefficient (-1.0 to 1.0). |
|
|
168
|
+
| `FREQUENCY_OFFSET` | Carrier frequency offset in Hz. |
|
|
169
|
+
| `PHASE_OFFSET` | Phase offset in radians. |
|
|
170
|
+
| `DC_OFFSET_I` | DC offset on the in-phase component. |
|
|
171
|
+
| `DC_OFFSET_Q` | DC offset on the quadrature component. |
|
|
172
|
+
| `IQ_GAIN_IMBALANCE` | Gain imbalance between I and Q channels. |
|
|
173
|
+
| `IQ_PHASE_IMBALANCE` | Phase imbalance between I and Q channels (degrees). |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### `ADSBAlgorithms`
|
|
178
|
+
|
|
179
|
+
Static utility class providing ADS-B encoding algorithms.
|
|
180
|
+
|
|
181
|
+
| Method | Returns | Description |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `calculate_crc(data: int)` | `int` | Computes 24-bit CRC parity using polynomial `0xFFF409`. |
|
|
184
|
+
| `encode_cpr(lat, lon, odd)` | `tuple[int, int]` | Encodes lat/lon into CPR 17-bit values. |
|
|
185
|
+
| `encode_altitude(alt: int)` | `int` | Encodes altitude in feet into 12-bit Gillham-coded format. |
|
|
186
|
+
| `encode_ground_track(degrees, valid)` | `tuple[int, int]` | Encodes ground track heading into the 7-bit format. |
|
|
187
|
+
|
|
188
|
+
## Distribution Format
|
|
189
|
+
|
|
190
|
+
All configurable distributions use the format `[[min_val, max_val, weight], ...]` where weights for a given parameter must sum to `1.0` (+/- 0.01 tolerance). Keys can be enum members or their string values (e.g., `ChannelParams.SNR_DB` or `"snr_db"`).
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](LICENSE) - Copyright (c) 2026 Adam Hamri (adamhamri9)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "adsb-generator"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "`adsb-generator` produces realistic I/Q (In-phase/Quadrature) samples of clean and impaired ADS-B (Mode S Downlink Format 17) baseband signals, along with the raw 112-bit message and all applied parameters. It implements an infinite iterator that streams reproducible samples with full control over message type distributions, transmission parameters, and channel impairments."
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10,<3.14"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Adam Hamri", email = "adamhamri9@proton.me" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["adsb", "mode-s", "aviation", "signal-generation", "iq-samples", "baseband"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Scientific/Engineering",
|
|
27
|
+
"Topic :: Communications :: Ham Radio",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"numpy>=1.26.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8.0.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/adamhamri9/adsb-datagen"
|
|
40
|
+
Repository = "https://github.com/adamhamri9/adsb-datagen"
|
|
41
|
+
Issues = "https://github.com/adamhamri9/adsb-datagen/issues"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
2
|
+
|
|
3
|
+
from .generator import ADSBSample, ADSBGenerator
|
|
4
|
+
from .channel import ChannelParams, ADSBChannel
|
|
5
|
+
from .encoder import TXParams, ADSBEncoder
|
|
6
|
+
from .message import MessageType, ADSBMessage
|
|
7
|
+
|
|
8
|
+
__all__ = ["ADSBSample", "ADSBGenerator", "ChannelParams", "ADSBChannel",
|
|
9
|
+
"TXParams", "ADSBEncoder", "MessageType", "ADSBMessage"]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
class ADSBAlgorithms():
|
|
4
|
+
|
|
5
|
+
CRC_POLY = 0xFFF409
|
|
6
|
+
|
|
7
|
+
@classmethod
|
|
8
|
+
def calculate_crc(cls, data: int) -> int:
|
|
9
|
+
|
|
10
|
+
packet = data << 24
|
|
11
|
+
remainder = packet
|
|
12
|
+
|
|
13
|
+
for bit in range(111, 23, -1):
|
|
14
|
+
if (remainder >> bit) & 1:
|
|
15
|
+
remainder ^= cls.CRC_POLY << (bit - 24)
|
|
16
|
+
|
|
17
|
+
crc = remainder & 0xFFFFFF
|
|
18
|
+
|
|
19
|
+
return packet | crc
|
|
20
|
+
|
|
21
|
+
_NL_TABLE = [
|
|
22
|
+
(87, 59), (83, 58), (79, 57), (76, 56), (73, 55),
|
|
23
|
+
(70, 54), (66, 53), (63, 52), (60, 51), (57, 50),
|
|
24
|
+
(53, 49), (50, 48), (46, 47), (43, 46), (40, 45),
|
|
25
|
+
(37, 44), (34, 43), (31, 42), (28, 41), (25, 40),
|
|
26
|
+
(22, 39), (19, 38), (16, 37), (13, 36), (9, 35),
|
|
27
|
+
(6, 34), (3, 33), (0, 32),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def _nl(cls, lat: float) -> int:
|
|
32
|
+
if abs(lat) >= 87:
|
|
33
|
+
return 1
|
|
34
|
+
for lat_min, nl in cls._NL_TABLE:
|
|
35
|
+
if abs(lat) >= lat_min:
|
|
36
|
+
return nl
|
|
37
|
+
return 32
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def encode_cpr(cls, lat: float, lon: float, odd: bool) -> tuple[int, int]:
|
|
41
|
+
lat = max(-90.0, min(90.0, lat))
|
|
42
|
+
lon = max(-180.0, min(180.0, lon))
|
|
43
|
+
|
|
44
|
+
NB = 17
|
|
45
|
+
factor = 1 << NB
|
|
46
|
+
|
|
47
|
+
nz_lat = 59 if odd else 60
|
|
48
|
+
dlat = 360.0 / nz_lat
|
|
49
|
+
|
|
50
|
+
zone_lat = math.floor(lat / dlat)
|
|
51
|
+
rem_lat = lat - zone_lat * dlat
|
|
52
|
+
yz = int(round((rem_lat / dlat) * factor)) & (factor - 1)
|
|
53
|
+
|
|
54
|
+
rlat = dlat * (yz / factor + zone_lat)
|
|
55
|
+
rlat = max(-90.0, min(90.0, rlat))
|
|
56
|
+
|
|
57
|
+
nl = cls._nl(rlat)
|
|
58
|
+
|
|
59
|
+
dlon = 360.0 / nl
|
|
60
|
+
zone_lon = math.floor(lon / dlon)
|
|
61
|
+
rem_lon = lon - zone_lon * dlon
|
|
62
|
+
xz = int(round((rem_lon / dlon) * factor)) & (factor - 1)
|
|
63
|
+
|
|
64
|
+
return yz, xz
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def encode_altitude(alt: int) -> int:
|
|
68
|
+
alt_clamped = max(-1000, min(50100, alt))
|
|
69
|
+
n = (alt_clamped + 1000) // 25
|
|
70
|
+
|
|
71
|
+
top_bits = (n >> 4) & 0x7F
|
|
72
|
+
bottom_bits = n & 0x0F
|
|
73
|
+
|
|
74
|
+
return (top_bits << 5) | (1 << 4) | bottom_bits
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def encode_ground_track(degrees: float, valid: bool = True) -> tuple[int, int]:
|
|
78
|
+
if not valid or degrees is None:
|
|
79
|
+
return 0, 0
|
|
80
|
+
|
|
81
|
+
degrees = degrees % 360.0
|
|
82
|
+
trk_code = round(degrees * 128.0 / 360.0)
|
|
83
|
+
|
|
84
|
+
return 1, trk_code
|