import json class HardwareConstraints: """ Класс, хранящий аппаратные ограничения и настройки MRI-системы. Может инициализироваться из JSON-файла. """ def __init__(self, json_path: str = None): # Значения по умолчанию (совместимые с pypulseq) self.rf_raster_time = 1e-6 # сек, шаг временной дискретизации RF self.grad_raster_time = 10e-6 # сек, шаг дискретизации градиента self.adc_raster_time = 100e-9 # сек, шаг дискретизации АЦП self.block_duration_raster = 10e-6 # сек, шаг дискретизации длительности блока # Системные ограничения (по умолчанию отсутствуют задержки мертвого времени) self.rf_dead_time = 0.0 self.rf_ringdown_time = 0.0 self.adc_dead_time = 0.0 self.gamma = 42.576e6 # Гиромагнитное отношение (Гц/Т) для протона # Кастомные параметры системы self.TR_DELAY = 20e-9 # сек, задержка после съема (между TR) self.RF_DELAY = 500e-6 # сек, задержка перед RF-импульсом self.START_DELAY = 17e-6 # сек, начальная задержка перед последовательностью self.MIN_BLOCK_DURATION = 20e-9 # сек, минимальная длительность блока (квант времени последовательности) self.GRAD_DELAY = 1000e-9 # Флаги вставки задержек в синхро-последовательность. # Если флаг False — соответствующий блок задержки не вставляется # (и не вычитается из соседнего), что убирает артефакты 1/0 тактов. self.TR_DELAY_ENABLED = True self.RF_DELAY_ENABLED = True self.START_DELAY_ENABLED = True # Максимальные амплитуды self.RF_MAX = 1.0 # относительная макс. амплитуда RF (нормирована на 1.0) self.GRAD_MAX = 9e-3 * self.gamma # макс. градиент (Гц/м) по умолчанию 9 mT/m * gamma # Загрузка параметров из JSON при указании пути if json_path: self.load_from_json(json_path) def load_from_json(self, json_path: str): """ Загружает параметры аппаратных ограничений из JSON-файла. """ with open(json_path, 'r') as f: data = json.load(f) # Обновление обязательных параметров (если указаны в файле) self.rf_raster_time = data.get("rf_raster_time", self.rf_raster_time) self.grad_raster_time = data.get("grad_raster_time", self.grad_raster_time) self.adc_raster_time = data.get("adc_raster_time", self.adc_raster_time) self.block_duration_raster = data.get("block_duration_raster", self.block_duration_raster) self.rf_dead_time = data.get("rf_dead_time", self.rf_dead_time) self.rf_ringdown_time = data.get("rf_ringdown_time", self.rf_ringdown_time) self.adc_dead_time = data.get("adc_dead_time", self.adc_dead_time) self.gamma = data.get("gamma", self.gamma) # Обновление пользовательских параметров self.TR_DELAY = data.get("TR_DELAY", self.TR_DELAY) self.RF_DELAY = data.get("RF_DELAY", self.RF_DELAY) if self.rf_raster_time == 0.5e-6: self.START_DELAY = 885 * self.MIN_BLOCK_DURATION elif self.rf_raster_time == 0.05e-6: self.START_DELAY = 89 * self.MIN_BLOCK_DURATION else: self.START_DELAY = self.MIN_BLOCK_DURATION * 10 self.MIN_BLOCK_DURATION = data.get("MIN_BLOCK_DURATION", self.MIN_BLOCK_DURATION) # Флаги вставки задержек self.TR_DELAY_ENABLED = data.get("TR_DELAY_ENABLED", self.TR_DELAY_ENABLED) self.RF_DELAY_ENABLED = data.get("RF_DELAY_ENABLED", self.RF_DELAY_ENABLED) self.START_DELAY_ENABLED = data.get("START_DELAY_ENABLED", self.START_DELAY_ENABLED) # Обновление максимальных амплитуд (если указаны) self.RF_MAX = data.get("RF_MAX", self.RF_MAX) self.GRAD_MAX = data.get("GRAD_MAX", self.GRAD_MAX)