Initial ARSS. Need to improve and add functions.

This commit is contained in:
2026-03-02 14:48:21 +09:00
commit 775668afdb
20 changed files with 1610 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
function [adc_raw_data, adc_combined] = apply_lna_and_mixer(RxOut, TxOut, RadarParams)
% : adc_raw_data [NumRx, NumTx, NumChirps, N_adc_samples] -
% adc_combined [NumRx, NumChirps, N_adc_samples] - ADC ( TX )
% : RxOut, TxOut, RadarParams
% MIMO TX .
Timing = RadarParams.Waveform.Timing;
fs_adc = RadarParams.Waveform.fs_adc;
f_start = RadarParams.Waveform.f_start;
Slope = RadarParams.Waveform.Slope;
pn_level = RadarParams.Waveform.nonideal.pn_level;
f_ripple = RadarParams.Waveform.nonideal.f_ripple;
peak_phase_error = RadarParams.Waveform.nonideal.peak_phase_error;
enable_phase_noise = true;
enable_nonlinearity = true;
if isfield(RadarParams.Waveform.nonideal, 'enable_phase_noise')
enable_phase_noise = logical(RadarParams.Waveform.nonideal.enable_phase_noise);
end
if isfield(RadarParams.Waveform.nonideal, 'enable_nonlinearity')
enable_nonlinearity = logical(RadarParams.Waveform.nonideal.enable_nonlinearity);
end
use_datasheet_phase_noise = false;
phase_noise_cfg = struct();
if isfield(RadarParams.Waveform.nonideal, 'phaseNoise')
phase_noise_cfg = RadarParams.Waveform.nonideal.phaseNoise;
if isfield(phase_noise_cfg,'enabled') && phase_noise_cfg.enabled && ...
isfield(phase_noise_cfg,'offset_Hz') && isfield(phase_noise_cfg,'level_dBc_Hz')
use_datasheet_phase_noise = true;
end
end
mimoMode = RadarParams.Waveform.mimoMode;
NumTx = RadarParams.Antenna.NumTx;
rxPathGain_dB = RadarParams.Rxpath.rxPathGain_dB;
system_NF_dB = RadarParams.Rxpath.system_NF_dB;
PA_Profile = RadarParams.RFOutput.PA_Profile;
SpurParams = RadarParams.SpurParams;
enable_spur = true;
if isstruct(SpurParams) && isfield(SpurParams, 'enabled')
enable_spur = logical(SpurParams.enabled);
end
[NumTargets, NumRx, ~, NumChirps, N_adc_samples] = size(RxOut.space_loss_amp);
% TDM : TX별
if strcmpi(mimoMode, 'TDM') && mod(NumChirps, NumTx) ~= 0
error('TDM mode requires NumChirps to be a multiple of NumTx. Current NumChirps=%d, NumTx=%d.', NumChirps, NumTx);
end
% t=0 ( ) , ADC
t_adc = Timing.AdcStartTime : 1/fs_adc : (Timing.AdcStartTime + Timing.AdcSampTime - 1/fs_adc);
f_inst = f_start + Slope * t_adc;
% --- 1. PA_Profile을 (Vtx) ---
% PA_Profile의 ( PA_Profile(:,1), PA_Profile(:,2) )
if isstruct(PA_Profile)
P_inst_dBm = interp1(PA_Profile.freqs, PA_Profile.power_dBm, f_inst, 'linear', 'extrap');
else
P_inst_dBm = interp1(PA_Profile(:,1), PA_Profile(:,2), f_inst, 'linear', 'extrap');
end
% dBm -> Watt -> (V) (50 )
Vtx_inst = sqrt(10.^((P_inst_dBm - 30) / 10) * 50);
% --- 2. ---
T_ref = RadarParams.Basic.T0;
P_noise_floor_W = RadarParams.Basic.kb * T_ref * fs_adc;
rxPathGain_lin = 10^(rxPathGain_dB/10);
system_NF_lin = 10^(system_NF_dB/10);
% ADC
P_noise_total_W = P_noise_floor_W * system_NF_lin * rxPathGain_lin;
sigma_n = sqrt(P_noise_total_W * 50);
% --- MIMO TX ---
% TDM: TX만
% DDMA: TX +
% legacy phase-noise model ( )
window_size = 50;
ma_filter = ones(1,window_size)/window_size;
% --- 3. ADC Raw Data ---
adc_raw_data = zeros(NumRx, NumTx, NumChirps, N_adc_samples);
for m = 1:NumChirps
% generate phase noise & nonlinearity for this chirp
t_rel = t_adc; % time within chirp
if enable_phase_noise && use_datasheet_phase_noise
phi_noise = generate_phase_noise_from_datasheet(t_rel, phase_noise_cfg);
elseif enable_phase_noise
phi_noise = pn_level * filter(ma_filter, 1, randn(1, N_adc_samples));
else
phi_noise = zeros(1, N_adc_samples);
end
phi_nonlin = zeros(1, N_adc_samples);
if enable_nonlinearity
ramp_idx2 = (t_rel >= Timing.IdleTime);
phi_nonlin(ramp_idx2) = peak_phase_error * sin(2 * pi * f_ripple * (t_rel(ramp_idx2) - Timing.IdleTime));
end
% MIMO TX
if strcmpi(mimoMode, 'TDM')
% TDM: TX (1,2,3,1,2,3,...)
active_tx_list = mod(m - 1, NumTx) + 1; % : m=1TX1, m=2TX2, m=3TX1...
elseif strcmpi(mimoMode, 'DDMA')
% DDMA: TX
active_tx_list = 1:NumTx;
else
% : TX
active_tx_list = 1:NumTx;
end
for tx = 1:NumTx
% TDM TX는
if strcmpi(mimoMode, 'TDM') && ~ismember(tx, active_tx_list)
adc_raw_data(:, tx, m, :) = 0; % TX는 0
continue;
end
for rx = 1:NumRx
signal_mix = zeros(1, N_adc_samples);
for kt = 1:NumTargets
% (1) : Vtx(f) * G_tx * Space_Loss(f) * G_rx * rxPathGain
A_path = squeeze(RxOut.space_loss_amp(kt, rx, tx, m, :))';
% (Vtx_inst)
A_total = Vtx_inst .* (TxOut.G_tx_amp(kt, tx) * A_path * sqrt(rxPathGain_lin));
% (2) TTD Beat Phase ( t_adc )
tau = squeeze(RxOut.tau(kt, rx, tx, m, :))';
beat_phase = 2*pi * (f_start * tau + Slope * tau .* t_adc - 0.5 * Slope * tau.^2);
% add transmit impairments (phase noise + nonlinearity)
beat_phase = beat_phase + phi_noise + phi_nonlin;
% (3) DDMA
if strcmpi(mimoMode, 'DDMA')
phase_shift = 2 * pi * (tx - 1) * (m - 1) / NumTx;
beat_phase = beat_phase + phase_shift;
end
% (4)
base_sig = A_total .* exp(1j * beat_phase);
if enable_spur && exist('SpurParams','var') && ~isempty(SpurParams)
spur_vec = generate_spur(SpurParams, t_adc, base_sig);
signal_mix = signal_mix + base_sig + spur_vec;
else
signal_mix = signal_mix + base_sig;
end
end
% (5)
noise = (sigma_n/sqrt(2)) * (randn(1, N_adc_samples) + 1j*randn(1, N_adc_samples));
adc_raw_data(rx, tx, m, :) = signal_mix + noise;
end
end
end
% --- 4. ADC ( TX RX별로 ) ---
% RX가 TX
adc_combined = zeros(NumRx, NumChirps, N_adc_samples);
for m = 1:NumChirps
for rx = 1:NumRx
% TX의
combined_signal = squeeze(sum(adc_raw_data(rx, :, m, :), 2));
% ADC spur이 (orientation )
if enable_spur && exist('SpurParams','var') && isfield(SpurParams,'adc')
spur_adc = generate_spur(SpurParams, t_adc);
% ensure same shape as combined_signal (Nx1 vs 1xN)
spur_adc = reshape(spur_adc, size(combined_signal));
combined_signal = combined_signal + spur_adc;
end
adc_combined(rx, m, :) = combined_signal;
end
end
% --- 5. : DC offset/LO leakage clipping ---
if enable_spur && exist('SpurParams','var')
% LO leakage: /DC
if isfield(SpurParams,'lo_leak')
dc_amp = SpurParams.lo_leak.amp;
adc_combined = adc_combined + dc_amp;
end
% Clipping spur:
if isfield(SpurParams,'clip')
target_range = SpurParams.clip.range;
beat_freq = 2 * Slope * target_range / RadarParams.Basic.c;
clip_amp = SpurParams.clip.amp;
for m = 1:NumChirps
% ()
clip_tone = clip_amp * cos(2*pi*beat_freq*t_adc);
for rx = 1:NumRx
adc_combined(rx, m, :) = adc_combined(rx, m, :) + reshape(clip_tone, [1, 1, length(clip_tone)]);
end
end
end
end
end