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