From c4a98fc313006ce62b01cf419713d0ee11f61d98 Mon Sep 17 00:00:00 2001 From: yeokwanggoo-stack Date: Thu, 5 Mar 2026 20:01:31 +0900 Subject: [PATCH] =?UTF-8?q?1)=20Figure=20=EC=B0=BD=20on/off=20=ED=86=A0?= =?UTF-8?q?=EA=B8=80=20=EC=B6=94=EA=B0=80=202)=20Coverage=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=8F=84=EA=B5=AC=20=EC=B6=94=EA=B0=80=20-=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B2=80=EC=A6=9D=20=ED=95=84=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 05. Performance Analysis/analyze_coverage.m | 324 ++++++++++++++++++++ Main.m | 112 +++++-- 2 files changed, 402 insertions(+), 34 deletions(-) create mode 100644 05. Performance Analysis/analyze_coverage.m diff --git a/05. Performance Analysis/analyze_coverage.m b/05. Performance Analysis/analyze_coverage.m new file mode 100644 index 0000000..b111860 --- /dev/null +++ b/05. Performance Analysis/analyze_coverage.m @@ -0,0 +1,324 @@ +function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = analyze_coverage(RadarParams, target_range_m, target_rcs_dBsm) + % Coverage 분석: 2D 안테나 패턴 기반 각도별 SNR 계산 + % + % 방법: + % 1) Azimuth 0도, Elevation 0도에서 기준 SNR 계산 + % 2) 다른 각도의 상대 이득 차이로 SNR 계산 (효율적) + % 3) 입력한 RCS 값 반영 + % + % 입력: + % - RadarParams: 레이더 파라미터 구조체 + % - target_range_m: 표적 거리 (m), 기본값 100 + % - target_rcs_dBsm: 표적 RCS (dBsm), 기본값 0 + % + % 출력: + % - snr_coverage_2d: 2D SNR 맵 [elevation x azimuth] + % - azimuth_deg: 방위각 벡터 + % - elevation_deg: 고각 벡터 + % - coverage_info: 커버리지 정보 구조체 + % - fig: 생성된 figure 핸들 + + %% 입력 파라미터 처리 + if nargin < 2 + target_range_m = 100; % 기본: 100m + end + if nargin < 3 + target_rcs_dBsm = 0; % 기본: 0 dBsm = 1 + end + + %% 기본 파라미터 추출 + lambda = RadarParams.Waveform.lambda_c; + fc = RadarParams.Waveform.fc; + kb = RadarParams.Basic.kb; + T0 = RadarParams.Basic.T0; + + % 안테나 및 경로 파라미터 + TxPattern = RadarParams.Antenna.TxPattern; + RxPattern = RadarParams.Antenna.RxPattern; + NumTx = RadarParams.Antenna.NumTx; + NumRx = RadarParams.Antenna.NumRx; + + % 수신 경로 파라미터 + rxPathGain_dB = RadarParams.Rxpath.rxPathGain_dB; + system_NF_dB = RadarParams.Rxpath.system_NF_dB; + + % RCS (선형값) + rcs_target = 10^(target_rcs_dBsm / 10); + + %% 평균 2D 안테나 패턴 생성 (1D -> 2D 변환 포함) + % 공통 그리드 설정 (높은 해상도) + az_common = -90:1:90; + el_common = -90:1:90; + [AZ_common_grid, EL_common_grid] = meshgrid(az_common, el_common); + + % TX 패턴들을 2D로 변환 및 평균 + avg_tx_gain_2d = zeros(length(el_common), length(az_common)); + for tx = 1:NumTx + if isfield(TxPattern(tx), 'gain_az_dBi') % 1D 패턴 + az_angles = TxPattern(tx).az_angles; + el_angles = TxPattern(tx).el_angles; + gain_az = TxPattern(tx).gain_az_dBi; + gain_el = TxPattern(tx).gain_el_dBi; + + % 각 패턴의 최대값 저장 + max_gain_az = max(gain_az); + max_gain_el = max(gain_el); + max_gain_ref = max(max_gain_az, max_gain_el); + + % Normalize (최대값 = 0 dB) + gain_az_norm = gain_az - max_gain_az; + gain_el_norm = gain_el - max_gain_el; + + % 정규화된 패턴 보간 + gain_az_interp = interp1(az_angles, gain_az_norm, AZ_common_grid, 'linear', 'extrap'); + gain_el_interp = interp1(el_angles, gain_el_norm, EL_common_grid, 'linear', 'extrap'); + + % 2D로 결합하고 최대 이득 더하기 + tx_gain_2d = gain_az_interp + gain_el_interp + max_gain_ref; + else % 2D 패턴 + az_angles = TxPattern(tx).az_angles; + el_angles = TxPattern(tx).el_angles; + gain_dBi = TxPattern(tx).gain_dBi; + [AZ_grid, EL_grid] = ndgrid(az_angles, el_angles); + F = scatteredInterpolant(AZ_grid(:), EL_grid(:), gain_dBi(:), 'linear', 'nearest'); + tx_gain_2d = F(AZ_common_grid, EL_common_grid); + end + avg_tx_gain_2d = avg_tx_gain_2d + tx_gain_2d; + end + avg_tx_gain_2d = avg_tx_gain_2d / NumTx; + + % RX 패턴들을 2D로 변환 및 평균 (동일한 방식) + avg_rx_gain_2d = zeros(length(el_common), length(az_common)); + for rx = 1:NumRx + if isfield(RxPattern(rx), 'gain_az_dBi') % 1D 패턴 + az_angles = RxPattern(rx).az_angles; + el_angles = RxPattern(rx).el_angles; + gain_az = RxPattern(rx).gain_az_dBi; + gain_el = RxPattern(rx).gain_el_dBi; + + % 각 패턴의 최대값 저장 + max_gain_az = max(gain_az); + max_gain_el = max(gain_el); + max_gain_ref = max(max_gain_az, max_gain_el); + + % Normalize (최대값 = 0 dB) + gain_az_norm = gain_az - max_gain_az; + gain_el_norm = gain_el - max_gain_el; + + % 정규화된 패턴 보간 + gain_az_interp = interp1(az_angles, gain_az_norm, AZ_common_grid, 'linear', 'extrap'); + gain_el_interp = interp1(el_angles, gain_el_norm, EL_common_grid, 'linear', 'extrap'); + + % 2D로 결합하고 최대 이득 더하기 + rx_gain_2d = gain_az_interp + gain_el_interp + max_gain_ref; + else % 2D 패턴 + az_angles = RxPattern(rx).az_angles; + el_angles = RxPattern(rx).el_angles; + gain_dBi = RxPattern(rx).gain_dBi; + [AZ_grid, EL_grid] = ndgrid(az_angles, el_angles); + F = scatteredInterpolant(AZ_grid(:), EL_grid(:), gain_dBi(:), 'linear', 'nearest'); + rx_gain_2d = F(AZ_common_grid, EL_common_grid); + end + avg_rx_gain_2d = avg_rx_gain_2d + rx_gain_2d; + end + avg_rx_gain_2d = avg_rx_gain_2d / NumRx; + + %% TX 전력 및 공통 파라미터 계산 + % TX 전력 + pa_profile_freq = RadarParams.RFOutput.PA_Profile.freqs; + pa_profile_power_dbm = RadarParams.RFOutput.PA_Profile.power_dBm; + ptx_dbm = interp1(pa_profile_freq, pa_profile_power_dbm, fc, 'linear', 'extrap'); + ptx_w = 10^((ptx_dbm - 30) / 10); + + % 경로 손실 및 잡음 계산 (모든 각도에서 공통) + path_loss_factor = (4 * pi * target_range_m)^2; + rxGain_linear = 10^(rxPathGain_dB / 10); + noise_power_w = kb * T0 * RadarParams.Waveform.fs_adc; + system_NF_linear = 10^(system_NF_dB / 10); + total_noise_power_w = noise_power_w * system_NF_linear * rxGain_linear; % RX 이득 포함 + + %% 각도별 SNR 계산 (2D 안테나 패턴 직접 사용 - 벡터화 연산) + % X축: Azimuth, Y축: Elevation, Z축: SNR + azimuth_deg = -90:1:90; % X축: -90 ~ 90도, 1도 단위 + elevation_deg = -90:1:90; % Y축: -90 ~ 90도, 1도 단위 + + % az_common과 elevation_deg가 동일한 그리드이므로 인덱스 매칭 + % 안테나 이득 추출 (dB) + g_tx_db_grid = avg_tx_gain_2d; % [num_el x num_az] + g_rx_db_grid = avg_rx_gain_2d; % [num_el x num_az] + + % 이득을 선형으로 변환 + g_tx_linear_grid = 10.^(g_tx_db_grid / 10); + g_rx_linear_grid = 10.^(g_rx_db_grid / 10); + + % SNR 계산: 벡터화 연산 (스칼라 항 × 2D 배열) + p_rx_w = (ptx_w * g_tx_linear_grid .* g_rx_linear_grid * (lambda^2) * rcs_target) / ((path_loss_factor^2) * (4 * pi)); + p_rx_after_rxgain_w = p_rx_w * rxGain_linear; + snr_linear = p_rx_after_rxgain_w / total_noise_power_w; + snr_coverage_2d = 10 * log10(max(snr_linear, eps)); + + %% 1D CUT 추출 (elevation=0에서의 Azimuth cut, azimuth=0에서의 Elevation cut) + [~, el_idx_zero] = min(abs(elevation_deg - 0)); + snr_az_cut = snr_coverage_2d(el_idx_zero, :); % elevation=0 + + [~, az_idx_zero] = min(abs(azimuth_deg - 0)); + snr_el_cut = snr_coverage_2d(:, az_idx_zero); % azimuth=0 + + %% Cartesian 좌표계 변환 (Range, Azimuth, Elevation -> x, y, z) + [AZ_deg_grid, EL_deg_grid] = meshgrid(azimuth_deg, elevation_deg); + x_grid_m = target_range_m .* cosd(EL_deg_grid) .* cosd(AZ_deg_grid); + y_grid_m = target_range_m .* cosd(EL_deg_grid) .* sind(AZ_deg_grid); + z_grid_m = target_range_m .* sind(EL_deg_grid); + + %% Coverage 정보 통계 + snr_2d_vec = snr_coverage_2d(:); + + coverage_info.azimuth_deg = azimuth_deg; + coverage_info.elevation_deg = elevation_deg; + coverage_info.snr_coverage_2d = snr_coverage_2d; + coverage_info.snr_az_cut = snr_az_cut; + coverage_info.snr_el_cut = snr_el_cut; + coverage_info.target_range_m = target_range_m; + coverage_info.target_rcs_dBsm = target_rcs_dBsm; + coverage_info.x_grid_m = x_grid_m; + coverage_info.y_grid_m = y_grid_m; + coverage_info.z_grid_m = z_grid_m; + coverage_info.mean_snr = mean(snr_2d_vec); + coverage_info.max_snr = max(snr_2d_vec); + coverage_info.min_snr = min(snr_2d_vec); + [~, max_idx_2d] = max(snr_2d_vec); + [max_el_idx_2d, max_az_idx_2d] = ind2sub(size(snr_coverage_2d), max_idx_2d); + coverage_info.max_snr_azimuth = azimuth_deg(max_az_idx_2d); + coverage_info.max_snr_elevation = elevation_deg(max_el_idx_2d); + coverage_info.max_snr_x_m = x_grid_m(max_el_idx_2d, max_az_idx_2d); + coverage_info.max_snr_y_m = y_grid_m(max_el_idx_2d, max_az_idx_2d); + coverage_info.max_snr_z_m = z_grid_m(max_el_idx_2d, max_az_idx_2d); + + %% 3D + 1D Cut 시각화 + fig = figure('Name', 'Coverage Analysis - 3D + 1D Cut'); + set(fig, 'Position', [100, 100, 1600, 900]); + + % Subplot 1: x-SNR Cut (elevation=0) + subplot(2, 3, 1); + x_cut_m = target_range_m .* cosd(azimuth_deg); + plot(x_cut_m, snr_az_cut, 'b-', 'LineWidth', 2); + hold on; + [~, max_idx_az] = max(snr_az_cut); + plot(x_cut_m(max_idx_az), snr_az_cut(max_idx_az), 'r*', 'MarkerSize', 15, 'LineWidth', 2); + grid on; + xlabel('x (m)'); + ylabel('SNR (dB)'); + title('1D CUT: elevation = 0 deg'); + hold off; + + % Subplot 2: z-SNR Cut (azimuth=0) + subplot(2, 3, 2); + z_cut_m = target_range_m .* sind(elevation_deg); + plot(z_cut_m, snr_el_cut, 'g-', 'LineWidth', 2); + hold on; + [~, max_idx_el] = max(snr_el_cut); + plot(z_cut_m(max_idx_el), snr_el_cut(max_idx_el), 'r*', 'MarkerSize', 15, 'LineWidth', 2); + grid on; + xlabel('z (m)'); + ylabel('SNR (dB)'); + title('1D CUT: azimuth = 0 deg'); + hold off; + + % Subplot 3: x-y top view (color = SNR at elevation=0) + subplot(2, 3, 3); + x_top_m = target_range_m .* cosd(azimuth_deg); + y_top_m = target_range_m .* sind(azimuth_deg); + scatter(x_top_m, y_top_m, 45, snr_az_cut, 'filled'); + axis equal; + grid on; + cb = colorbar; + ylabel(cb, 'SNR (dB)'); + xlabel('x (m)'); + ylabel('y (m)'); + title('Top View (elevation = 0 deg)'); + + % Subplot 4-5: 3D Surface (geometry = x,y,z, color = SNR) + subplot(2, 3, 4:5, 'replace'); + surf(x_grid_m, y_grid_m, z_grid_m, snr_coverage_2d, 'EdgeColor', 'none'); + colormap(jet); + cb_3d = colorbar; + ylabel(cb_3d, 'SNR (dB)'); + xlabel('x (m)'); + ylabel('y (m)'); + zlabel('z (m)'); + title('3D Coverage in Cartesian Coordinates (Click to inspect)'); + view(45, 30); + grid on; + hold on; + plot3(coverage_info.max_snr_x_m, coverage_info.max_snr_y_m, coverage_info.max_snr_z_m, 'r*', 'MarkerSize', 20, 'LineWidth', 2); + hold off; + + % Datacursor mode 활성화 (클릭 기능) + dcm_obj = datacursormode(fig); + dcm_obj.Enable = 'on'; + set(dcm_obj, 'UpdateFcn', @datatip_update_callback); + set(dcm_obj, 'SnapToDataVertex', 'on'); + + % Help text + fprintf('\n>>> Data Cursor Mode ENABLED <<<\n'); + fprintf('Instructions:\n'); + fprintf(' 1. Left-click on the 3D surface to inspect points\n'); + fprintf(' 2. Tooltip shows x, y, z (m) and SNR[dB]\n'); + fprintf(' 3. Press Escape or click "Disable Data Cursor" in Figure Tools to deactivate\n\n'); + + %% Nested function for datacursor callback + function output_txt = datatip_update_callback(~, event) + pos = event.Position; + x_val = pos(1); + y_val = pos(2); + z_val = pos(3); + + dist2 = (x_grid_m - x_val).^2 + (y_grid_m - y_val).^2 + (z_grid_m - z_val).^2; + [~, nearest_idx] = min(dist2(:)); + [nearest_el_idx, nearest_az_idx] = ind2sub(size(dist2), nearest_idx); + snr_val = snr_coverage_2d(nearest_el_idx, nearest_az_idx); + + output_txt = { + ['x: ' num2str(x_val, '%.3f') ' m'] + ['y: ' num2str(y_val, '%.3f') ' m'] + ['z: ' num2str(z_val, '%.3f') ' m'] + ['SNR[dB]: ' num2str(snr_val, '%.3f')] + }; + end + + % Subplot 6: x-z side view (azimuth=0, color=SNR) + subplot(2, 3, 6); + x_side_m = target_range_m .* cosd(elevation_deg); + z_side_m = target_range_m .* sind(elevation_deg); + scatter(x_side_m, z_side_m, 45, snr_el_cut, 'filled'); + grid on; + cb_side = colorbar; + ylabel(cb_side, 'SNR (dB)'); + xlabel('x (m)'); + ylabel('z (m)'); + title('Side View (azimuth = 0 deg)'); + + % 전체 타이틀 + sgtitle(sprintf('3D Coverage (x,y,z) | Max SNR: %.2f dB at (%.2f, %.2f, %.2f) m', ... + coverage_info.max_snr, coverage_info.max_snr_x_m, coverage_info.max_snr_y_m, coverage_info.max_snr_z_m), 'FontSize', 12); + + %% 통계 출력 + fprintf('\n===== 3D COVERAGE ANALYSIS REPORT (Cartesian x,y,z) =====\n'); + fprintf('Target Configuration:\n'); + fprintf(' - Range: %.1f m\n', target_range_m); + fprintf(' - RCS: %.1f dBsm\n', target_rcs_dBsm); + fprintf(' - Analysis Angles: Azimuth ±90 deg, Elevation ±90 deg\n'); + fprintf('\nSNR Statistics (Full Grid):\n'); + fprintf(' - Mean SNR: %.2f dB\n', coverage_info.mean_snr); + fprintf(' - Max SNR: %.2f dB @ (x,y,z)=(%.2f, %.2f, %.2f) m\n', coverage_info.max_snr, coverage_info.max_snr_x_m, coverage_info.max_snr_y_m, coverage_info.max_snr_z_m); + fprintf(' - Min SNR: %.2f dB\n', coverage_info.min_snr); + fprintf(' - SNR Range: %.2f dB\n', coverage_info.max_snr - coverage_info.min_snr); + fprintf('\n1D CUT Statistics:\n'); + fprintf(' - elevation=0 cut: Max=%.2f dB, Min=%.2f dB\n', max(snr_az_cut), min(snr_az_cut)); + fprintf(' - azimuth=0 cut: Max=%.2f dB, Min=%.2f dB\n', max(snr_el_cut), min(snr_el_cut)); + fprintf('System Parameters:\n'); + fprintf(' - TX Power: %.2f dBm\n', ptx_dbm); + fprintf(' - RX Gain: %.1f dB\n', rxPathGain_dB); + fprintf(' - System NF: %.1f dB\n', system_NF_dB); + fprintf('=============================================\n\n'); +end diff --git a/Main.m b/Main.m index 4c21b9c..6b44b0a 100644 --- a/Main.m +++ b/Main.m @@ -10,6 +10,17 @@ currentFilePath = mfilename('fullpath'); currentFolder = fileparts(currentFilePath); addpath(genpath(currentFolder)); +%% =================== 시각화 토글 =================== +% 각 figure를 개별적으로 on/off 할 수 있습니다. +PlotToggle.range_profile = false; % Step 8 Range Profile +PlotToggle.tx_single = false; % Figure 1 Single chirp waveform +PlotToggle.tx_multi = false; % Figure 2 Multi chirp waveform +PlotToggle.tx_antenna = false; % Figure 3 TX antenna pattern +PlotToggle.rx_antenna = false; % Figure 4 RX antenna pattern +PlotToggle.rd_map = false; % Figure 5 Range-Doppler map +PlotToggle.cfar = false; % Figure 6 CFAR detections +PlotToggle.coverage = true; % Figure 7 Coverage analysis + %% =================== 파라미터 입력 =================== % 1) 기본 물리 파라미터 @@ -205,15 +216,17 @@ adc_digital_expanded = reshape(adc_digital, [size(adc_digital,1), 1, size(adc_di [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)']); +if PlotToggle.range_profile + 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'); + % LPF 컷오프 지점 표시 + xline((RadarParams.Rxpath.fc_lpf * RadarParams.Basic.c)/(2*RadarParams.Waveform.Slope), '--r', 'LPF Cut-off'); +end % step 9. Doppler-FFT 수행 [rd_cube, v_axis] = process_doppler_fft(range_data, RadarParams); @@ -232,40 +245,71 @@ 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); +fig_single = []; +if PlotToggle.tx_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); +end % [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); +fig_multi = []; +if PlotToggle.tx_multi + 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); +end % --- [ 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); +fig_tx_ant = []; +if PlotToggle.tx_antenna + fig_tx_ant = visualize_antenna_pattern(TxPattern, 'TX'); end -hold off; +fig_rx_ant = []; +if PlotToggle.rx_antenna + fig_rx_ant = visualize_antenna_pattern(RxPattern, 'RX'); +end + +% [Figure 5] Range-Doppler Map 시각화 (NumRx NCI RDM) +fig_rd_map = []; +if PlotToggle.rd_map + fig_rd_map = visualize_rd_map_with_spurs(target_rd_map, r_axis, v_axis, Target, RadarParams.SpurParams, RadarParams, RadarParams.Waveform.Slope); +end + +% [Figure 6] CFAR 탐지 결과 시각화 (2D MAP 위 원 마커) +fig_cfar = []; +if PlotToggle.cfar + 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; +end + +% ================= Coverage Analysis (Performance Analysis) ================= +% [Figure 7] Angular Coverage 분석: 2D 안테나 패턴 기반 효율적 SNR 계산 +% 거리 100m, RCS 0 dBsm 표적의 각도별 SNR 분석 +snr_coverage_2d = []; +azimuth_deg = []; +elevation_deg = []; +coverage_info = []; +fig_coverage = []; +if PlotToggle.coverage + [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig_coverage] = analyze_coverage(RadarParams, 10, 0); +end