modrec-workflow/scripts/dataset_manager/data_gen.py

98 lines
4.1 KiB
Python
Raw Normal View History

import argparse
2025-05-21 15:52:16 -04:00
import numpy as np
from utils.data import Recording
2025-05-21 15:52:16 -04:00
from utils.signal import block_generator
2025-06-16 13:43:59 -04:00
from helpers.app_settings import get_app_settings
2025-05-21 15:52:16 -04:00
settings = get_app_settings().dataset
2025-05-21 15:52:16 -04:00
mods = settings.modulation_settings
def generate_modulated_signals(output_dir: str) -> None:
"""
Generates modulated IQ signal recordings across specified modulation types and SNR values,
adds AWGN noise, and saves the resulting samples as .npy files to the given output directory.
The function uses modulation parameters defined in app.yaml and supports modulation types like
2025-06-18 13:44:29 -04:00
PSK and QAM through configurable constellation settings. The generated recordings are tagged
with metadata such as modulation type, SNR, roll-off factor (beta), and samples-per-symbol (sps).
Parameters:
output_dir (str): Path to the directory where .npy files will be saved.
Returns:
None
"""
2025-05-21 15:52:16 -04:00
2025-06-16 13:43:59 -04:00
for modulation in settings.modulation_types:
2025-06-18 10:05:14 -04:00
for snr in np.arange(settings.snr_start, settings.snr_stop, settings.snr_step):
for _ in range(settings.mult_factor):
2025-06-16 13:43:59 -04:00
recording_length = settings.recording_length
beta = (
settings.beta
) # the rolloff factor, can be changed to add variety
sps = (
settings.sps
) # samples per symbol, or the relative bandwidth of the digital signal. Can also be changed.
2025-05-21 15:52:16 -04:00
# blocks don't directly take the string 'qpsk' so we use the dict 'mods' to get parameters
constellation_type = mods[modulation]["constellation_type"]
num_bits_per_symbol = mods[modulation]["num_bits_per_symbol"]
2025-05-21 15:52:16 -04:00
# construct the digital modulation blocks with these parameters
# we have bit source -> mapper -> upsampling -> pulse shaping
2025-05-21 15:52:16 -04:00
bit_source = block_generator.RandomBinarySource()
mapper = block_generator.Mapper(
constellation_type=constellation_type,
num_bits_per_symbol=num_bits_per_symbol,
)
upsampler = block_generator.Upsampling(factor=sps)
pulse_shaping_filter = block_generator.RaisedCosineFilter(
upsampling_factor=sps, beta=beta
)
2025-05-21 15:52:16 -04:00
pulse_shaping_filter.connect_input([upsampler])
upsampler.connect_input([mapper])
mapper.connect_input([bit_source])
2025-05-21 15:52:16 -04:00
modulation_recording = pulse_shaping_filter.record(
num_samples=recording_length
)
2025-05-21 15:52:16 -04:00
# add noise by calculating the power of the modulation recording and generating AWGN from the snr parameter
signal_power = np.mean(np.abs(modulation_recording.data[0] ** 2))
awgn_source = block_generator.AWGNSource(
variance=(signal_power / 2) * (10 ** (((-1 * snr) / 20)))
)
noise = awgn_source.record(num_samples=recording_length)
samples_with_noise = modulation_recording.data + noise.data
output_recording = Recording(data=samples_with_noise)
2025-05-21 15:52:16 -04:00
# add metadata for ML later
output_recording.add_to_metadata(key="modulation", value=modulation)
output_recording.add_to_metadata(key="snr", value=int(snr))
output_recording.add_to_metadata(key="beta", value=beta)
output_recording.add_to_metadata(key="sps", value=sps)
2025-05-21 15:52:16 -04:00
# view if you want
# output_recording.view()
# save to file
output_recording.to_npy(
path=output_dir
) # optionally add path and filename parameters
2025-05-21 15:52:16 -04:00
if __name__ == "__main__":
2025-06-13 14:17:13 -04:00
p = argparse.ArgumentParser(description="Generate modulated signal .npy files")
p.add_argument(
"--output-dir", default=".", help="Folder where .npy files will be saved"
2025-06-13 14:17:13 -04:00
)
args = p.parse_args()
generate_modulated_signals(args.output_dir)
2025-06-13 14:46:32 -04:00
print("generated data to " + args.output_dir)