-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_vunit_data
More file actions
executable file
·136 lines (102 loc) · 3.95 KB
/
Copy pathgenerate_vunit_data
File metadata and controls
executable file
·136 lines (102 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env python3
import numpy as np
import os
# Get the directory of this script.
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)
fs = 61.44e6 # Sample rate.
fc = 5e6 # Carrier.
T = 1/fs
half_us = 0.5e-6 # half-microsecond resolution
silence_us = 10 # before and after
preamble_bits = 0b1010000101000000
adsb_message = "8D7C7A465815B0B6ADBBB2611F7B"
# --- Silence slots ---
num_silence_slots = int(round(silence_us / (half_us*1e6))) # number of 0.5 us slots
silence_slots = [0] * num_silence_slots
# --- Preamble slots (16 half-us) ---
pre_bits = []
for b in range(15, -1, -1): # MSB-first
pre_bits.append((preamble_bits >> b) & 1)
# --- Message slots ---
# Each ADS-B bit is 1 us = 2 half-us slots
msg_bits = []
for i in range(0, len(adsb_message), 2): # parse byte by byte
byte = int(adsb_message[i:i+2], 16)
for b in range(7, -1, -1): # MSB-first
bit = (byte >> b) & 1
# map bit to 2 half-us slots: 1 -> [1,0], 0 -> [0,1] (PPM)
if bit:
msg_bits.extend([1, 0])
else:
msg_bits.extend([0, 1])
# --- Combine everything ---
pattern_half = []
pattern_half.extend(silence_slots)
pattern_half.extend(pre_bits)
pattern_half.extend(msg_bits)
pattern_half.extend(silence_slots)
print("Total half-us slots:", len(pattern_half))
print("First 50 slots:", pattern_half[:50])
# Parameters (tweak if you want)
half_us = 0.5e-6 # half-microsecond slot
samples_per_half_exact = fs * half_us # fractional samples per half-us slot
amplitude = 1.0 # final float amplitude (apply additional gain later)
# 1) compute integer sample counts per half-us slot using fractional accumulation
samples_list = []
accum = 0.0
for slot_val in pattern_half:
accum += samples_per_half_exact
n_samples = int(round(accum))
# safety: ensure at least 1 sample for slots (shouldn't be 0 for typical fs)
if n_samples < 1:
n_samples = 1
samples_list.append(n_samples)
accum -= n_samples # carry remainder forward
# 2) total samples and per-sample carrier (phase continuous)
total_samples = sum(samples_list)
n = np.arange(total_samples)
phase = 2.0 * np.pi * fc * n / fs
carrier_i = np.cos(phase)
carrier_q = np.sin(phase)
# 3) build envelope per-sample using samples_list (1.0 for pulse slots, 0.0 for silence)
envelope = np.zeros(total_samples, dtype=float)
idx = 0
for slot_val, n_samples in zip(pattern_half, samples_list):
end = idx + n_samples
if end > total_samples:
end = total_samples
if slot_val:
envelope[idx:end] = 1.0
idx = end
# 4) compose complex signal (I + jQ), preserving phase continuity
sigout = amplitude * (carrier_i * envelope + 1j * carrier_q * envelope)
print(sigout);
# --- Write IQ file ---
bits = 12
scale = 2**(bits - 1) - 1
gain = 0.75 # Headroom.
scaled_i = (sigout.real * scale * gain).astype(int)
scaled_q = (sigout.imag * scale * gain).astype(int)
os.makedirs("tb/data/gen/", exist_ok=True)
filename = "tb/data/gen/adsb_61_440_000_hertz.dat"
with open(filename, "w") as f:
for i in range(len(scaled_i)):
f.write(f"{scaled_i[i]} {scaled_q[i]}\n")
print(f"Wrote '{filename}'.")
# Load saved IQ file.
# head -c 49152 tb/data/40e6/segments/20250430_7C79B4/cap1/seg16 > tb/data/40e6/segments/20250430_7C79B4/cap1/seg16_truncated
rx = np.fromfile('tb/data/40e6/segments/20250430_7C79B4/cap1/seg16_truncated', dtype=np.complex64)
# Scale to 12-bit signed integers
max_val = np.max(np.abs(rx))
scale = 2047 / max_val if max_val != 0 else 1
# Convert real and imaginary parts separately
i_scaled = np.round(np.real(rx) * scale).astype(np.int16)
q_scaled = np.round(np.imag(rx) * scale).astype(np.int16)
# Clip to ensure 12-bit range
i_scaled = np.clip(i_scaled, -2048, 2047)
q_scaled = np.clip(q_scaled, -2048, 2047)
# Prepare I/Q pairs
iq_pairs = np.column_stack((i_scaled, q_scaled))
# Save to text file in the desired format
np.savetxt('tb/data/gen/adsb_capture_40_000_000_hertz.dat', iq_pairs, fmt='%d')