add engine
This commit is contained in:
parent
08d46eea8c
commit
4bec0e6d34
109
scenario_simulator/core/simulation_engine.py
Normal file
109
scenario_simulator/core/simulation_engine.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
Core module for radar signal simulation.
|
||||||
|
|
||||||
|
This module provides the classes and functions necessary to generate
|
||||||
|
synthetic I/Q data for specified radar configurations and target scenarios.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import numpy as np
|
||||||
|
from scipy.constants import c
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RadarConfig:
|
||||||
|
"""
|
||||||
|
Stores the configuration parameters for the simulated radar.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
carrier_frequency (float): Carrier frequency in Hertz (Hz).
|
||||||
|
prf (float): Pulse Repetition Frequency in Hertz (Hz).
|
||||||
|
pulse_width (float): Duration of a single radar pulse in seconds (s).
|
||||||
|
sample_rate (float): Sampling rate of the ADC in samples per second (Hz).
|
||||||
|
"""
|
||||||
|
carrier_frequency: float
|
||||||
|
prf: float
|
||||||
|
pulse_width: float
|
||||||
|
sample_rate: float
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Target:
|
||||||
|
"""
|
||||||
|
Represents a single point target in the simulation.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
initial_position (np.ndarray): 3D initial position [x, y, z] in meters (m).
|
||||||
|
velocity (np.ndarray): 3D constant velocity [vx, vy, vz] in m/s.
|
||||||
|
rcs (float): Radar Cross Section in square meters (m^2).
|
||||||
|
"""
|
||||||
|
initial_position: np.ndarray
|
||||||
|
velocity: np.ndarray
|
||||||
|
rcs: float
|
||||||
|
|
||||||
|
def generate_iq_data(config: RadarConfig, target: Target, num_pulses: int) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Generates the I/Q data matrix for a single target scenario.
|
||||||
|
|
||||||
|
This function simulates the received echo from a single point target with
|
||||||
|
constant velocity over a specified number of pulses (Coherent Processing
|
||||||
|
Interval - CPI).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: The radar configuration parameters.
|
||||||
|
target: The target's properties.
|
||||||
|
num_pulses: The total number of pulses to simulate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A 2D numpy array of complex numbers representing the I/Q data.
|
||||||
|
Shape: (num_pulses, num_samples_per_pulse).
|
||||||
|
"""
|
||||||
|
# --- Derived parameters calculation
|
||||||
|
wavelength = c / config.carrier_frequency
|
||||||
|
pri = 1.0 / config.prf # Pulse Repetition Interval
|
||||||
|
|
||||||
|
# Calculate the number of samples within one pulse's duration
|
||||||
|
num_samples_per_pulse = int(config.pulse_width * config.sample_rate)
|
||||||
|
|
||||||
|
# --- Initialize output data structure
|
||||||
|
# This matrix will store the complex I/Q data for each pulse
|
||||||
|
iq_matrix = np.zeros((num_pulses, num_samples_per_pulse), dtype=np.complex128)
|
||||||
|
|
||||||
|
# --- Main simulation loop (over slow-time)
|
||||||
|
for pulse_index in range(num_pulses):
|
||||||
|
# Current time relative to the start of the CPI
|
||||||
|
current_slow_time = pulse_index * pri
|
||||||
|
|
||||||
|
# Update target position based on its constant velocity
|
||||||
|
current_position = target.initial_position + target.velocity * current_slow_time
|
||||||
|
|
||||||
|
# Calculate range from radar (at origin) to target
|
||||||
|
range_to_target = np.linalg.norm(current_position)
|
||||||
|
|
||||||
|
# Calculate the two-way time delay for the echo
|
||||||
|
time_delay = 2 * range_to_target / c
|
||||||
|
|
||||||
|
# Simplified signal amplitude based on radar range equation (power ~ 1/R^4)
|
||||||
|
# We use sqrt(rcs) because we are modeling voltage/amplitude, not power.
|
||||||
|
amplitude = np.sqrt(target.rcs) / (range_to_target**2)
|
||||||
|
|
||||||
|
# The core of the simulation: calculate the complex phase shift
|
||||||
|
# This phase is determined by the round-trip path length in wavelengths
|
||||||
|
phase_shift = np.exp(-1j * 4 * np.pi * range_to_target / wavelength)
|
||||||
|
|
||||||
|
signal_complex_amplitude = amplitude * phase_shift
|
||||||
|
|
||||||
|
# Model a simple rectangular pulse
|
||||||
|
pulse_shape = np.ones(num_samples_per_pulse)
|
||||||
|
received_pulse = signal_complex_amplitude * pulse_shape
|
||||||
|
|
||||||
|
# --- Place the received pulse into the I/Q matrix at the correct time delay
|
||||||
|
start_sample = int(round(time_delay * config.sample_rate))
|
||||||
|
|
||||||
|
if 0 <= start_sample < iq_matrix.shape[1]:
|
||||||
|
# This handles cases where the pulse might partially go out of the sampling window
|
||||||
|
samples_to_write = min(num_samples_per_pulse, iq_matrix.shape[1] - start_sample)
|
||||||
|
|
||||||
|
iq_matrix[
|
||||||
|
pulse_index, start_sample : start_sample + samples_to_write
|
||||||
|
] = received_pulse[:samples_to_write]
|
||||||
|
|
||||||
|
return iq_matrix
|
||||||
Loading…
Reference in New Issue
Block a user