Initial ARSS. Need to improve and add functions.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
% 입력 변수에서 chirpIdx 제거, t와 fs 추가
|
||||
function tx_matrix = apply_mimo_mode(x_amp, t, Timing, NumTx, mimoMode, fs)
|
||||
N_samples = length(x_amp);
|
||||
tx_matrix = zeros(NumTx, N_samples);
|
||||
|
||||
T_chirp = Timing.IdleTime + Timing.RampEndTime;
|
||||
SamplesPerChirp = round(T_chirp * fs);
|
||||
|
||||
% [핵심]: 각 샘플이 몇 번째 처프(1, 2, 3...)에 해당하는지 배열로 계산
|
||||
chirp_indices = floor((0:N_samples-1) / SamplesPerChirp) + 1;
|
||||
|
||||
switch upper(mimoMode)
|
||||
case 'TDM'
|
||||
% 처프 인덱스에 따라 1,2,3,1,2,3 번갈아가며 안테나 켬
|
||||
active_tx_seq = mod(chirp_indices - 1, NumTx) + 1;
|
||||
for tx = 1:NumTx
|
||||
% 활성화된 안테나 번호와 일치하는 구간만 신호 통과
|
||||
tx_matrix(tx, :) = x_amp .* (active_tx_seq == tx);
|
||||
end
|
||||
|
||||
case 'DDMA'
|
||||
% 모든 처프에서 켜지지만 처프 번호에 따라 위상이 누적해서 틀어짐
|
||||
for tx = 1:NumTx
|
||||
phase_shift_seq = 2 * pi * (tx - 1) * (chirp_indices - 1) / NumTx;
|
||||
tx_matrix(tx, :) = x_amp .* exp(1j * phase_shift_seq);
|
||||
end
|
||||
|
||||
otherwise
|
||||
error('지원하지 않는 MIMO 모드입니다. TDM 또는 DDMA를 선택하세요.');
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
function [t, tx_mask] = generate_waveform_timing(RadarParams)
|
||||
% RadarParams 기반 시간 벡터와 TX 활성화 마스크 생성
|
||||
% 시간 벡터와 TX 활성화 마스크를 생성합니다.
|
||||
%
|
||||
% 입력:
|
||||
% - RadarParams: 메인 파라미터 구조체
|
||||
%
|
||||
% 출력:
|
||||
% - t: 시간 벡터 (초)
|
||||
% - tx_mask: TX 활성화 마스크 (0 또는 1)
|
||||
|
||||
Timing = RadarParams.Waveform.Timing;
|
||||
NumChirps = RadarParams.Waveform.NumChirps;
|
||||
T_chirp = Timing.IdleTime + Timing.RampEndTime;
|
||||
T_frame = T_chirp * NumChirps; % 전체 프레임 시간
|
||||
fs = RadarParams.Waveform.fs_waveform;
|
||||
N_samples = round(T_frame * fs);
|
||||
% 시간 벡터 (초 단위) 생성
|
||||
t = linspace(0, T_frame, N_samples);
|
||||
% 각 처프 내부에서의 상대적 시간(t_mod) 계산 (초 기준)
|
||||
t_mod = mod(t, T_chirp);
|
||||
% TX 활성화 마스킹
|
||||
tx_mask = zeros(1, N_samples);
|
||||
tx_start_abs = Timing.IdleTime + Timing.TxStartTime;
|
||||
tx_mask(t_mod >= tx_start_abs) = 1;
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
function TxOut = radiate_antenna(Target, TxPatternArray)
|
||||
% 입력: 타겟 정보, TX 패턴 배열
|
||||
% 출력: TxOut.G_tx_amp (타겟별, TX별 송신 안테나 선형 진폭 이득 [NumTargets x NumTx])
|
||||
|
||||
NumTx = length(TxPatternArray);
|
||||
NumTargets = Target.NumTargets;
|
||||
TxOut.G_tx_amp = zeros(NumTargets, NumTx);
|
||||
|
||||
for k = 1:NumTargets
|
||||
for tx = 1:NumTx
|
||||
pat = TxPatternArray(tx);
|
||||
% 타겟 방향의 이득 계산
|
||||
if strcmpi(pat.Type, '2D')
|
||||
g_dBi = interp2(pat.az_angles, pat.el_angles, pat.gain_dBi, Target.az(k), Target.el(k), 'linear', -20);
|
||||
else
|
||||
g_az = interp1(pat.az_angles, pat.gain_az_dBi, Target.az(k), 'linear', -20);
|
||||
g_el = interp1(pat.el_angles, pat.gain_el_dBi, Target.el(k), 'linear', -20);
|
||||
g_dBi = g_az + g_el - pat.max_gain_dBi;
|
||||
end
|
||||
|
||||
% 전압 진폭 스케일의 선형 이득 저장 (sqrt(10^(G_dBi/10)))
|
||||
TxOut.G_tx_amp(k, tx) = sqrt(10^(g_dBi/10));
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,145 @@
|
||||
function fig = visualize_multi_tx_waveform(t, Timing, fc, f_start, Slope, tx_mask, TotalNumChirps, mimoMode, NumTx)
|
||||
fig = figure('Name', 'Multi-Chirp & MIMO Modulation', 'Position', [150, 150, 1100, 750]);
|
||||
|
||||
T_chirp = Timing.IdleTime + Timing.RampEndTime;
|
||||
NumChirpsToPlot = min(TotalNumChirps, 4);
|
||||
|
||||
plot_idx = (t <= NumChirpsToPlot * T_chirp);
|
||||
t_plot = t(plot_idx);
|
||||
tx_mask_plot = tx_mask(plot_idx);
|
||||
|
||||
t_mod = mod(t_plot, T_chirp);
|
||||
t_ramp = t_mod - Timing.IdleTime;
|
||||
|
||||
inst_freq_theoretical = f_start + Slope * t_ramp;
|
||||
inst_freq_masked = inst_freq_theoretical;
|
||||
inst_freq_masked(tx_mask_plot == 0) = NaN;
|
||||
|
||||
% =========================================================
|
||||
% [Subplot 1] 상단: 시간에 따른 주파수 파형
|
||||
% =========================================================
|
||||
subplot(2, 1, 1);
|
||||
|
||||
y_min = (f_start / 1e9) - 0.25;
|
||||
y_max = (f_start / 1e9) + (Slope * Timing.RampEndTime / 1e9) + 0.1;
|
||||
|
||||
plot(t_plot * 1e6, inst_freq_masked / 1e9, 'b', 'LineWidth', 2);
|
||||
grid on; hold on;
|
||||
|
||||
title(sprintf('Continuous Multi-Chirp Sequence (Showing %d of %d Chirps)', NumChirpsToPlot, TotalNumChirps), 'FontSize', 12);
|
||||
ylabel('Absolute Frequency (GHz)', 'FontSize', 11);
|
||||
|
||||
ylim([y_min, y_max]);
|
||||
if ~isempty(t_plot)
|
||||
xlim([0, max(t_plot)*1e6]);
|
||||
else
|
||||
xlim([0, 1]); % fallback range when no data
|
||||
end
|
||||
|
||||
f_valid_start = f_start + (Slope * Timing.AdcStartTime);
|
||||
f_valid_end = f_start + (Slope * (Timing.AdcStartTime + Timing.AdcSampTime));
|
||||
valid_bandwidth = f_valid_end - f_valid_start;
|
||||
|
||||
info_str = {
|
||||
sprintf('▶ Valid Start Freq : %.4f GHz', f_valid_start / 1e9), ...
|
||||
sprintf('▶ Center Freq : %.4f GHz', fc / 1e9), ...
|
||||
sprintf('▶ Valid End Freq : %.4f GHz', f_valid_end / 1e9), ...
|
||||
sprintf('▶ Transmit Bandwidth : %.4f GHz', valid_bandwidth / 1e9), ...
|
||||
sprintf('▶ Total Gen Chirps : %d', TotalNumChirps)
|
||||
};
|
||||
text(0.02, 0.96, info_str, 'Units', 'normalized', 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [1 1 1 0.85], 'EdgeColor', 'k', 'VerticalAlignment', 'top', 'Margin', 5);
|
||||
|
||||
guide_line_args = {'Color', [0 0 0.5], 'LineStyle', '--', 'LineWidth', 1};
|
||||
for i = 0:NumChirpsToPlot
|
||||
plot([i * T_chirp * 1e6, i * T_chirp * 1e6], [y_min, y_max], guide_line_args{:});
|
||||
end
|
||||
|
||||
draw_dim_arrow = @(x1, x2, y, label_str) ...
|
||||
[plot([x1, x2], [y, y], 'k-', 'LineWidth', 1.2), ...
|
||||
fill([x1, x1 + min(0.6, (x2-x1)*0.35), x1 + min(0.6, (x2-x1)*0.35)], [y, y + 0.015, y - 0.015], 'k', 'EdgeColor', 'none'), ...
|
||||
fill([x2, x2 - min(0.6, (x2-x1)*0.35), x2 - min(0.6, (x2-x1)*0.35)], [y, y + 0.015, y - 0.015], 'k', 'EdgeColor', 'none'), ...
|
||||
text((x1+x2)/2, y + 0.03, label_str, 'HorizontalAlignment', 'center', 'VerticalAlignment', 'bottom', 'FontSize', 9, 'FontWeight', 'bold', 'BackgroundColor', 'w', 'EdgeColor', 'k')];
|
||||
|
||||
y_pri = y_min + 0.08;
|
||||
pri_us = T_chirp * 1e6;
|
||||
prf_khz = (1 / T_chirp) / 1e3;
|
||||
pri_prf_label = sprintf('PRI: %.1f \\mus\nPRF: %.1f kHz', pri_us, prf_khz);
|
||||
draw_dim_arrow(0, pri_us, y_pri, pri_prf_label);
|
||||
hold off;
|
||||
|
||||
% =========================================================
|
||||
% [Subplot 2] 하단: MIMO 방식 통합형 Active TX & Phase 맵
|
||||
% =========================================================
|
||||
subplot(2, 1, 2);
|
||||
hold on; grid on;
|
||||
|
||||
% --- [핵심 추가]: TX 개수에 맞춘 고유 색상 팔레트 생성 ---
|
||||
% lines() 함수는 구분이 명확하고 세련된 색상 배열을 반환합니다.
|
||||
tx_colors = lines(NumTx);
|
||||
|
||||
for tx = 1:NumTx
|
||||
plot([0, NumChirpsToPlot * T_chirp * 1e6], [tx, tx], ':', 'Color', [0.8 0.8 0.8], 'HandleVisibility', 'off');
|
||||
end
|
||||
|
||||
for i = 0 : NumChirpsToPlot - 1
|
||||
t_center_us = (i + 0.5) * T_chirp * 1e6;
|
||||
|
||||
if strcmpi(mimoMode, 'TDM')
|
||||
active_tx_list = mod(i, NumTx) + 1;
|
||||
elseif strcmpi(mimoMode, 'DDMA')
|
||||
active_tx_list = 1:NumTx;
|
||||
else
|
||||
active_tx_list = 1;
|
||||
end
|
||||
|
||||
for tx = 1:NumTx
|
||||
% 현재 처리 중인 TX 안테나의 고유 색상 추출
|
||||
current_color = tx_colors(tx, :);
|
||||
|
||||
if ismember(tx, active_tx_list)
|
||||
if strcmpi(mimoMode, 'DDMA')
|
||||
phase_rad = 2 * pi * (tx - 1) * i / NumTx;
|
||||
phase_deg = mod(rad2deg(phase_rad), 360);
|
||||
else
|
||||
phase_deg = 0;
|
||||
end
|
||||
|
||||
% 활성화: 해당 안테나의 색상으로 꽉 채워진 원
|
||||
plot(t_center_us, tx, 'o', 'MarkerSize', 12, 'MarkerFaceColor', current_color, 'MarkerEdgeColor', current_color, 'HandleVisibility', 'off');
|
||||
|
||||
text_offset_us = T_chirp * 1e6 * 0.08;
|
||||
text(t_center_us + text_offset_us, tx, sprintf('%d^\\circ', round(phase_deg)), ...
|
||||
'VerticalAlignment', 'middle', 'HorizontalAlignment', 'left', ...
|
||||
'FontSize', 9, 'FontWeight', 'bold', 'Color', current_color);
|
||||
else
|
||||
% 비활성화: 해당 안테나의 색상 테두리를 가진 비워진 원
|
||||
plot(t_center_us, tx, 'o', 'MarkerSize', 12, 'MarkerFaceColor', 'w', 'MarkerEdgeColor', current_color, 'HandleVisibility', 'off');
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
title(sprintf('%s Active TX Antenna & Phase Map', upper(mimoMode)), 'FontSize', 12);
|
||||
ylabel('TX Antenna', 'FontSize', 11);
|
||||
|
||||
yticks(1:NumTx);
|
||||
% Y축 라벨에도 텍스트 색상을 입혀 직관성 극대화 (선택 사항)
|
||||
% yticklabels(arrayfun(@(x) sprintf('\\color[rgb]{%f,%f,%f}TX %d', tx_colors(x,1), tx_colors(x,2), tx_colors(x,3), x), 1:NumTx, 'UniformOutput', false));
|
||||
yticklabels(arrayfun(@(x) sprintf('TX %d', x), 1:NumTx, 'UniformOutput', false));
|
||||
ylim([0.5, NumTx + 0.5]);
|
||||
|
||||
% --- [범례 수정]: 중립적인 색상(다크 그레이)으로 상태의 개념만 표시 ---
|
||||
neutral_color = [0.3 0.3 0.3];
|
||||
h_on = plot(NaN, NaN, 'o', 'MarkerSize', 10, 'MarkerFaceColor', neutral_color, 'MarkerEdgeColor', neutral_color, 'DisplayName', 'Active');
|
||||
h_off = plot(NaN, NaN, 'o', 'MarkerSize', 10, 'MarkerFaceColor', 'w', 'MarkerEdgeColor', neutral_color, 'DisplayName', 'Inactive');
|
||||
legend([h_on, h_off], 'Location', 'best', 'FontSize', 10, 'Box', 'on');
|
||||
|
||||
xlabel('Time (\mus)', 'FontSize', 11);
|
||||
xlim([0, max(t_plot)*1e6]);
|
||||
|
||||
y_lims_bottom = ylim;
|
||||
for i = 0:NumChirpsToPlot
|
||||
plot([i * T_chirp * 1e6, i * T_chirp * 1e6], y_lims_bottom, guide_line_args{:}, 'HandleVisibility', 'off');
|
||||
end
|
||||
|
||||
hold off;
|
||||
end
|
||||
@@ -0,0 +1,78 @@
|
||||
function fig = visualize_tx_waveform(t, Timing, fc, f_start, Slope, tx_mask, peak_phase_error, f_ripple)
|
||||
fig = figure('Name', 'FMCW Radar Timing & True RF Frequency', 'Position', [100, 100, 1100, 700]);
|
||||
|
||||
t_ramp = t - Timing.IdleTime;
|
||||
|
||||
% 주파수 스윕 계산
|
||||
inst_freq_theoretical = f_start + Slope * t_ramp;
|
||||
freq_nonlin = peak_phase_error * f_ripple * cos(2 * pi * f_ripple * t_ramp);
|
||||
inst_freq_theoretical = inst_freq_theoretical + freq_nonlin;
|
||||
|
||||
inst_freq_masked = inst_freq_theoretical;
|
||||
inst_freq_masked(tx_mask == 0) = NaN;
|
||||
|
||||
% Y축 동적 스케일링 설정
|
||||
y_min = (f_start / 1e9) - 0.2;
|
||||
y_max = (f_start / 1e9) + (Slope * Timing.RampEndTime / 1e9) + 0.1;
|
||||
|
||||
% --- [주파수 곡선 플롯] ---
|
||||
plot(t * 1e6, inst_freq_masked / 1e9, 'b', 'LineWidth', 2.5);
|
||||
title(sprintf('Mathematical Instantaneous RF Frequency & HW Timing (Center fc = %.2f GHz)', fc/1e9), 'FontSize', 12);
|
||||
xlabel('Time (\mus)', 'FontSize', 11); ylabel('Absolute Frequency (GHz)', 'FontSize', 11);
|
||||
|
||||
ylim([y_min, y_max]);
|
||||
grid on; hold on;
|
||||
|
||||
% --- [좌측 상단: 유효 주파수 및 대역폭 정보 박스 추가] ---
|
||||
% ADC 샘플링 구간에 해당하는 유효 시작/끝 주파수 및 대역폭 계산
|
||||
f_valid_start = f_start + (Slope * Timing.AdcStartTime);
|
||||
f_valid_end = f_start + (Slope * (Timing.AdcStartTime + Timing.AdcSampTime));
|
||||
valid_bandwidth = f_valid_end - f_valid_start; % 유효 대역폭 추가 계산
|
||||
|
||||
info_str = {
|
||||
sprintf('▶ Valid Start Freq : %.4f GHz', f_valid_start / 1e9), ...
|
||||
sprintf('▶ Center Freq : %.4f GHz', fc / 1e9), ...
|
||||
sprintf('▶ Valid End Freq : %.4f GHz', f_valid_end / 1e9), ...
|
||||
sprintf('▶ Valid Bandwidth : %.4f GHz', valid_bandwidth / 1e9) % 정보 박스에 표시
|
||||
};
|
||||
|
||||
text(0.02, 0.96, info_str, 'Units', 'normalized', ...
|
||||
'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [1 1 1 0.85], ...
|
||||
'EdgeColor', 'k', 'VerticalAlignment', 'top', 'Margin', 5);
|
||||
|
||||
|
||||
% --- [가이드 세로선 그리기] ---
|
||||
y_lims = ylim;
|
||||
guide_line_args = {'Color', [0 0 0.5], 'LineStyle', '--', 'LineWidth', 1};
|
||||
|
||||
x_coords = [0, Timing.IdleTime, ...
|
||||
(Timing.IdleTime + Timing.TxStartTime), ...
|
||||
(Timing.IdleTime + Timing.AdcStartTime), ...
|
||||
(Timing.IdleTime + Timing.AdcStartTime + Timing.AdcSampTime), ...
|
||||
(Timing.IdleTime + Timing.RampEndTime)];
|
||||
x_coords_us = x_coords * 1e6;
|
||||
|
||||
for i = 1:length(x_coords_us)
|
||||
plot([x_coords_us(i), x_coords_us(i)], y_lims, guide_line_args{:});
|
||||
end
|
||||
|
||||
% --- [커스텀 다각형 화살표 함수] ---
|
||||
draw_dim_arrow = @(x1, x2, y, name, val_us) ...
|
||||
[plot([x1, x2], [y, y], 'k-', 'LineWidth', 1.2), ...
|
||||
fill([x1, x1 + min(0.6, (x2-x1)*0.35), x1 + min(0.6, (x2-x1)*0.35)], [y, y + 0.015, y - 0.015], 'k', 'EdgeColor', 'none'), ...
|
||||
fill([x2, x2 - min(0.6, (x2-x1)*0.35), x2 - min(0.6, (x2-x1)*0.35)], [y, y + 0.015, y - 0.015], 'k', 'EdgeColor', 'none'), ...
|
||||
text((x1+x2)/2, y + 0.03, sprintf('%s\n(%.1f \\mus)', name, val_us), 'HorizontalAlignment', 'center', 'VerticalAlignment', 'bottom', 'FontSize', 9, 'FontWeight', 'bold', 'BackgroundColor', 'w', 'EdgeColor', 'k')];
|
||||
|
||||
% 화살표 배치 높이 동적 설정
|
||||
y1 = y_min + 0.05;
|
||||
y2 = y_min + 0.18;
|
||||
y3 = y_min + 0.31;
|
||||
|
||||
draw_dim_arrow(x_coords_us(1), x_coords_us(2), y1, 'Idle Time', Timing.IdleTime * 1e6);
|
||||
draw_dim_arrow(x_coords_us(2), x_coords_us(3), y2, 'TX Start', Timing.TxStartTime * 1e6);
|
||||
draw_dim_arrow(x_coords_us(2), x_coords_us(4), y3, 'ADC Delay', Timing.AdcStartTime * 1e6);
|
||||
draw_dim_arrow(x_coords_us(4), x_coords_us(5), y2, 'ADC Sampling (Valid)', Timing.AdcSampTime * 1e6);
|
||||
draw_dim_arrow(x_coords_us(5), x_coords_us(6), y1, 'Excess', Timing.ExcessTime * 1e6);
|
||||
|
||||
hold off;
|
||||
end
|
||||
Reference in New Issue
Block a user