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=1→TX1, m=2→TX2, m=3→TX1... 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