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
+244
View File
@@ -0,0 +1,244 @@
% =========================================================================
% Tx_Main.m
% Pure MATLAB FMCW TX Simulator (Multi-Chirp Frame Generation)
% =========================================================================
clear; clc; close all;
% =============== ===============
currentFilePath = mfilename('fullpath');
currentFolder = fileparts(currentFilePath);
addpath(genpath(currentFolder));
%% =================== ===================
% 1)
RadarParams.Basic.c = 3e8; % (m/s)
RadarParams.Basic.kb = physconst('Boltzmann'); %
RadarParams.Basic.T0 = 290; % (K)
% 2)
RadarParams.Waveform.fc = 77e9; % (Hz)
RadarParams.Waveform.lambda_c = RadarParams.Basic.c / RadarParams.Waveform.fc; % (m)
RadarParams.Waveform.NumChirps = 128; %
RadarParams.Waveform.B_valid = 1e9; % (Hz)
RadarParams.Waveform.fs_adc = 10e6; % ADC (Hz)
RadarParams.Waveform.fs_waveform = 1e6; % (Hz)
% 2-1)
RadarParams.Waveform.Timing.IdleTime = 7e-6; % (s)
RadarParams.Waveform.Timing.TxStartTime = 1e-6; % Tx on (s)
RadarParams.Waveform.Timing.AdcStartTime = 6e-6; % ADC on (s)
RadarParams.Waveform.Timing.AdcSampTime = 50e-6; % ADC (s)
RadarParams.Waveform.Timing.ExcessTime = 1e-6; % (s)
RadarParams.Waveform.Timing.RampEndTime = RadarParams.Waveform.Timing.IdleTime + RadarParams.Waveform.Timing.TxStartTime + RadarParams.Waveform.Timing.AdcSampTime + RadarParams.Waveform.Timing.ExcessTime; % (s)
RadarParams.Waveform.PRI = RadarParams.Waveform.Timing.IdleTime + RadarParams.Waveform.Timing.RampEndTime; % Pulse Repetition Interval (s)
RadarParams.Waveform.PRF = 1 / RadarParams.Waveform.PRI; % Pulse Repetition Frequency (Hz)
RadarParams.Waveform.Slope = RadarParams.Waveform.B_valid / RadarParams.Waveform.Timing.AdcSampTime; % (Hz/s)
RadarParams.Waveform.f_start = RadarParams.Waveform.fc - RadarParams.Waveform.B_valid/2; % (Hz)
% 2-2) Phase noise & nonlinearity parameters
RadarParams.Waveform.nonideal.pn_level = 0.05; %
RadarParams.Waveform.nonideal.f_ripple = 300e3; %
RadarParams.Waveform.nonideal.peak_phase_error = 0.1; %
RadarParams.Waveform.nonideal.power_drop_edge = 0.8; %
RadarParams.Waveform.nonideal.enable_phase_noise = false; % phase noise on/off
RadarParams.Waveform.nonideal.enable_nonlinearity = false; % nonlinearity on/off
% Datasheet phase noise (: -89 dBc/Hz @ 1 MHz)
RadarParams.Waveform.nonideal.phaseNoise.offset_Hz = [1e6];
RadarParams.Waveform.nonideal.phaseNoise.level_dBc_Hz = [-89];
RadarParams.Waveform.nonideal.phaseNoise.enabled = false;
% 2-3) MIMO
RadarParams.Waveform.mimoMode = 'TDM'; % 'TDM' 'DDMA'
% 3) RF - [dBm]
RadarParams.RFOutput.PA_Profile.freqs = [76e9, 76.5e9, 77e9, 77.5e9, 78e9, 79e9];
RadarParams.RFOutput.PA_Profile.power_dBm = [10.5, 11.5, 12.0, 11.5, 10.5, 8.0];
% 4)
% 4-1) (3xN , : m)
RadarParams.Antenna.NumTx = 2;
RadarParams.Antenna.NumRx = 4;
RadarParams.Antenna.lambda = RadarParams.Waveform.lambda_c;
RadarParams.Antenna.TxPos = [ (0:RadarParams.Antenna.NumTx-1)*2*RadarParams.Antenna.lambda; zeros(1,RadarParams.Antenna.NumTx); zeros(1,RadarParams.Antenna.NumTx) ]; % 3xNumTx
RadarParams.Antenna.RxPos = [ (0:RadarParams.Antenna.NumRx-1)*0.5*RadarParams.Antenna.lambda; zeros(1,RadarParams.Antenna.NumRx); zeros(1,RadarParams.Antenna.NumRx) ]; % 3xNumRx
% 4-2) ( NumTx, NumRx )
RadarParams.Antenna.tx_files = {}; % ( )
RadarParams.Antenna.rx_files = {};
RadarParams.Antenna.TxPattern = build_antenna_patterns(RadarParams.Antenna.NumTx, '1D', 12, 5, 30, 10, RadarParams.Antenna.tx_files);
RadarParams.Antenna.RxPattern = build_antenna_patterns(RadarParams.Antenna.NumRx, '2D', 14, 2, 40, 15, RadarParams.Antenna.rx_files);
% 5)
RadarParams.Target.R = [12, 80, 120]; % (m)
RadarParams.Target.v = [7, -5, 0]; % (m/s)
RadarParams.Target.rcs = [10, 5, 20]; % RCS (dBsm)
RadarParams.Target.az = [10, -10, 0]; % (deg)
RadarParams.Target.el = [0, 0, 5]; % (deg)
RadarParams.Target.NumTargets = length(RadarParams.Target.R);
% 6)
RadarParams.Rxpath.fc_hpf = 1400; % HPF (Hz)
RadarParams.Rxpath.fc_lpf = 0.8*RadarParams.Waveform.fs_adc/2; % LPF (Hz)
RadarParams.Rxpath.adc_bits = 12; % ADC
RadarParams.Rxpath.adc_v_full_scale = 2.0; % full-scale (Vp-p)
RadarParams.Rxpath.receiver_mode = 'Real'; % 'IQ' 'Real'
RadarParams.Rxpath.rxPathGain_dB = 50; % RX (dB)
RadarParams.Rxpath.system_NF_dB = 15; % (dB)
% 6-5) Spur parameters ( )
% Spur의 , LO leakage, , ADC , .
% SpurParams .
% spur의 , spur apply_lna_and_mixer SpurParams를 spur adc_combined에 .
RadarParams.SpurParams = struct();
RadarParams.SpurParams.lo = struct('amp',0.01,'freq',1e6); %
RadarParams.SpurParams.mixer= struct('alpha2',1e-4,'alpha3',1e-6); % 2/3
RadarParams.SpurParams.adc = struct('amp',1e-3,'freq',2e6); % ADC
RadarParams.SpurParams.switch = struct('amp',5e-4,'freq',500e3); %
RadarParams.SpurParams.pulse = struct('amp',0.0,'freq',200e3,'duty',0.1,'phase',0);%
RadarParams.SpurParams.lo_leak = struct('amp',0.05); % DC/LO leakage
RadarParams.SpurParams.clip = struct('amp',0.03,'range',20); % Clipping spur
RadarParams.SpurParams.enabled = false; % spur on/off
% 7)
RadarParams.SP.RDM.window_type_range = 'hann'; % Range FFT용
RadarParams.SP.RDM.window_type_doppler = 'chebwin'; % Doppler FFT용
RadarParams.SP.CFAR.method = 'OS'; % 'CA' 'OS'
RadarParams.SP.CFAR.dimension = '2D'; % '1D' '2D'
RadarParams.SP.CFAR.axis = 'doppler'; % 1D일 : 'range' 'doppler'
RadarParams.SP.CFAR.pfa = 1e-6; % false alarm
RadarParams.SP.CFAR.train = [8, 8]; % [doppler, range] training cell (1D면 )
RadarParams.SP.CFAR.guard = [2, 2]; % [doppler, range] guard cell (1D면 )
RadarParams.SP.CFAR.rank = 0.75; % OS-CFAR rank (0~1)
RadarParams.SP.CFAR.os_scale = 15.0; % OS-CFAR
%% 2. (TX )
% step 0. TX
Target = RadarParams.Target;
TxPattern = RadarParams.Antenna.TxPattern;
RxPattern = RadarParams.Antenna.RxPattern;
NumTx = RadarParams.Antenna.NumTx;
NumChirps = RadarParams.Waveform.NumChirps;
T_chirp = RadarParams.Waveform.Timing.IdleTime + RadarParams.Waveform.Timing.RampEndTime;
T_frame = T_chirp * NumChirps;
[t, tx_mask] = generate_waveform_timing(RadarParams);
% ================= ADC Raw Data (step 1 ~ 6) =================
% step 1. (Ptx )
TxOut = radiate_antenna(Target, TxPattern);
% step 2. (TTD )
ChannelOut = apply_channel_effects(RadarParams);
% step 3.
RxOut = receive_antenna(ChannelOut, Target, RxPattern);
% step 4-1. (TX_Gain * Space_Loss * RX_Gain)
% RF (V_tx) "시스템 전달 함수" .
SystemAmp = zeros(size(RxOut.space_loss_amp));
for k = 1:Target.NumTargets
for tx = 1:NumTx
SystemAmp(k, :, tx, :, :) = RxOut.space_loss_amp(k, :, tx, :, :) * TxOut.G_tx_amp(k, tx);
end
end
% step 4-2. RF/IF ADC
% - : /, PA , , , MIMO , non-ideal, spur를
% RX .
% - 1) adc_raw_data : [NumRx x NumTx x NumChirps x N_adc_samples]
% TX별로 ( TX )
% - 2) adc_combined : [NumRx x NumChirps x N_adc_samples]
% ADC ( RX에서 TX )
[adc_raw_data, adc_combined] = apply_lna_and_mixer(RxOut, TxOut, RadarParams);
% step 5. ADC Combined ( ADC ) HPF
adc_combined_hpf = apply_analog_hpf(adc_combined, RadarParams.Waveform.fs_adc, RadarParams.Rxpath.fc_hpf);
% step 6. ADC (Quantization & Clipping)
%
v_peak = max(abs(adc_combined_hpf(:)));
lsb_val = RadarParams.Rxpath.adc_v_full_scale / (2^RadarParams.Rxpath.adc_bits);
% ADC
adc_digital = apply_adc_quantization(adc_combined_hpf, RadarParams.Rxpath.adc_bits, RadarParams.Rxpath.adc_v_full_scale, RadarParams.Rxpath.receiver_mode);
% ================= (step 7 ~ ) =================
% step 7. Range-FFT Ideal LPF
% LPF
% (RadarParams.Rxpath.fc_lpf, RadarParams.SP.RDM.*)
% adc_digital을 [NumRx, 1, NumChirps, N_samples] (process_range_fft_lpf )
adc_digital_expanded = reshape(adc_digital, [size(adc_digital,1), 1, size(adc_digital,2), size(adc_digital,3)]);
[range_data, r_axis] = process_range_fft_lpf(adc_digital_expanded, RadarParams);
% step 8. Range Profile (1 , 1 )
figure('Name', 'Range Profile with Ideal LPF');
plot(r_axis, 20*log10(abs(squeeze(range_data(1,1,1,:)))));
grid on; hold on;
xlabel('Range (m)');
ylabel('Magnitude (dB)');
title(['Range Profile (LPF Cut-off: ', num2str(RadarParams.Rxpath.fc_lpf/1e6), ' MHz)']);
% LPF
xline((RadarParams.Rxpath.fc_lpf * RadarParams.Basic.c)/(2*RadarParams.Waveform.Slope), '--r', 'LPF Cut-off');
% step 9. Doppler-FFT
[rd_cube, v_axis] = process_doppler_fft(range_data, RadarParams);
% step 10. NumRx (Noncoherent Integration) RDM
target_rd_map = integrate_nci_rdm(rd_cube, RadarParams); % [Doppler x Range]
% step 11. CFAR (OS/CA, 1D/2D, range/doppler )
[cfar_mask, cfar_threshold, cfar_detections] = detect_targets_cfar(target_rd_map, RadarParams);
%% 3. (Single Chirp & Multi-Chirp)
% [Figure 1]
T_chirp = RadarParams.Waveform.Timing.IdleTime + RadarParams.Waveform.Timing.RampEndTime;
idx_single = (t <= T_chirp);
t_single = t(idx_single);
tx_mask_single = tx_mask(idx_single);
fig_single = visualize_tx_waveform(t_single, RadarParams.Waveform.Timing, RadarParams.Waveform.fc, RadarParams.Waveform.f_start, RadarParams.Waveform.Slope, tx_mask_single, RadarParams.Waveform.nonideal.peak_phase_error, RadarParams.Waveform.nonideal.f_ripple);
% [Figure 2] 퀀 MIMO
% (: mimoMode와 NumTx )
fig_multi = visualize_multi_tx_waveform(t, RadarParams.Waveform.Timing, RadarParams.Waveform.fc, RadarParams.Waveform.f_start, RadarParams.Waveform.Slope, tx_mask, NumChirps, RadarParams.Waveform.mimoMode, NumTx);
% --- [ Figure 3, 4: ] ---
% 'TX' 'RX' .
fig_tx_ant = visualize_antenna_pattern(TxPattern, 'TX');
fig_rx_ant = visualize_antenna_pattern(RxPattern, 'RX');
% [Figure 5] Range-Doppler Map (NumRx NCI RDM)
fig_rd_map = visualize_rd_map_with_spurs(target_rd_map, r_axis, v_axis, Target, RadarParams.SpurParams, RadarParams, RadarParams.Waveform.Slope);
% [Figure 6] CFAR (2D MAP )
fig_cfar = figure('Name', 'CFAR Detections on NCI RDM');
imagesc(r_axis, v_axis, 20*log10(abs(target_rd_map)));
axis xy;
colormap(jet);
colorbar;
xlabel('Range (m)');
ylabel('Velocity (m/s)');
title('CFAR Detections (Circle Markers)');
hold on;
if ~isempty(cfar_detections)
det_d_idx = cfar_detections(:,1); % Doppler bin index
det_r_idx = cfar_detections(:,2); % Range bin index
det_r = r_axis(det_r_idx);
det_v = v_axis(det_d_idx);
plot(det_r, det_v, 'wo', 'MarkerSize', 7, 'LineWidth', 1.5);
end
hold off;