From 3c05d2be0905a797f3db3aa0ad34b11d6bbe6769 Mon Sep 17 00:00:00 2001 From: yeokwanggoo-stack Date: Fri, 6 Mar 2026 06:48:07 +0900 Subject: [PATCH] =?UTF-8?q?1.=20SNR=20=EC=8B=A0=ED=98=B8=EC=B2=98=EB=A6=AC?= =?UTF-8?q?,=20Secondary=20surface=20=EC=98=81=ED=96=A5=20=EA=B3=A0?= =?UTF-8?q?=EB=A0=A4=ED=95=98=EC=97=AC=20SNR=20=EC=9E=AC=EA=B3=84=EC=82=B0?= =?UTF-8?q?=202.=20Coverage=20Figure=20=EA=B0=9C=EC=84=A0=203.=20=EC=9C=88?= =?UTF-8?q?=EB=8F=84=EC=9A=B0=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EA=B3=84=EC=82=B0=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create_window_with_metrics.m | 118 ++++++++++ 04. Signal Processing/process_doppler_fft.m | 17 +- 04. Signal Processing/process_range_fft_lpf.m | 16 +- 05. Performance Analysis/analyze_coverage.m | 203 +++++++++++------- Main.m | 39 ++-- 5 files changed, 270 insertions(+), 123 deletions(-) create mode 100644 04. Signal Processing/create_window_with_metrics.m diff --git a/04. Signal Processing/create_window_with_metrics.m b/04. Signal Processing/create_window_with_metrics.m new file mode 100644 index 0000000..5577ad7 --- /dev/null +++ b/04. Signal Processing/create_window_with_metrics.m @@ -0,0 +1,118 @@ +function [windowVector, metrics] = create_window_with_metrics(windowType, windowLength, varargin) + % CREATE_WINDOW_WITH_METRICS - 윈도우 함수 생성 및 성능 지표 계산 + % + % 입력: + % windowType - 윈도우 타입 문자열: 'none', 'hann', 'hamming', 'blackman', 'chebwin' + % windowLength - 윈도우 길이 (샘플 수) + % varargin - 추가 파라미터 (예: chebwin의 경우 sidelobe level) + % + % 출력: + % windowVector - 생성된 윈도우 벡터 (1 x windowLength) + % metrics - 윈도우 성능 지표 구조체 + % .type : 윈도우 타입 + % .length : 윈도우 길이 + % .snr_loss_dB : SNR 손실 (dB) + % .scalloping_loss_dB: Scalloping 손실 (dB) + % .coherent_gain : 코히어런트 이득 + % .enbw : Equivalent Noise Bandwidth (bins) + + % 기본 파라미터 설정 + if windowLength <= 0 + error('Window length must be positive.'); + end + + % 윈도우 함수 생성 + switch lower(windowType) + case 'none' + windowVector = ones(1, windowLength); + + case 'hann' + windowVector = hann(windowLength)'; + + case 'hamming' + windowVector = hamming(windowLength)'; + + case 'blackman' + windowVector = blackman(windowLength)'; + + case 'chebwin' + % Chebyshev 윈도우는 sidelobe level 파라미터 필요 (기본값: 60dB) + if ~isempty(varargin) + sidelobe_dB = varargin{1}; + else + sidelobe_dB = 60; + end + windowVector = chebwin(windowLength, sidelobe_dB)'; + + otherwise + warning('Unknown window type "%s". Using Hann window as default.', windowType); + windowVector = hann(windowLength)'; + windowType = 'hann'; + end + + % 윈도우 성능 지표 계산 + metrics = calculate_window_metrics(windowVector, windowType); +end + + +function metrics = calculate_window_metrics(windowVector, windowType) + % CALCULATE_WINDOW_METRICS - 윈도우 함수의 성능 지표 계산 + % + % 계산 항목: + % 1. SNR Loss (dB) : 윈도우 적용으로 인한 SNR 손실 + % 2. Scalloping Loss (dB): FFT bin 사이(0.5 bin offset)에서의 최대 손실 + % 3. Coherent Gain : 윈도우의 평균 진폭 + % 4. ENBW (bins) : Equivalent Noise Bandwidth + + w = windowVector(:).'; % Row vector로 변환 + N = numel(w); + + if N == 0 + metrics = struct('type', windowType, 'length', 0, ... + 'snr_loss_dB', NaN, 'scalloping_loss_dB', NaN, ... + 'coherent_gain', NaN, 'enbw', NaN); + return; + end + + % 1. Coherent Gain (코히어런트 이득) + coherent_gain = mean(w); + + % 2. Noise Power Gain (잡음 전력 이득) + noise_power_gain = mean(abs(w).^2); + + % 3. SNR Loss (dB) + % SNR_loss = (Noise Power Gain) / (Coherent Gain)^2 + % 이는 윈도우 적용 시 신호 대 잡음비가 얼마나 감소하는지를 나타냄 + if abs(coherent_gain) > eps + snr_loss_linear = noise_power_gain / (abs(coherent_gain)^2); + snr_loss_dB = 10 * log10(snr_loss_linear); + else + snr_loss_dB = NaN; + end + + % 4. Equivalent Noise Bandwidth (ENBW) + % ENBW는 윈도우가 얼마나 많은 주파수 bin의 잡음을 통과시키는지 나타냄 + enbw = N * noise_power_gain / (sum(w)^2); + + % 5. Scalloping Loss (dB) + % FFT bin 중간(0.5 bin offset)에 신호가 위치할 때의 최대 손실 + % 이는 가장 나쁜 경우의 신호 손실을 나타냄 + sample_index = 0:(N-1); + half_bin_response = abs(sum(w .* exp(-1j * 2 * pi * 0.5 * sample_index / N))); + dc_response = abs(sum(w)); + + if dc_response > eps + scalloping_loss_dB = -20 * log10(half_bin_response / dc_response); + else + scalloping_loss_dB = NaN; + end + + % 결과 구조체 생성 + metrics = struct(... + 'type', lower(windowType), ... + 'length', N, ... + 'snr_loss_dB', snr_loss_dB, ... + 'scalloping_loss_dB', scalloping_loss_dB, ... + 'coherent_gain', coherent_gain, ... + 'enbw', enbw); +end diff --git a/04. Signal Processing/process_doppler_fft.m b/04. Signal Processing/process_doppler_fft.m index da810ca..8f51503 100644 --- a/04. Signal Processing/process_doppler_fft.m +++ b/04. Signal Processing/process_doppler_fft.m @@ -3,7 +3,6 @@ function [rd_map, doppler_axis] = process_doppler_fft(range_profile, RadarParams % - range_profile: [NumRx, NumTx, NumChirps, NumRangeBins] % - RadarParams: 메인 파라미터 구조체 NumChirps = RadarParams.Waveform.NumChirps; - window_type = RadarParams.SP.RDM.window_type_doppler; [~, ~, ~, ~] = size(range_profile); % 중심 주파수에서의 파장 @@ -12,18 +11,10 @@ function [rd_map, doppler_axis] = process_doppler_fft(range_profile, RadarParams % 1. 처프 간 반복 주기 (PRI, Pulse Repetition Interval) T_pri = RadarParams.Waveform.PRI; - % 2. Doppler-FFT용 윈도우 함수 (사용자 선택 가능) - % 도플러 방향(3번째 차원)으로 사이드로브를 억제합니다. - if strcmpi(window_type, 'none') - win_doppler = ones(1, NumChirps); - elseif strcmpi(window_type, 'hamming') - win_doppler = hamming(NumChirps)'; - elseif strcmpi(window_type, 'blackman') - win_doppler = blackman(NumChirps)'; - elseif strcmpi(window_type, 'hann') - win_doppler = hann(NumChirps)'; - else % default: 'chebwin' - win_doppler = chebwin(NumChirps, 60)'; % 60dB 사이드로브 억제 + % 2. Main.m에서 생성된 Doppler-FFT용 윈도우 함수 적용 + win_doppler = RadarParams.SP.RDM.window_doppler; + if length(win_doppler) ~= NumChirps + error('Doppler window length mismatch: expected %d, got %d', NumChirps, length(win_doppler)); end win_data = range_profile .* reshape(win_doppler, [1, 1, NumChirps, 1]); diff --git a/04. Signal Processing/process_range_fft_lpf.m b/04. Signal Processing/process_range_fft_lpf.m index 8176a86..8b05606 100644 --- a/04. Signal Processing/process_range_fft_lpf.m +++ b/04. Signal Processing/process_range_fft_lpf.m @@ -5,22 +5,14 @@ function [range_profile, range_axis] = process_range_fft_lpf(adc_raw_data, Radar fs_adc = RadarParams.Waveform.fs_adc; Slope = RadarParams.Waveform.Slope; fc_lpf_Hz = RadarParams.Rxpath.fc_lpf; - window_type = RadarParams.SP.RDM.window_type_range; [~, ~, ~, N_samples] = size(adc_raw_data); c = RadarParams.Basic.c; - % 1. 윈도우 함수 적용 - if strcmpi(window_type, 'none') - win = ones(1, N_samples); - elseif strcmpi(window_type, 'hamming') - win = hamming(N_samples)'; - elseif strcmpi(window_type, 'blackman') - win = blackman(N_samples)'; - elseif strcmpi(window_type, 'chebwin') - win = chebwin(N_samples, 60)'; - else % default: 'hann' - win = hann(N_samples)'; + % 1. Main.m에서 생성된 윈도우 함수 적용 + win = RadarParams.SP.RDM.window_range; + if length(win) ~= N_samples + error('Window length mismatch: expected %d, got %d', N_samples, length(win)); end win_data = adc_raw_data .* reshape(win, [1, 1, 1, N_samples]); diff --git a/05. Performance Analysis/analyze_coverage.m b/05. Performance Analysis/analyze_coverage.m index b111860..670df3e 100644 --- a/05. Performance Analysis/analyze_coverage.m +++ b/05. Performance Analysis/analyze_coverage.m @@ -150,9 +150,29 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana % 이득을 선형으로 변환 g_tx_linear_grid = 10.^(g_tx_db_grid / 10); g_rx_linear_grid = 10.^(g_rx_db_grid / 10); + + % 신호처리 이득 + SP_gain_rngFFT = RadarParams.Waveform.Timing.AdcSampTime * RadarParams.Waveform.fs_adc; + SP_gain_dopFFT = RadarParams.Waveform.NumChirps; + SP_loss_rngwin = 10^(RadarParams.SP.RDM.window_metrics_range.snr_loss_dB / 10); + SP_loss_dopwin = 10^(RadarParams.SP.RDM.window_metrics_doppler.snr_loss_dB / 10); + SP_loss_rng_straddle = 10^(RadarParams.SP.RDM.window_metrics_range.scalloping_loss_dB / 10); + SP_loss_dop_straddle = 10^(RadarParams.SP.RDM.window_metrics_doppler.scalloping_loss_dB / 10); + SP_total = SP_gain_rngFFT * SP_gain_dopFFT / (SP_loss_rngwin * SP_loss_dopwin * SP_loss_rng_straddle * SP_loss_dop_straddle); + + % Secondary Surface Loss + secondary_loss = 10^(RadarParams.Antenna.SecondarySurfaceLoss_dB / 10); + + fprintf('SP gain rng FFT: %.2f, SP gain doppler FFT: %.2f\n', SP_gain_rngFFT, SP_gain_dopFFT); + fprintf('SP loss range window: %.2f dB, SP loss doppler window: %.2f dB\n', RadarParams.SP.RDM.window_metrics_range.snr_loss_dB, RadarParams.SP.RDM.window_metrics_doppler.snr_loss_dB); + fprintf('SP loss range straddle: %.2f dB, SP loss doppler straddle: %.2f dB\n', RadarParams.SP.RDM.window_metrics_range.scalloping_loss_dB, RadarParams.SP.RDM.window_metrics_doppler.scalloping_loss_dB); + fprintf('SP loss doppler window: %.2f dB\n', RadarParams.SP.RDM.window_metrics_doppler.snr_loss_dB); + fprintf('SP loss doppler straddle: %.2f dB\n', RadarParams.SP.RDM.window_metrics_doppler.scalloping_loss_dB); + fprintf('Secondary surface loss: %.2f dB\n', RadarParams.Antenna.SecondarySurfaceLoss_dB); % 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_w = (ptx_w * (lambda^2) * rcs_target) / ((path_loss_factor^2) * (4 * pi)) * SP_total / secondary_loss; + p_rx_w = p_rx_w * g_tx_linear_grid .* g_rx_linear_grid; % 연산량 최적화를 위해 안테나 이득을 나중에 곱함 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)); @@ -194,38 +214,18 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana 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'); + %% 3D Coverage 시각화 + fig = figure('Name', 'Coverage Analysis - 3D'); set(fig, 'Position', [100, 100, 1600, 900]); + + % 1D cut SNR 추출 + [~, el_idx_zero] = min(abs(elevation_deg - 0)); + [~, az_idx_zero] = min(abs(azimuth_deg - 0)); + snr_az_cut = snr_coverage_2d(el_idx_zero, :); % elevation=0 cut + snr_el_cut = snr_coverage_2d(:, az_idx_zero); % azimuth=0 cut - % 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); + % Subplot 1: x-y top view (color = SNR at elevation=0 cut) + ax_topview = subplot(2, 2, 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'); @@ -235,10 +235,10 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana ylabel(cb, 'SNR (dB)'); xlabel('x (m)'); ylabel('y (m)'); - title('Top View (elevation = 0 deg)'); + title('Top View (Azimuth)'); - % Subplot 4-5: 3D Surface (geometry = x,y,z, color = SNR) - subplot(2, 3, 4:5, 'replace'); + % Subplot 2: 3D Surface (geometry = x,y,z, color = SNR) - spans full width at top + ax_3d = subplot(2, 2, 1:2); surf(x_grid_m, y_grid_m, z_grid_m, snr_coverage_2d, 'EdgeColor', 'none'); colormap(jet); cb_3d = colorbar; @@ -246,48 +246,15 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana xlabel('x (m)'); ylabel('y (m)'); zlabel('z (m)'); - title('3D Coverage in Cartesian Coordinates (Click to inspect)'); + title('3D Coverage in Cartesian Coordinates'); 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); + % Subplot 3: x-z side view (azimuth=0, color=SNR at azimuth=0 cut) + ax_sideview = subplot(2, 2, 4); 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'); @@ -296,12 +263,31 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana ylabel(cb_side, 'SNR (dB)'); xlabel('x (m)'); ylabel('z (m)'); - title('Side View (azimuth = 0 deg)'); + title('Side View (Elevation)'); % 전체 타이틀 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); + % 단일 Data Cursor Mode 설정 (모든 축에 적용) + dcm_obj = datacursormode(fig); + dcm_obj.Enable = 'on'; + set(dcm_obj, 'UpdateFcn', {@unified_datatip_callback, ... + ax_topview, ax_3d, ax_sideview, ... + x_top_m, y_top_m, snr_az_cut, azimuth_deg, ... + x_grid_m, y_grid_m, z_grid_m, snr_coverage_2d, elevation_deg, ... + x_side_m, z_side_m, snr_el_cut}); + dcm_obj.SnapToDataVertex = 'on'; + + % Help text + fprintf('\n>>> Data Cursor Mode ENABLED <<<\n'); + fprintf('Instructions:\n'); + fprintf(' 1. Left-click on any plot to inspect points\n'); + fprintf(' 2. Top View shows: x, y, Azimuth, SNR\n'); + fprintf(' 3. 3D Surface shows: x, y, z, Azimuth, Elevation, SNR\n'); + fprintf(' 4. Side View shows: x, z, Elevation, SNR\n'); + fprintf(' 5. Press Escape or click "Disable Data Cursor" in Figure Tools to deactivate\n\n'); + %% 통계 출력 fprintf('\n===== 3D COVERAGE ANALYSIS REPORT (Cartesian x,y,z) =====\n'); fprintf('Target Configuration:\n'); @@ -313,12 +299,81 @@ function [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig] = ana 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 + +function output_txt = unified_datatip_callback(~, event, ... + ax_topview, ax_3d, ax_sideview, ... + x_top_m, y_top_m, snr_az_cut, azimuth_deg, ... + x_grid_m, y_grid_m, z_grid_m, snr_coverage_2d, elevation_deg, ... + x_side_m, z_side_m, snr_el_cut) + % Unified datatip callback for all subplots + + try + pos = event.Position; + current_ax = event.Target.Parent; % Get current axis + + % Determine which subplot was clicked + if isequal(current_ax, ax_3d) + % 3D Plot + 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); + az_val = azimuth_deg(nearest_az_idx); + el_val = elevation_deg(nearest_el_idx); + + output_txt = { + ['x: ' num2str(x_val, '%.3f') ' m'] + ['y: ' num2str(y_val, '%.3f') ' m'] + ['z: ' num2str(z_val, '%.3f') ' m'] + ['Azimuth: ' num2str(az_val, '%.2f') ' deg'] + ['Elevation: ' num2str(el_val, '%.2f') ' deg'] + ['SNR[dB]: ' num2str(snr_val, '%.3f')] + }; + elseif isequal(current_ax, ax_topview) + % Top View - x, y, Azimuth, SNR + x_val = pos(1); + y_val = pos(2); + + [~, nearest_az_idx] = min(abs(x_top_m - x_val).^2 + abs(y_top_m - y_val).^2); + az_val = azimuth_deg(nearest_az_idx); + snr_val = snr_az_cut(nearest_az_idx); + + output_txt = { + ['x: ' num2str(x_val, '%.3f') ' m'] + ['y: ' num2str(y_val, '%.3f') ' m'] + ['Azimuth: ' num2str(az_val, '%.2f') ' deg'] + ['SNR[dB]: ' num2str(snr_val, '%.3f')] + }; + elseif isequal(current_ax, ax_sideview) + % Side View - x, z, Elevation, SNR + x_val = pos(1); + z_val = pos(2); + + [~, nearest_el_idx] = min(abs(x_side_m - x_val).^2 + abs(z_side_m - z_val).^2); + el_val = elevation_deg(nearest_el_idx); + snr_val = snr_el_cut(nearest_el_idx); + + output_txt = { + ['x: ' num2str(x_val, '%.3f') ' m'] + ['z: ' num2str(z_val, '%.3f') ' m'] + ['Elevation: ' num2str(el_val, '%.2f') ' deg'] + ['SNR[dB]: ' num2str(snr_val, '%.3f')] + }; + else + output_txt = 'Unknown plot'; + end + catch ME + output_txt = ['Error: ' ME.message]; + end end diff --git a/Main.m b/Main.m index eb3a5a7..89f6ed1 100644 --- a/Main.m +++ b/Main.m @@ -119,29 +119,13 @@ RadarParams.SP.RDM.window_type_doppler = 'chebwin'; % Doppler FFT용 윈도 num_samples_range = round(RadarParams.Waveform.fs_adc * RadarParams.Waveform.Timing.AdcSampTime); num_chirps_doppler = RadarParams.Waveform.NumChirps; -if strcmpi(RadarParams.SP.RDM.window_type_range, 'none') - RadarParams.SP.RDM.window_range = ones(1, num_samples_range); -elseif strcmpi(RadarParams.SP.RDM.window_type_range, 'hamming') - RadarParams.SP.RDM.window_range = hamming(num_samples_range)'; -elseif strcmpi(RadarParams.SP.RDM.window_type_range, 'blackman') - RadarParams.SP.RDM.window_range = blackman(num_samples_range)'; -elseif strcmpi(RadarParams.SP.RDM.window_type_range, 'chebwin') - RadarParams.SP.RDM.window_range = chebwin(num_samples_range, 60)'; -else - RadarParams.SP.RDM.window_range = hann(num_samples_range)'; -end +% Range 윈도우 생성 및 성능 지표 계산 +[RadarParams.SP.RDM.window_range, RadarParams.SP.RDM.window_metrics_range] = ... + create_window_with_metrics(RadarParams.SP.RDM.window_type_range, num_samples_range, 60); -if strcmpi(RadarParams.SP.RDM.window_type_doppler, 'none') - RadarParams.SP.RDM.window_doppler = ones(1, num_chirps_doppler); -elseif strcmpi(RadarParams.SP.RDM.window_type_doppler, 'hamming') - RadarParams.SP.RDM.window_doppler = hamming(num_chirps_doppler)'; -elseif strcmpi(RadarParams.SP.RDM.window_type_doppler, 'blackman') - RadarParams.SP.RDM.window_doppler = blackman(num_chirps_doppler)'; -elseif strcmpi(RadarParams.SP.RDM.window_type_doppler, 'hann') - RadarParams.SP.RDM.window_doppler = hann(num_chirps_doppler)'; -else - RadarParams.SP.RDM.window_doppler = chebwin(num_chirps_doppler, 60)'; -end +% Doppler 윈도우 생성 및 성능 지표 계산 +[RadarParams.SP.RDM.window_doppler, RadarParams.SP.RDM.window_metrics_doppler] = ... + create_window_with_metrics(RadarParams.SP.RDM.window_type_doppler, num_chirps_doppler, 60); RadarParams.SP.CFAR.method = 'OS'; % 'CA' 또는 'OS' RadarParams.SP.CFAR.dimension = '2D'; % '1D' 또는 '2D' @@ -151,7 +135,14 @@ RadarParams.SP.CFAR.train = [8, 8]; % [doppler, range] training cell 수 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 임계 스케일 - + +% 8) Secondary surface loss (안테나 반사 손실) +RadarParams.Antenna.SecondarySurfaceLoss_dB = 3; + +% 9) 커버리지 분석 파라미터 +RadarParams.Coverage.R_max = 10; % 최대 탐지 거리 (m) +RadarParams.Coverage.RCS_dBsm = 0; % 표적 RCS (dBsm) + %% 2. 모듈별 함수 호출 (TX 파이프라인) % step 0. 파형 시각화를 위한 시간 벡터 및 TX 마스크 생성 @@ -308,7 +299,7 @@ elevation_deg = []; coverage_info = []; fig_coverage = []; if PlotToggle.coverage - [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig_coverage] = analyze_coverage(RadarParams, 20, 0); + [snr_coverage_2d, azimuth_deg, elevation_deg, coverage_info, fig_coverage] = analyze_coverage(RadarParams, RadarParams.Coverage.R_max, RadarParams.Coverage.RCS_dBsm); end