import matplotlib.pyplot as plt import numpy as np from typing import List from ..utils.dataclass import TTLBlock from ..hardware.constraints import HardwareConstraints def plot_ttl_timeline( ttl_blocks: List[TTLBlock], original_blocks: List[dict], tick_duration: float = 20e-9 ): """ Визуализирует TTL timeline + оригинальные сигналы: RF и ADC отображаются как синусоиды, GRAD как трапеция. Оригинальные сигналы сдвигаются вправо с учётом начального TTL-блока. :param ttl_blocks: список TTLBlock объектов (результат Synchronizer) :param original_blocks: список исходных блоков {"type": [...], "duration": float} :param tick_duration: длительность одного тика (сек) """ # === TTL timeline === time = 0 ttl_stream = {"RF": [], "SW": [], "ADC": [], "GR": []} time_stream = [] for block in ttl_blocks: for _ in range(block.duration): time_stream.append(time) ttl_stream["RF"].append(block.RF) ttl_stream["SW"].append(block.SW) ttl_stream["ADC"].append(block.ADC) ttl_stream["GR"].append(block.GR) time += tick_duration # === Исходные сигналы (RF, ADC — синус; GRAD — трапеция) === signal_plot = {"RF": [], "ADC": [], "GRAD": []} signal_time = [] t = 0 for block in original_blocks: duration = block["duration"] ticks = int(duration / tick_duration) t_block = np.linspace(t, t + duration, ticks) # RF синус if "RF" in block["type"]: signal_plot["RF"].extend(np.sin(2 * np.pi * 20e6 * t_block)) # 20 кГц else: signal_plot["RF"].extend([0] * ticks) # ADC синус, меньшей амплитудой if "ADC" in block["type"]: signal_plot["ADC"].extend(0.5 * np.sin(2 * np.pi * 10e6 * t_block)) else: signal_plot["ADC"].extend([0] * ticks) # GRAD — трапеция if "GRAD" in block["type"]: ramp_len = ticks // 4 plateau_len = ticks // 2 trapezoid = list(np.linspace(0, 1, ramp_len)) + \ [1] * plateau_len + \ list(np.linspace(1, 0, ticks - ramp_len - plateau_len)) signal_plot["GRAD"].extend(trapezoid) else: signal_plot["GRAD"].extend([0] * ticks) signal_time.extend(t_block) t += duration hw = HardwareConstraints() # === Сдвиг исходных сигналов на START_DELAY === start_offset = tick_duration * ( round(hw.START_DELAY / tick_duration) + max( round(hw.RF_DELAY / tick_duration), round(hw.TR_DELAY / tick_duration), round(hw.GRAD_DELAY / tick_duration) ) ) adjusted_signal_time = [t + start_offset for t in signal_time] # === Визуализация === plt.figure(figsize=(14, 8)) # TTL сигналы — логика for i, (label, values) in enumerate(ttl_stream.items()): plt.step( [t * 1e3 for t in time_stream], [v + i * 2 for v in values], where='post', label=f"TTL: {label}" ) # Исходные сигналы (синус/трапеция) offset_map = {"RF": 8, "ADC": 10, "GRAD": 12} color_map = {"RF": "blue", "ADC": "purple", "GRAD": "orange"} for label in signal_plot: y = np.array(signal_plot[label]) + offset_map[label] plt.plot( [t * 1e3 for t in adjusted_signal_time], y, label=f"Signal: {label}", color=color_map[label] ) # Финальные штрихи plt.title("TTL Timeline + Original Signal Shapes (с учётом задержек)") plt.xlabel("Time (mks)") yticks = [i * 2 for i in range(4)] + list(offset_map.values()) ylabels = list(ttl_stream.keys()) + list(offset_map.keys()) plt.yticks(yticks, ylabels) plt.grid(True) plt.legend(loc="upper right") plt.tight_layout() plt.show()